Skip to content

Commit

Permalink
Programmatically only show speaker badge for current year
Browse files Browse the repository at this point in the history
  • Loading branch information
lazerwalker committed Oct 19, 2024
1 parent d3d9961 commit ca6e7d9
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 46 deletions.
4 changes: 2 additions & 2 deletions server/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ interface Database {

setModStatus(userId: string, isMod: boolean)

setSpeakerStatus(userId: string, isSpeaker: boolean)
setSpeakerStatus(userId: string, year: string, isSpeaker: boolean)

banUser(user: User, isBanned: boolean)

modList(): Promise<string[]>
speakerList(): Promise<string[]>
speakerListForYear(year: string): Promise<string[]>

// -----------------------------------------------------------------
// SETTINGS DATA
Expand Down
40 changes: 20 additions & 20 deletions server/src/endpoints/toggleSpeakerStatus.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AuthenticatedEndpointFunction, LogFn } from '../endpoint'
import { awardUserBadge, isSpeaker, minimizeUser, User } from '../user'
import { awardUserBadge, isSpeaker, isSpeakerForYear, minimizeUser, User } from '../user'
import { DB } from '../database'
import { UnlockableBadgeMap } from '../badges'

const toggleSpeakerStatus: AuthenticatedEndpointFunction = async (user: User, inputs: any, log: LogFn) => {
const userIdToToggle: string = inputs.userId
const year: string = inputs.year
const thisYear: string = `${(new Date()).getFullYear()}`
const isForPastYear = inputs.year !== thisYear
const isForPastYear = year !== thisYear

if (!userIdToToggle) {
return {
Expand All @@ -19,27 +20,26 @@ const toggleSpeakerStatus: AuthenticatedEndpointFunction = async (user: User, in

let toggledUser: User

/* toggle past speaker */
if (isForPastYear) {
const pastSpeakerBadge = UnlockableBadgeMap['🎙️']
// Set their database status (which will give them the special badge if current year)
if (await isSpeakerForYear(userIdToToggle, year)) {
log(`[MOD] Setting user ${userIdToToggle} to speaker=false for ${year}`)
toggledUser = await DB.setSpeakerStatus(userIdToToggle, year, false)
} else {
log(`[MOD] Setting user ${userIdToToggle} to speaker=true for ${year}`)
toggledUser = await DB.setSpeakerStatus(userIdToToggle, year, true)
}

const profile = await DB.getUser(userIdToToggle)
// Assign them the 'past speaker' badge
// (We don't currently have per-year speakers, that would be a reasonable improvement)
const pastSpeakerBadge = UnlockableBadgeMap['🎙️']

if (profile.unlockedBadges.includes(pastSpeakerBadge)) {
const remainingUnlockedBadges = profile.unlockedBadges.filter(badge => badge !== pastSpeakerBadge)
toggledUser = await DB.setPartialUserProfile(userIdToToggle, { unlockedBadges: remainingUnlockedBadges })
} else {
toggledUser = await awardUserBadge(userIdToToggle, pastSpeakerBadge)
}
/* toggle current speaker */
const profile = await DB.getUser(userIdToToggle)

if (profile.unlockedBadges.includes(pastSpeakerBadge)) {
const remainingUnlockedBadges = profile.unlockedBadges.filter(badge => badge !== pastSpeakerBadge)
toggledUser = await DB.setPartialUserProfile(userIdToToggle, { unlockedBadges: remainingUnlockedBadges })
} else {
if (await isSpeaker(userIdToToggle)) {
log(`[MOD] Setting user ${userIdToToggle} to speaker=false`)
toggledUser = await DB.setSpeakerStatus(userIdToToggle, false)
} else {
log(`[MOD] Setting user ${userIdToToggle} to speaker=true`)
toggledUser = await DB.setSpeakerStatus(userIdToToggle, true)
}
toggledUser = await awardUserBadge(userIdToToggle, pastSpeakerBadge)
}

return {
Expand Down
37 changes: 23 additions & 14 deletions server/src/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ interface RedisInternal extends Database {
addMod (userId: string)
removeMod (userId: string)

addSpeaker (userId: string)
removeSpeaker (userId: string)
addSpeakerForYear (userId: string, year: string)
removeSpeakerForYear (userId: string, year: string)
}

const Redis: RedisInternal = {
Expand Down Expand Up @@ -213,8 +213,8 @@ const Redis: RedisInternal = {
return await getSet(modListKey) || []
},

async speakerList (): Promise<string[]> {
return await getSet(speakerListKey) || []
async speakerListForYear (year: string): Promise<string[]> {
return await getSet(speakerListKey(year)) || []
},

async setModStatus (userId: string, isMod: boolean) {
Expand All @@ -236,24 +236,30 @@ const Redis: RedisInternal = {
await removeFromSet(modListKey, userId)
},

async setSpeakerStatus (userId: string, isSpeaker: boolean) {
async setSpeakerStatus (userId: string, year: string, isSpeaker: boolean) {
const profile = await Redis.getUser(userId)
if (!profile.speakerYears) profile.speakerYears = []

if (isSpeaker) {
await Redis.addSpeaker(userId)
await Redis.addSpeakerForYear(userId, year)
if (!profile.speakerYears.includes(year)) {
profile.speakerYears.push(year);

Check failure on line 246 in server/src/redis.ts

View workflow job for this annotation

GitHub Actions / build

Extra semicolon
}
} else {
await Redis.removeSpeaker(userId)
await Redis.removeSpeakerForYear(userId, year)
profile.speakerYears = profile.speakerYears.filter(y => y !== year)
}
const profile = await Redis.getUser(userId)
profile.isSpeaker = isSpeaker

await Redis.setUserProfile(userId, profile)
return profile
},

async addSpeaker (userId: string) {
await addToSet(speakerListKey, userId)
async addSpeakerForYear (userId: string, year: string) {
await addToSet(speakerListKey(year), userId)
},

async removeSpeaker (userId: string) {
await removeFromSet(speakerListKey, userId)
async removeSpeakerForYear (userId: string, year: string) {
await removeFromSet(speakerListKey(year), userId)
},

// Server settings
Expand Down Expand Up @@ -403,14 +409,17 @@ const Redis: RedisInternal = {
const activeUsersKey = 'activeUsersList'

const modListKey = 'mods'
const speakerListKey = 'speakers'

const serverSettingsKey = 'serverSettings'

const allUserIdsKey = 'allUserIds'

const roomIdsKey = 'roomIds'

function speakerListKey (year: string): string {
return 'speakers_${year}'

Check failure on line 420 in server/src/redis.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected template string expression
}

function roomDataKey (roomId: string): string {
return `room_${roomId}`
}
Expand Down
13 changes: 9 additions & 4 deletions server/src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface MinimalUser {
username: string;
pronouns?: string;
isMod?: boolean;
isSpeaker?: boolean // TODO: Currently never set
speakerYears?: string[]
isBanned?: boolean;
// From https://www.w3schools.com/colors/colors_names.asp
nameColor?: string;
Expand Down Expand Up @@ -57,11 +57,16 @@ export async function isMod (userId: string) {
return modList.includes(userId)
}

export async function isSpeaker (userId: string) {
const speakerList = await DB.speakerList()
export async function isSpeakerForYear (userId: string, year: string) {
const speakerList = await DB.speakerListForYear(year)
return speakerList.includes(userId)
}

export async function isSpeaker (userId: string) {
const thisYear: string = `${(new Date()).getFullYear()}`
return await isSpeakerForYear(userId, thisYear)
}

export async function updateModStatus (userId: string) {
const userIsMod = await isMod(userId)

Expand Down Expand Up @@ -214,7 +219,7 @@ export function minimizeUser (user: User | PublicUser): MinimalUser {
item: user.item,
polymorph: user.polymorph,
isMod: user.isMod,
isSpeaker: user.isSpeaker,
speakerYears: user.speakerYears,
fontReward: user.fontReward,
equippedBadges: user.equippedBadges,
pronouns: user.pronouns
Expand Down
13 changes: 7 additions & 6 deletions src/components/NameView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,21 @@ export default function NameView (props: Props) {

const isSelf = props.userId === myId

// This will fail if the user's client has the wrong year set,
// that shouldn't be a concern?
const thisYear: string = `${(new Date()).getFullYear()}`
const lastYear: string = `${(new Date()).getFullYear() - 1}`

const user: User = userMap[props.userId]
const username = user && user.username
const isMod = user && user.isMod
const isSpeaker = user && user.isSpeaker
const isSpeaker = user && user.speakerYears?.includes(thisYear)
const isBanned = user && user.isBanned

// isMod = the user whose name being rendered is a mod
// userIsMod = the user who is logged in is a mod
const userIsMod = userMap[myId].isMod

const thisYear: string = `${(new Date()).getFullYear()}`
const lastYear: string = `${(new Date()).getFullYear() - 1}`

// This sometimes gets called before `connect` returns any users
// That itself is a bug to fix, but this can at least guard against it.
if (!user || (Object.keys(userMap).length === 1 && !isSelf)) {
Expand Down Expand Up @@ -157,8 +159,7 @@ export default function NameView (props: Props) {
const badges = (user.equippedBadges || [])
.map((b, i) => <BadgeView key={`badge-${i}`} emoji={b?.emoji} description={b?.description} isCustom={b?.isCustom} />)

// TODO: This is not yet being set anywhere
if (user.isSpeaker) {
if (isSpeaker) {
badges.unshift(
<BadgeView key='badge-speaker' isCustom={true} emoji='speaker' description='Speaker' />
)
Expand Down

0 comments on commit ca6e7d9

Please sign in to comment.