app/types/index.ts (90 lines of code) (raw):
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {z} from 'zod';
export const GameIdSchema = z.string();
export const GameIdObjectSchema = z.object({gameId: GameIdSchema});
export const AnswerSelectionSchema = z.array(z.boolean());
export const AnswerSelectionWithGameIdSchema = z.object({
gameId: GameIdSchema,
answerSelection: AnswerSelectionSchema,
});
export const TimePerQuestionSchema = z.number({invalid_type_error: 'Time per question must be a number'}).int().max(600, 'Time per question must be 600 or less.').min(10, 'Time per question must be at least 10.');
export const TimePerAnswerSchema = z.number({invalid_type_error: 'Time per answer must be a number'}).int().max(600, 'Time per answer must be 600 or less.').min(5, 'Time per answer must be at least 5.');
export const questionAdvancementOptionDetails = [
{
type: 'MANUAL',
description: 'Manually advance to the next question.',
shortName: 'Manual',
automaticallyAdvanceToNextQuestion: false,
},
{
type: 'AUTOMATIC',
description: 'Automatically advance question on a timer.',
shortName: 'Automatic',
automaticallyAdvanceToNextQuestion: true,
},
] as const;
const questionAdvancementOptions = ['MANUAL', 'AUTOMATIC'] as const;
export const QuestionAdvancementEnum = z.enum(questionAdvancementOptions);
export const questionAdvancements = QuestionAdvancementEnum.Values;
export type QuestionAdvancement = z.infer<typeof QuestionAdvancementEnum>;
export const GameSettingsSchema = z.object({
timePerQuestion: TimePerQuestionSchema,
timePerAnswer: TimePerAnswerSchema,
questionAdvancement: QuestionAdvancementEnum,
});
export type GameSettings = z.infer<typeof GameSettingsSchema>;
const AnswerSchema = z.object({
isCorrect: z.boolean(),
isSelected: z.boolean().default(false),
text: z.string(),
});
export const QuestionSchema = z.object({
answers: z.array(AnswerSchema).min(1).max(4),
prompt: z.string(),
explanation: z.string(),
playerGuesses: z.record(z.string(), z.array(z.boolean())).default({}),
});
export type Question = z.infer<typeof QuestionSchema>;
const gameStatesOptions = ['NOT_STARTED', 'SHOWING_CORRECT_ANSWERS', 'AWAITING_PLAYER_ANSWERS', 'GAME_OVER'] as const;
const GameStateEnum = z.enum(gameStatesOptions);
export const gameStates = GameStateEnum.Values;
export const GameStateUpdateSchema = z.object({
state: GameStateEnum,
currentQuestionIndex: z.number().int().nonnegative(),
});
export type GameStateUpdate = z.infer<typeof GameStateUpdateSchema>;
export const LeaderSchema = z.object({
uid: z.string(),
displayName: z.string(),
});
const emptyLeader = LeaderSchema.parse({
uid: '',
displayName: '',
});
export const GameSchema = z.object({
questions: z.record(z.string(), QuestionSchema),
leader: LeaderSchema,
players: z.record(z.string(), z.string()),
state: GameStateEnum,
currentQuestionIndex: z.number().int().nonnegative(),
currentStateStartTime: z.object({seconds: z.number()}),
questionAdvancement: QuestionAdvancementEnum,
timePerQuestion: TimePerQuestionSchema,
timePerAnswer: TimePerAnswerSchema,
});
export const emptyGame = GameSchema.parse({
questions: {},
leader: emptyLeader,
players: {},
state: 'NOT_STARTED',
currentQuestionIndex: 0,
questionAdvancement: 'AUTOMATIC',
currentStateStartTime: {seconds: 0},
timePerQuestion: 60,
timePerAnswer: 20,
});
export type Game = z.infer<typeof GameSchema>;
export const TokensSchema = z.object({
userToken: z.string(),
appCheckToken: z.string(),
});
export type Tokens = z.infer<typeof TokensSchema>;