Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{
"files.trimTrailingWhitespace": true,
"npm.packageManager": "yarn",
"cSpell.words": ["injectable", "nestjs", "postgres", "styleguide", "typeorm"],
"cSpell.words": [
"injectable",
"memeber",
"nestjs",
"postgres",
"styleguide",
"typeorm"
],
"gitlens.advanced.blame.customArguments": [],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
Expand Down
15 changes: 13 additions & 2 deletions apps/auth-front/src/components/AvatarDropzone/AvatarDropzone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,23 @@ export const AvatarDropzone = memo(({ avatarUrl, onChange }: DropZoneProps) => {
maxSize: MAX_FILE_SIZE,
onDropAccepted: acceptedFiles => {
const acceptedFile = acceptedFiles[0]

if (!acceptedFile) {
return
}

setStateFile({ file: acceptedFile, previewUrl: URL.createObjectURL(acceptedFile) })
clearError()
onChange?.(acceptedFile)
},
onDropRejected: fileRejections => {
handleError(fileRejections[0])
const fileRejection = fileRejections[0]

if (!fileRejection) {
return
}

handleError(fileRejection)
},
})

Expand All @@ -57,7 +68,7 @@ export const AvatarDropzone = memo(({ avatarUrl, onChange }: DropZoneProps) => {
<div className={styles.dropZone} {...getRootProps()}>
<input {...getInputProps()} />
{!isDragActive ? (
<div className={clsx(styles.container, styles.containerBorder, { [styles.containerBorderError]: hasUploadError })}>
<div className={clsx(styles.container, styles.containerBorder, hasUploadError && styles.containerBorderError)}>
{stateFile?.file ? (
<UserPhotoDrop name={stateFile.file.name} src={stateFile.previewUrl} />
) : (
Expand Down
1 change: 1 addition & 0 deletions apps/auth-front/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const createChannelInfo = async (lang: Language): Promise<ChannelInfo> =>
description: t('main.description'),
atomUrl: generateFullUrl(`/feed/${lang}`),
url: generateFullUrl(`/${lang}/posts`),
updatedAt: latestPost.publishedAt,
updatedAt: latestPost?.publishedAt ?? new Date().toISOString(),
}

return { ...channelMetadata, posts: feedPosts }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ const posts = [
},
]

posts[0].translates = { en: posts[2] }
posts[2].translates = { ru: posts[1] }
posts[0]!.translates = { en: posts[2] }
posts[2]!.translates = { ru: posts[1] }

describe('filterBlogPosts', () => {
it('Should return all (uniq by locale) posts if search and tags empty', () => {
Expand Down
1 change: 1 addition & 0 deletions apps/blog/src/generation-utils/mapHeadingsToTOC.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export function mapHeadingsToTOC(headings, tree = [], currentLevel = 2) {
/**
* @type {import('../types').TOCTreeItem}
*/
// @ts-expect-error - headings[0] exists
const heading = headings[0]
if (heading.level < currentLevel) {
return tree
Expand Down
1 change: 1 addition & 0 deletions apps/blog/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
Expand Down
1 change: 1 addition & 0 deletions apps/gamehub-client/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noUncheckedIndexedAccess": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
Expand Down
1 change: 1 addition & 0 deletions apps/ligretto-core-backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"declaration": false,
"moduleResolution": "node",
"strictNullChecks": true,
"noUncheckedIndexedAccess": true,
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const leaderListTableSpecs = [
{ title: '#', size: { xs: 2, md: 1.5 } },
{ title: 'Player', size: { xs: 7.7, md: 8.7 } },
{ title: 'Points', size: { xs: 1.8, md: 1.8 } },
]
] as const

const placeImages = [firstLevelImg, secondLevelImg, thirdLevelImg]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface PlaygroundProps {
onDeckClick: (playgroundDeckIndex: number) => void
}

const getLastCard = (deck: CardsDeck | null): Card | undefined => last(deck?.cards)
const getLastCard = (deck: CardsDeck | null | undefined): Card | undefined => last(deck?.cards)

export const Playground: React.FC<PlaygroundProps> = ({ cardsDecks, onDeckClick }) => {
const cards: (Card | undefined)[] = useMemo(() => {
Expand Down
1 change: 1 addition & 0 deletions apps/ligretto-frontend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ describe('Games Controller', () => {
expect(state).toMatchSnapshot()

expect(socketMockImpl.emit).toBeCalledTimes(2)
expect(socketMockImpl.emit).toBeCalledWith('event', createRoomSuccessAction({ game: state.games[gameId] }))
expect(socketMockImpl.emit).toBeCalledWith('event', createRoomSuccessAction({ game: state.games[gameId]! }))
expect(socketMockImpl.to).toBeCalledWith(SOCKET_ROOM_LOBBY)
expect(socketMockImpl.emit).toBeCalledWith('event', updateRoomsAction({ rooms: [gameToRoom(state.games[gameId])] }))
expect(socketMockImpl.emit).toBeCalledWith('event', updateRoomsAction({ rooms: [gameToRoom(state.games[gameId]!)] }))
})

it('Should emit createRoomErrorAction if room already exists', async () => {
Expand Down Expand Up @@ -216,8 +216,8 @@ describe('Games Controller', () => {
const state = await database.get(storage => storage)
expect(state).toMatchSnapshot()
expect(socketOne.to).toBeCalledTimes(4)
expect(socketOne.emit).toBeCalledWith('event', updateRoomsAction({ rooms: [gameToRoom(state.games[roomUuid])] }))
expect(socketOne.emit).toBeCalledWith('event', updateGameAction(state.games[roomUuid]))
expect(socketOne.emit).toBeCalledWith('event', updateRoomsAction({ rooms: [gameToRoom(state.games[roomUuid]!)] }))
expect(socketOne.emit).toBeCalledWith('event', updateGameAction(state.games[roomUuid]!))
})

it('Should create a relevant game state if one of two players leaved', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class GameplayController extends Controller {
private async updateGame(socket: Socket, gameId: string, gameState?: Game) {
const game = gameState || (await this.gameService.getGame(gameId))

if (!game) {
return
}

const action = updateGameAction(game)

socket.to(gameId).emit('event', action)
Expand Down
7 changes: 2 additions & 5 deletions apps/ligretto-gameplay-backend/src/database/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,8 @@ export class Database implements Database {
private listeners: Record<string, (nextStorage: Storage) => void> = {}

private notifyListeners() {
for (const listenerKey in this.listeners) {
if (this.listeners.hasOwnProperty(listenerKey)) {
const listener = this.listeners[listenerKey]
listener(this.storage)
}
for (const listener of Object.values(this.listeners)) {
listener(this.storage)
}
}

Expand Down
4 changes: 4 additions & 0 deletions apps/ligretto-gameplay-backend/src/entities/game/game.repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export class GameRepository {
async updateGame(gameId: UUID, updater: (game: Game) => Game): Promise<Game> {
const game = await this.getGame(gameId)

if (!game) {
throw new Error(`Game with id ${gameId} not found`)
}

return this.database.set(storage => (storage.games[gameId] = updater(game)))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ export class GameService {
async getRoundResult(gameId: UUID) {
const game = await this.getGame(gameId)

if (!game) {
throw new Error(`Game with id ${gameId} not found`)
}

const initialScoresByPlayer = Object.keys(game.players).reduce<Record<string, 0>>((scores, playerId) => ({ ...scores, [playerId]: 0 }), {})

const clearPlaygroundDecks = game.playground.decks.filter(nonNullable)
Expand Down
42 changes: 30 additions & 12 deletions apps/ligretto-gameplay-backend/src/entities/player/player.repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,63 @@ export class PlayerRepository {
@inject(IOC_TYPES.Database) private database: Database

async getPlayer(gameId: UUID, playerId: UUID) {
return this.database.get(storage => storage.games[gameId].players[playerId])
return this.database.get(storage => storage.games[gameId]?.players?.[playerId])
}

async getCards(gameId: UUID, playerId: UUID) {
return this.database.get(storage => storage.games[gameId].players[playerId]?.cards)
return this.database.get(storage => storage.games[gameId]?.players?.[playerId]?.cards)
}

async getCard(gameId: UUID, playerId: UUID, position: number) {
return this.database.get(storage => storage.games[gameId].players[playerId]?.cards[position])
return this.database.get(storage => storage.games[gameId]?.players?.[playerId]?.cards?.[position])
}

async addCard(gameId: UUID, playerId: UUID, card: Card, position: number) {
return this.database.set(storage => storage.games[gameId].players[playerId]?.cards.splice(position, 1, card))
return this.database.set(storage => {
const player = storage.games[gameId]?.players?.[playerId]
const cards = player?.cards

if (!cards) {
return
}

return cards.splice(position, 1, card)
})
}

async removeCard(gameId: UUID, playerId: UUID, position: number) {
return this.database.set(storage => storage.games[gameId].players[playerId]?.cards.splice(position, 1, null))
return this.database.set(storage => {
const player = storage.games[gameId]?.players?.[playerId]
const cards = player?.cards

if (!cards) {
return
}

return cards.splice(position, 1, null)
})
}

async getLigrettoDeck(gameId: UUID, playerId: UUID) {
return this.database.get(storage => storage.games[gameId].players[playerId]?.ligrettoDeck)
return this.database.get(storage => storage.games[gameId]?.players?.[playerId]?.ligrettoDeck)
}

async removeCardFromLigrettoDeck(gameId: UUID, playerId: UUID) {
await this.database.set(storage => storage.games[gameId].players[playerId]?.ligrettoDeck.cards.pop())
await this.database.set(storage => storage.games[gameId]?.players?.[playerId]?.ligrettoDeck?.cards.pop())

return (await this.getLigrettoDeck(gameId, playerId))?.cards.length
}

async getStackDeck(gameId: UUID, playerId: UUID) {
return this.database.get(storage => storage.games[gameId].players[playerId]?.stackDeck)
return this.database.get(storage => storage.games[gameId]?.players?.[playerId]?.stackDeck)
}

async getStackOpenDeck(gameId: UUID, playerId: UUID) {
return this.database.get(storage => storage.games[gameId].players[playerId]?.stackOpenDeck)
return this.database.get(storage => storage.games[gameId]?.players?.[playerId]?.stackOpenDeck)
}

async removeCardFromStackOpenDeck(gameId: UUID, playerId: UUID) {
return this.database.set(storage => storage.games[gameId].players[playerId]?.stackOpenDeck.cards.pop())
return this.database.set(storage => storage.games[gameId]?.players?.[playerId]?.stackOpenDeck?.cards.pop())
}

async updateStackDeck(gameId: UUID, playerId: UUID, updater: (cardsDeck: CardsDeck) => CardsDeck) {
Expand All @@ -55,7 +73,7 @@ export class PlayerRepository {
return
}
return this.database.set(storage => {
const player = storage.games[gameId].players[playerId]
const player = storage.games[gameId]?.players?.[playerId]
if (!player) {
return
}
Expand All @@ -69,7 +87,7 @@ export class PlayerRepository {
return
}
return this.database.set(storage => {
const player = storage.games[gameId].players[playerId]
const player = storage.games[gameId]?.players?.[playerId]
if (!player) {
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,50 @@ export class PlaygroundRepository {
@inject(IOC_TYPES.Database) private database: Database

getDecks(gameId: UUID) {
return this.database.get(storage => storage.games[gameId].playground.decks)
return this.database.get(storage => storage.games[gameId]?.playground.decks)
}

getDeck(gameId: UUID, position: number) {
return this.database.get(storage => storage.games[gameId].playground.decks[position])
return this.database.get(storage => storage.games[gameId]?.playground.decks?.[position])
}

addDroppedDeck(gameId: UUID, cardsDeck: CardsDeck) {
return this.database.set(storage => {
const decks = storage.games[gameId].playground.droppedDecks
decks.push(cardsDeck)
return decks
const game = storage.games[gameId]
const droppedDecks = game?.playground.droppedDecks

if (!droppedDecks) {
return
}

droppedDecks.push(cardsDeck)
return droppedDecks
})
}

removeDeck(gameId: UUID, position: number) {
return this.database.set(storage => {
storage.games[gameId].playground.decks[position] = null
const game = storage.games[gameId]
if (!game) {
return
}

game.playground.decks[position] = null
})
}

async updateDeck(gameId: UUID, position: number, updater: (deck: CardsDeck | null) => CardsDeck) {
const deck = await this.getDeck(gameId, position)

return this.database.set(storage => {
const updated = updater(deck)
const game = storage.games[gameId]
if (!game) {
return
}

const updated = updater(deck ?? null)
console.log('Updated deck', position, updated)
storage.games[gameId].playground.decks[position] = updated
game.playground.decks[position] = updated
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@ export class PlaygroundService {

async findAvailableDeckIndex(gameId: UUID, card: Card) {
const decks = await this.getDecks(gameId)
return decks.findIndex(deck => isDeckAvailable(deck, card))

if (!decks) {
return -1
}

return decks.findIndex(deck => isDeckAvailable(deck ?? null, card))
}

async putCard(gameId: UUID, card: Card, deckIndex: number) {
const deck = await this.playgroundRepository.getDeck(gameId, deckIndex)

if (!isDeckAvailable(deck, card)) {
if (!isDeckAvailable(deck ?? null, card)) {
return
}

Expand Down
1 change: 1 addition & 0 deletions apps/ligretto-gameplay-backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"strict": true,
// Not compatible with inversify
"strictPropertyInitialization": false,
"noUncheckedIndexedAccess": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
Expand Down
1 change: 1 addition & 0 deletions packages/analytics/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"outDir": "./build",
"sourceMap": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"allowSyntheticDefaultImports": true
},
"exclude": ["node_modules", "build"],
Expand Down
Loading