Skip to content

Commit

Permalink
Demo Mode (#486)
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinejaussoin authored Mar 1, 2023
1 parent bef2a07 commit 6857d55
Show file tree
Hide file tree
Showing 35 changed files with 291 additions and 24 deletions.
1 change: 1 addition & 0 deletions backend/src/common/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ export const defaultSession: Omit<Session, 'createdBy'> = {
locked: false,
ready: [],
timer: null,
demo: false,
};
4 changes: 4 additions & 0 deletions backend/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface Session extends PostContainer, Entity {
createdBy: User;
ready: string[];
timer: Date | null;
demo: boolean;
}

export interface SessionMetadata extends Entity {
Expand Down Expand Up @@ -218,6 +219,7 @@ export type TrackingEvent =
| 'register/password'
| 'register/oauth'
| 'register/anonymous'
| 'register/demo'
| 'subscribe/initial'
| 'subscribe/launch-stripe'
| 'subscribe/purchased'
Expand Down Expand Up @@ -255,5 +257,7 @@ export type StripeLocales =
| 'ja-JP'
| 'nl-NL'
| 'pt-BR'
| 'pt-PT'
| 'no-NO'
| 'uk-UA'
| 'sv-S';
84 changes: 84 additions & 0 deletions backend/src/db/actions/demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { v4 } from 'uuid';
import { Post, Session } from '../../common/types.js';
import { UserEntity } from '../../db/entities/UserIdentity.js';
import { savePost, saveVote } from './posts.js';
import { createSession, getSession, saveSession } from './sessions.js';
import { getNext, getMiddle } from '../../lexorank.js';
import { registerAnonymousUser } from './users.js';
import { DeepPartial } from 'typeorm';

export async function createDemoSession(author: UserEntity): Promise<Session> {
const session = await createSession(author);
session.name = 'My Retro Demo';
session.demo = true;
session.options = {
...session.options,
allowActions: true,
allowGiphy: true,
allowMultipleVotes: true,
allowReordering: true,
allowSelfVoting: true,
allowTimer: true,
allowCancelVote: true,
allowGrouping: true,
maxDownVotes: 20,
maxUpVotes: 20,
readonlyOnTimerEnd: true,
};
await saveSession(author.id, session);
let rank = getMiddle();

const otherUser = (await registerAnonymousUser('John Doe^' + v4(), v4()))!;

const otherUserId = otherUser!.user!.id;

async function createPost(
content: string,
column: number,
votes = 0,
own = false
) {
rank = getNext(rank);
const postData: DeepPartial<Post> = {
content,
column,
giphy: null,
action: null,
rank,
votes: [],
group: null,
id: v4(),
};
const post = (await savePost(
own ? author.id : otherUserId,
session.id,
postData
))!;
for (let i = 0; i < votes; i++) {
saveVote(otherUserId, '', post.id, {
id: v4(),
type: 'like',
user: otherUser.user.toJson(),
});
}
const updatedSession = await getSession(session.id);
return updatedSession;
}

await Promise.all([
createPost("I'm enjoying our new retrospective board!", 0, 5),
createPost('I love how we can vote on posts', 0),
createPost('I wish I discovered this tool sooner 😅', 1, 2, true),
createPost(
'Have you tried different settings? Click on "Customise" to see what you can do.',
2
),
createPost('Try Giphy by clicking on the yellow smiley face!', 2, 1, true),
createPost(
'You can also share this URL with somebody else to collaborate on the same board.',
2
),
]);

return session;
}
3 changes: 2 additions & 1 deletion backend/src/db/actions/posts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DeepPartial } from 'typeorm';
import { Post, PostGroup, Vote } from '../../common/index.js';
import {
PostRepository,
Expand All @@ -17,7 +18,7 @@ export async function getNumberOfPosts(userId: string): Promise<number> {
export async function savePost(
userId: string,
sessionId: string,
post: Post
post: DeepPartial<Post>
): Promise<Post | null> {
return await transaction(async (manager) => {
const postRepository = manager.withRepository(PostRepository);
Expand Down
4 changes: 4 additions & 0 deletions backend/src/db/entities/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ export default class SessionEntity {
visitors: UserEntity[] | undefined;
@Column({ default: false })
public locked: boolean;
@Column({ default: false })
public demo: boolean;
@Column({ default: null, type: 'timestamp with time zone', nullable: true })
public timer: Date | null;
@Column('text', { array: true, default: '{}' })
Expand All @@ -90,6 +92,7 @@ export default class SessionEntity {
locked: this.locked,
ready: this.ready,
timer: this.timer,
demo: this.demo,
};
}

Expand All @@ -107,5 +110,6 @@ export default class SessionEntity {
this.locked = false;
this.ready = [];
this.timer = null;
this.demo = false;
}
}
14 changes: 14 additions & 0 deletions backend/src/db/migrations/1677668065305-Demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class Demo1677668065305 implements MigrationInterface {
name = 'Demo1677668065305'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" ADD "demo" boolean NOT NULL DEFAULT false`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "demo"`);
}

}
3 changes: 2 additions & 1 deletion backend/src/db/repositories/PostRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import SessionRepository from './SessionRepository.js';
import { Post as JsonPost, defaultSession } from '../../common/index.js';
import { cloneDeep } from 'lodash-es';
import { getBaseRepository, saveAndReload } from './BaseRepository.js';
import { DeepPartial } from 'typeorm';

export default getBaseRepository(PostEntity).extend({
async updateFromJson(
Expand All @@ -28,7 +29,7 @@ export default getBaseRepository(PostEntity).extend({
async saveFromJson(
sessionId: string,
userId: string,
post: JsonPost
post: DeepPartial<JsonPost>
): Promise<PostEntity | undefined> {
const session = await this.manager.findOne(SessionEntity, {
where: { id: sessionId },
Expand Down
24 changes: 24 additions & 0 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import mung from 'express-mung';
import { QueryFailedError } from 'typeorm';
import { deleteAccount } from './db/actions/delete.js';
import { noop } from 'lodash-es';
import { createDemoSession } from './db/actions/demo.js';

const realIpHeader = 'X-Forwarded-For';
const sessionSecret = `${config.SESSION_SECRET!}-4.11.5`; // Increment to force re-auth
Expand Down Expand Up @@ -281,6 +282,29 @@ db().then(() => {
});
});

// Create a demo session
app.post('/api/demo', heavyLoadLimiter, async (req, res) => {
const identity = await getIdentityFromRequest(req);
setScope(async (scope) => {
if (identity) {
try {
const session = await createDemoSession(identity.user);
res.status(200).send(session);
} catch (err: unknown) {
if (err instanceof QueryFailedError) {
reportQueryError(scope, err);
}
res.status(500).send();
throw err;
}
} else {
res
.status(401)
.send('You must be logged in in order to create a session');
}
});
});

app.post('/api/logout', async (req, res, next) => {
req.logout({ keepSessionInfo: false }, noop);
req.session?.destroy((err: string) => {
Expand Down
21 changes: 21 additions & 0 deletions backend/src/lexorank.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { LexoRank } from 'lexorank';

export function getMiddle(): string {
return LexoRank.middle().toString();
}

export function getNext(rank: string): string {
return LexoRank.parse(rank).genNext().toString();
}

export function getPrevious(rank: string): string {
return LexoRank.parse(rank).genPrev().toString();
}

export function getBetween(before: string, after: string): string {
try {
return LexoRank.parse(before).between(LexoRank.parse(after)).toString();
} catch {
return before;
}
}
6 changes: 3 additions & 3 deletions frontend/crowdin.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"project_id" : "512896"
"base_path" : "."
"base_path" : "../"
"base_url" : "https://api.crowdin.com"
"preserve_hierarchy": true

files: [
{
"source" : "/src/translations/locales/en-GB.json",
"translation" : "/src/translations/locales/%locale%.json"
"source" : "frontend/src/translations/locales/en-GB.json",
"translation" : "frontend/src/translations/locales/%locale%.json"
}
]
2 changes: 2 additions & 0 deletions frontend/src/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const Invite = lazy(() => import('./views/layout/Invite'));
const Panel = lazy(() => import('./views/Panel'));
const EncryptionDoc = lazy(() => import('./views/home/Encryption'));
const AdminPage = lazy(() => import('./views/admin/AdminPage'));
const Demo = lazy(() => import('./views/Demo'));

const Title = styled(Typography)`
color: white;
Expand Down Expand Up @@ -141,6 +142,7 @@ function App() {
<Route path="reset" element={<ResetPasswordPage />} />
<Route path="account" element={<AccountPage />} />
<Route path="login" element={<LoginPage />} />
<Route path="demo" element={<Demo />} />
<Route path="subscribe" element={<SubscribePageOuter />}>
<Route path="success" element={<SuccessPage />} />
<Route path="cancel" element={<CancelPage />} />
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export async function createGame(): Promise<Session | null> {
return await fetchPostGet<unknown, Session | null>('/api/create', null);
}

export async function createDemoGame(): Promise<Session | null> {
return await fetchPostGet<unknown, Session | null>('/api/demo', null);
}

export async function createEncryptedGame(
encryptionKey: string
): Promise<Session | null> {
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/common/models.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SessionOptions, Session } from './types';
import { SessionOptions, Session } from './types.js';

export const defaultOptions: SessionOptions = {
allowActions: true,
Expand All @@ -11,9 +11,9 @@ export const defaultOptions: SessionOptions = {
allowGiphy: true,
allowGrouping: true,
allowReordering: true,
allowCancelVote: true,
blurCards: false,
newPostsFirst: true,
allowCancelVote: true,
allowTimer: true,
timerDuration: 15 * 60,
readonlyOnTimerEnd: true,
Expand All @@ -35,4 +35,5 @@ export const defaultSession: Omit<Session, 'createdBy'> = {
locked: false,
ready: [],
timer: null,
demo: false,
};
4 changes: 4 additions & 0 deletions frontend/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface Session extends PostContainer, Entity {
createdBy: User;
ready: string[];
timer: Date | null;
demo: boolean;
}

export interface SessionMetadata extends Entity {
Expand Down Expand Up @@ -218,6 +219,7 @@ export type TrackingEvent =
| 'register/password'
| 'register/oauth'
| 'register/anonymous'
| 'register/demo'
| 'subscribe/initial'
| 'subscribe/launch-stripe'
| 'subscribe/purchased'
Expand Down Expand Up @@ -255,5 +257,7 @@ export type StripeLocales =
| 'ja-JP'
| 'nl-NL'
| 'pt-BR'
| 'pt-PT'
| 'no-NO'
| 'uk-UA'
| 'sv-S';
1 change: 1 addition & 0 deletions frontend/src/testing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const initialSession: Session = {
},
ready: [],
timer: null,
demo: false,
};

export function AllTheProviders({ children }: PropsWithChildren<{}>) {
Expand Down
Loading

0 comments on commit 6857d55

Please sign in to comment.