Skip to content

Commit

Permalink
Merge pull request #269 from TaloDev/develop
Browse files Browse the repository at this point in the history
Release 0.33.0
  • Loading branch information
tudddorrr authored Aug 1, 2024
2 parents 1f1849d + 9d49a06 commit 458991b
Show file tree
Hide file tree
Showing 17 changed files with 179 additions and 81 deletions.
33 changes: 23 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"lint-staged": {
"*.{ts,js,tsx,jsx}": "eslint --fix"
},
"version": "0.32.0",
"version": "0.33.0",
"engines": {
"node": "20.x"
},
Expand Down
Binary file modified src/.DS_Store
Binary file not shown.
13 changes: 8 additions & 5 deletions src/api/useEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { Game } from '../entities/game'
import makeValidatedGetRequest from './makeValidatedGetRequest'
import { z } from 'zod'

export const eventsVisualisationPayloadSchema = z.object({
name: z.string(),
date: z.number(),
count: z.number(),
change: z.number()
})

export default function useEvents(activeGame: Game, startDate: string, endDate: string) {
const fetcher = async ([url]: [string]) => {
const qs = new URLSearchParams({
Expand All @@ -13,11 +20,7 @@ export default function useEvents(activeGame: Game, startDate: string, endDate:

const res = await makeValidatedGetRequest(`${url}?${qs}`, z.object({
events: z.record(
z.object({
name: z.string(),
date: z.number(),
count: z.number()
})
z.array(eventsVisualisationPayloadSchema)
),
eventNames: z.array(z.string())
}))
Expand Down
27 changes: 27 additions & 0 deletions src/api/usePlayerAuthActivities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import useSWR from 'swr'
import buildError from '../utils/buildError'
import { Game } from '../entities/game'
import makeValidatedGetRequest from './makeValidatedGetRequest'
import { z } from 'zod'
import { playerAuthActivitySchema } from '../entities/playerAuthActivity'

export default function usePlayerAuthActivities(activeGame: Game, playerId: string) {
const fetcher = async ([url]: [string]) => {
const res = await makeValidatedGetRequest(url, z.object({
activities: z.array(playerAuthActivitySchema)
}))

return res
}

const { data, error } = useSWR(
[`/games/${activeGame.id}/players/${playerId}/auth-activities`],
fetcher
)

return {
activities: data?.activities ?? [],
loading: !data && !error,
error: error && buildError(error)
}
}
6 changes: 6 additions & 0 deletions src/assets/talo-service.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions src/components/ActivityRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import SecondaryTitle from './SecondaryTitle'
import { format } from 'date-fns'

type ActivityRendererProps = {
section: {
date: Date
items: {
createdAt: string
description: string
extra: Record<string, unknown>
}[]
}
}

export default function ActivityRenderer({ section }: ActivityRendererProps) {
return (
<div className='space-y-4'>
<SecondaryTitle className='text-lg'>{format(section.date, 'dd MMM Y')}</SecondaryTitle>

{section.items.map((item, itemIdx) => (
<div key={itemIdx} className='border-t-2 border-gray-700 pt-4'>
<p><span className='text-sm mr-2 text-indigo-300'>{format(new Date(item.createdAt), 'HH:mm')}</span> {item.description}</p>

{item.extra &&
<div className='-ml-2 flex flex-wrap'>
{Object.keys(item.extra).sort((a, b) => {
if (b === 'Player') return 1

return a.localeCompare(b)
}).map((key) => (
<code key={key} className='bg-gray-900 rounded p-2 text-xs md:text-sm ml-2 mt-2'>{key} = {String(item.extra[key])}</code>
))}
</div>
}
</div>
))}
</div>
)
}
2 changes: 2 additions & 0 deletions src/components/PlayerAliases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IconBrandSteam, IconMail, IconQuestionMark, IconUser } from '@tabler/ic
import Tippy from '@tippyjs/react'
import useSortedItems from '../utils/useSortedItems'
import { PlayerAlias } from '../entities/playerAlias'
import taloIcon from '../assets/talo-service.svg'

type PlayerAliasesProps = {
aliases: PlayerAlias[]
Expand All @@ -18,6 +19,7 @@ export default function PlayerAliases({
case 'steam': return <IconBrandSteam size={16} />
case 'username': return <IconUser size={16} />
case 'email': return <IconMail size={16} />
case 'talo': return <img src={taloIcon} alt='Talo' className='w-[16px] h-[16px]' />
default: return <IconQuestionMark size={16} />
}
}
Expand Down
8 changes: 3 additions & 5 deletions src/components/charts/ChartTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import { format } from 'date-fns'
import { uniqBy } from 'lodash-es'
import clsx from 'clsx'
import getEventColour from '../../utils/getEventColour'
import { z } from 'zod'
import { eventsVisualisationPayloadSchema } from '../../api/useEvents'

type Payload = {
count: number
name: string
change: number
}
type Payload = z.infer<typeof eventsVisualisationPayloadSchema>

type ChartTooltipProps = {
active?: boolean
Expand Down
2 changes: 1 addition & 1 deletion src/entities/apiKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const apiKeySchema = z.object({
gameId: z.number(),
createdBy: z.string(),
createdAt: z.string().datetime(),
lastUsedAt: z.string().datetime().optional()
lastUsedAt: z.string().datetime().nullable()
})

export type APIKey = z.infer<typeof apiKeySchema>
8 changes: 2 additions & 6 deletions src/entities/gameActivity.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { z } from 'zod'

const extraSchema = z.object({
display: z.record(z.unknown()).optional()
}).catchall(z.unknown())

export const gameActivitySchema = z.object({
id: z.number(),
type: z.string(),
type: z.number(),
description: z.string(),
extra: extraSchema,
extra: z.record(z.unknown()),
createdAt: z.string().datetime()
})

Expand Down
3 changes: 2 additions & 1 deletion src/entities/playerAlias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export enum PlayerAliasService {
EPIC = 'epic',
USERNAME = 'username',
EMAIL = 'email',
CUSTOM = 'custom'
CUSTOM = 'custom',
TALO = 'talo'
}

export const playerAliasSchema = z.object({
Expand Down
11 changes: 11 additions & 0 deletions src/entities/playerAuthActivity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { z } from 'zod'

export const playerAuthActivitySchema = z.object({
id: z.number(),
type: z.number(),
description: z.string(),
extra: z.record(z.unknown()),
createdAt: z.string().datetime()
})

export type PlayerAuthActivity = z.infer<typeof playerAuthActivitySchema>
54 changes: 5 additions & 49 deletions src/pages/Activity.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,17 @@
import { useMemo } from 'react'
import useGameActivities from '../api/useGameActivities'
import ErrorMessage from '../components/ErrorMessage'
import activeGameState, { SelectedActiveGame } from '../state/activeGameState'
import { useRecoilValue } from 'recoil'
import useSortedItems from '../utils/useSortedItems'
import { differenceInDays, subDays, startOfDay, isSameDay, format } from 'date-fns'
import SecondaryNav from '../components/SecondaryNav'
import { secondaryNavRoutes } from '../pages/Dashboard'
import Page from '../components/Page'
import SecondaryTitle from '../components/SecondaryTitle'
import useDaySections from '../utils/useDaySections'
import ActivityRenderer from '../components/ActivityRenderer'

function Activity() {
export default function Activity() {
const activeGame = useRecoilValue(activeGameState) as SelectedActiveGame
const { activities, loading, error } = useGameActivities(activeGame)

const sortedActivities = useSortedItems(activities, 'createdAt')

const sections = useMemo(() => {
if (sortedActivities.length === 0) return []
const latestDate = sortedActivities[0].createdAt
const oldestDate = sortedActivities[sortedActivities.length - 1].createdAt

const numSections = differenceInDays(new Date(latestDate), new Date(oldestDate)) + 1

const sections = []
for (let i = 0; i < numSections; i++) {
const date = startOfDay(subDays(new Date(latestDate), i))
sections.push({
date,
items: sortedActivities.filter((activity) => isSameDay(date, new Date(activity.createdAt)))
})
}

return sections.filter((section) => section.items.length > 0)
}, [sortedActivities])
const sections = useDaySections(activities)

return (
<Page
Expand All @@ -46,32 +24,10 @@ function Activity() {
}

{!error && sections.map((section, sectionIdx) => (
<div key={sectionIdx} className='space-y-4'>
<SecondaryTitle>{format(section.date, 'dd MMM Y')}</SecondaryTitle>

{section.items.map((item, itemIdx) => (
<div key={itemIdx} className='border-t border-gray-600 pt-4'>
<p><span className='text-sm mr-2 text-indigo-300'>{format(new Date(item.createdAt), 'HH:mm')}</span> {item.description}</p>

{item.extra &&
<div className='-ml-2 flex flex-wrap'>
{Object.keys(item.extra).sort((a, b) => {
if (b === 'Player') return 1

return a.localeCompare(b)
}).map((key) => (
<code key={key} className='bg-gray-900 rounded p-2 text-xs md:text-sm ml-2 mt-2'>{key} = {item.extra[key] as string | number}</code>
))}
</div>
}
</div>
))}
</div>
<ActivityRenderer key={sectionIdx} section={section} />
))}

{error && <ErrorMessage error={error} />}
</Page>
)
}

export default Activity
4 changes: 1 addition & 3 deletions src/pages/Events.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import EventsOverview from '../components/events/EventsOverview'

const Events = () => {
export default function Events() {
return <EventsOverview />
}

export default Events
Loading

0 comments on commit 458991b

Please sign in to comment.