Add in Discord OAuth and show player cards to authenticated users
This commit is contained in:
parent
4fb2d7b4c8
commit
cddc982e8b
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<nav class="navbar navbar-expand-sm" style="margin-bottom: 1rem" id="navbar">
|
||||
<RouterLink class="navbar-brand nav-link" to="/">SBa Season {{ seasonNumber }}</RouterLink>
|
||||
<RouterLink class="navbar-brand nav-link" to="/">SBa Season {{ seasonNumber() }}</RouterLink>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#top-navbar-collapse"
|
||||
aria-controls="top-navbar-collapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@ -13,7 +13,7 @@
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<RouterLink class="nav-link"
|
||||
:to="{ name: 'team', params: { seasonNumber: seasonNumber, teamAbbreviation: 'FA' } }">Free Agents
|
||||
:to="{ name: 'team', params: { seasonNumber: seasonNumber(), teamAbbreviation: 'FA' } }">Free Agents
|
||||
</RouterLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
@ -23,11 +23,16 @@
|
||||
<a class="nav-link" target="_blank" href="https://sbanews.manticorum.com/">News</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a id="login" class="nav-link">Login with Discord</a>
|
||||
<button v-if="!isAuthenticated" id="login" class="nav-link" @click="authenticate">Login with Discord</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<!-- TODO RouterLink to team page with team icon -->
|
||||
<a id="team-login-link" href="/teams?abbrev="></a>
|
||||
<li v-if="false" class="nav-item">
|
||||
<!-- make the above true if you want to clear cookies for testing -->
|
||||
<button class="nav-link" @click="clearCookie">Clear Cookie</button>
|
||||
</li>
|
||||
<li v-if="isAuthenticated && userTeam" class="nav-item">
|
||||
<RouterLink :to="{ name: 'team', params: { seasonNumber: seasonNumber(), teamAbbreviation: userTeam.abbrev } }">
|
||||
<img id="thumbnail" style="max-height: 35px; float:right; vertical-align:middle" :src=userTeam?.thumbnail>
|
||||
</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -68,17 +73,23 @@ import { RouterLink } from 'vue-router'
|
||||
import type { MenuOption } from 'naive-ui'
|
||||
import { fetchPlayers, type Player } from '@/services/playersService'
|
||||
import { CURRENT_SEASON } from '@/services/utilities'
|
||||
import type { Team } from '@/services/apiResponseTypes'
|
||||
import { fetchActiveTeamByOwnerId } from '@/services/teamsService'
|
||||
import { authenticate, clearCookie, completeAuthentication, getOwnerId, isDiscordAuthenticated, parseCookie } from '@/services/authenticationService'
|
||||
|
||||
export default {
|
||||
name: 'NavBar',
|
||||
data() {
|
||||
return {
|
||||
userTeam: undefined as Team | undefined,
|
||||
players: [] as Player[],
|
||||
searchPlayerName: undefined
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.populatePlayerNames()
|
||||
this.completeAuthentication()
|
||||
this.fetchUserTeam()
|
||||
},
|
||||
computed: {
|
||||
menuOptions(): MenuOption[] {
|
||||
@ -267,23 +278,43 @@ export default {
|
||||
{ label: 'News', key: 'News' }
|
||||
]
|
||||
},
|
||||
seasonNumber(): number {
|
||||
// TODO pull this from DB?
|
||||
return CURRENT_SEASON
|
||||
},
|
||||
sortedPlayerNames(): string[] {
|
||||
return this.players.sort((p1, p2) => p2.wara - p1.wara).map(p => p.name)
|
||||
},
|
||||
isAuthenticated(): boolean {
|
||||
return isDiscordAuthenticated()
|
||||
},
|
||||
ownerId(): string | undefined {
|
||||
return getOwnerId()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getPlayers(): Promise<Player[]> {
|
||||
return await fetchPlayers(this.seasonNumber)
|
||||
return await fetchPlayers(this.seasonNumber())
|
||||
},
|
||||
async populatePlayerNames(): Promise<void> {
|
||||
this.players = await this.getPlayers()
|
||||
},
|
||||
searchPlayers(): void {
|
||||
this.$router.push({ path: `/players/${this.seasonNumber}/${this.searchPlayerName}` })
|
||||
this.$router.push({ path: `/players/${this.seasonNumber()}/${this.searchPlayerName}` })
|
||||
},
|
||||
authenticate(): void {
|
||||
authenticate()
|
||||
},
|
||||
async completeAuthentication(): Promise<void> {
|
||||
if (this.ownerId) return
|
||||
|
||||
completeAuthentication()
|
||||
},
|
||||
async fetchUserTeam(): Promise<void> {
|
||||
if (!this.ownerId) return
|
||||
this.userTeam = await fetchActiveTeamByOwnerId(this.ownerId)
|
||||
},
|
||||
seasonNumber(): number {
|
||||
return CURRENT_SEASON
|
||||
},
|
||||
clearCookie(): void {
|
||||
clearCookie()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
67
src/services/authenticationService.ts
Normal file
67
src/services/authenticationService.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { SITE_URL } from './utilities'
|
||||
|
||||
export function authenticate(): void {
|
||||
// MAJOR DOMO - 712002920950005870
|
||||
const clientID = '712002920950005870'
|
||||
// TODO: dynamically redirect to current view by getting current query params and adding them back on after?
|
||||
const redirectURI = encodeURIComponent(SITE_URL)
|
||||
const scope = 'identify' // Adjust the scope as needed
|
||||
window.location.href = `https://discord.com/oauth2/authorize?client_id=${clientID}&redirect_uri=${redirectURI}&response_type=token&scope=${scope}`
|
||||
}
|
||||
|
||||
export function isDiscordAuthenticated(): boolean {
|
||||
return !!getOwnerId()
|
||||
}
|
||||
|
||||
export function getOwnerId(): string | undefined {
|
||||
return parseCookie(document.cookie)?.discord
|
||||
}
|
||||
|
||||
export async function completeAuthentication(): Promise<void> {
|
||||
if (!window.location.hash) {
|
||||
console.warn('No token hash found in return URL')
|
||||
return
|
||||
}
|
||||
|
||||
// Extract the access token from the URL params
|
||||
const urlParams = new URLSearchParams(window.location.hash.slice(1))
|
||||
const accessToken = urlParams.get('access_token')
|
||||
const tokenType = urlParams.get('token_type') //should be 'Bearer'
|
||||
const userResponse = await fetch('https://discord.com/api/users/@me', {
|
||||
headers: {
|
||||
authorization: `${tokenType} ${accessToken}`
|
||||
}
|
||||
})
|
||||
if (!userResponse.ok) {
|
||||
console.warn('userResponse was not OK', userResponse)
|
||||
return
|
||||
}
|
||||
// right now we only care about the id property but we could make a full discord account object if desired
|
||||
const user: { id: string } = await userResponse.json()
|
||||
if (!user?.id) {
|
||||
console.warn('No user or id found while authenticating')
|
||||
return
|
||||
}
|
||||
|
||||
window.location.href = SITE_URL
|
||||
saveOwnerIdCookie(user.id)
|
||||
}
|
||||
|
||||
// Based on https://www.30secondsofcode.org/js/s/parse-cookie/
|
||||
export function parseCookie(cookie: string | undefined): { [key: string]: string } {
|
||||
if (!cookie) return {}
|
||||
return cookie.split(';')
|
||||
.map(v => v.split('='))
|
||||
.reduce((acc: { [key: string]: string }, v) => {
|
||||
acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim())
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function saveOwnerIdCookie(ownerId: string): void {
|
||||
document.cookie = `discord=${ownerId}; expires=Fri, 1 Jan 2100 12:00:00 UTC; path=/`
|
||||
}
|
||||
|
||||
export function clearCookie(): void {
|
||||
document.cookie = "discord=;-1;path=/"
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Team } from './apiResponseTypes'
|
||||
import { SITE_URL } from './utilities'
|
||||
import { CURRENT_SEASON, SITE_URL } from './utilities'
|
||||
|
||||
export async function fetchTeam(seasonNumber: number, teamAbbreviation: string): Promise<Team> {
|
||||
const response = await fetch(`${SITE_URL}/api/v3/teams?season=${seasonNumber}&team_abbrev=${teamAbbreviation}`)
|
||||
@ -15,3 +15,19 @@ export async function fetchTeam(seasonNumber: number, teamAbbreviation: string):
|
||||
|
||||
return teamResponse.teams[0]
|
||||
}
|
||||
|
||||
export async function fetchActiveTeamByOwnerId(ownerId: string): Promise<Team | undefined> {
|
||||
const response = await fetch(`${SITE_URL}/api/v3/teams?season=${CURRENT_SEASON}&active_only=True&owner_id=${ownerId}`)
|
||||
|
||||
const teamResponse: {
|
||||
count: number
|
||||
teams: Team[]
|
||||
} = await response.json()
|
||||
|
||||
if (teamResponse.count === 0) {
|
||||
console.warn('teamServices.fetchActiveTeamByOwnerId - Received 0 active teams for owner id, are they active?')
|
||||
return undefined
|
||||
}
|
||||
|
||||
return teamResponse.teams[0]
|
||||
}
|
||||
|
||||
@ -59,6 +59,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { isDiscordAuthenticated } from '@/services/authenticationService'
|
||||
import { fetchLast2DecisionsByPlayerId, type Decision } from '@/services/decisionsService'
|
||||
import { type Player, fetchPlayerByName } from '@/services/playersService'
|
||||
|
||||
@ -75,9 +76,8 @@ export default {
|
||||
playerName: { type: String, required: true }
|
||||
},
|
||||
computed: {
|
||||
isAuthorized(): boolean {
|
||||
//TODO check discord oauth/cookie/token
|
||||
return false
|
||||
isAuthenticated(): boolean {
|
||||
return isDiscordAuthenticated()
|
||||
},
|
||||
teamAbbreviation(): string | undefined {
|
||||
return this.player?.team?.abbrev
|
||||
@ -94,11 +94,11 @@ export default {
|
||||
return this.player?.vanity_card ?? this.teamThumbnail
|
||||
},
|
||||
playerCardImage1Url(): string | undefined {
|
||||
if (!this.isAuthorized) return undefined
|
||||
if (!this.isAuthenticated) return undefined
|
||||
return this.player?.image
|
||||
},
|
||||
playerCardImage2Url(): string | undefined {
|
||||
if (!this.isAuthorized) return undefined
|
||||
if (!this.isAuthenticated) return undefined
|
||||
return this.player?.image2
|
||||
},
|
||||
injuryReturnDate(): string | undefined {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="team-view centerDiv">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<h1 id="team-record">{{ teamName }}</h1>
|
||||
<h1 id="team-record">{{ teamName }} {{ teamRecord }}</h1>
|
||||
<h2 id="standings"></h2>
|
||||
<h2 id="streak">Last 8: {{ lastEight }} / Streak: {{ streak }}</h2>
|
||||
</div>
|
||||
@ -199,6 +199,10 @@ export default {
|
||||
teamName(): string | undefined {
|
||||
return this.team?.lname
|
||||
},
|
||||
teamRecord(): string | undefined {
|
||||
if (!this.teamStanding) return undefined
|
||||
return `(${this.teamStanding?.wins}-${this.teamStanding.losses})`
|
||||
},
|
||||
minorsTeamName(): string | undefined {
|
||||
return this.teamMinors?.sname
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user