Skip to content

Commit

Permalink
ranked wip
Browse files Browse the repository at this point in the history
  • Loading branch information
sspenst committed Nov 10, 2023
1 parent eab0e7a commit 29446d3
Show file tree
Hide file tree
Showing 14 changed files with 157 additions and 35 deletions.
7 changes: 7 additions & 0 deletions components/cards/selectCardContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export default function SelectCardContent({ option }: SelectCardContentProps) {
{!option.hideStats && option.stats && <div className='pt-1 italic'>{option.stats.getText()}</div>}
</div>
</div>
{option.level?.isRanked && <>
<div className='absolute top-0 left-0 text-yellow-500 p-1'>
<svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' strokeWidth={2} stroke='currentColor' className='w-5 h-5'>
<path strokeLinecap='round' strokeLinejoin='round' d='M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z' />
</svg>
</div>
</>}
{option.stats?.isSolved() &&
<div className='absolute top-0 right-0'>
<Solved />
Expand Down
4 changes: 4 additions & 0 deletions components/formatted/formattedUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ export default function FormattedUser({ className, id, noLinks, noTooltip, onCli
user={user}
/>
</div>
<div className='flex gap-1'>
<span className='font-medium'>Ranked Solves:</span>
<span className='gray'>{userExtendedData.user.calcRankedSolves}</span>
</div>
<div className='flex gap-1'>
<span className='font-medium'>Levels Solved:</span>
<span className='gray'>{userExtendedData.user.score}</span>
Expand Down
25 changes: 25 additions & 0 deletions components/level/info/formattedLevelInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { Tab } from '@headlessui/react';
import FormattedDate from '@root/components/formatted/formattedDate';
import Solved from '@root/components/level/info/solved';
import FormattedLevelReviews from '@root/components/level/reviews/formattedLevelReviews';
import StyledTooltip from '@root/components/page/styledTooltip';
import Image from 'next/image';
import Link from 'next/link';
import React, { useContext, useState } from 'react';
import Dimensions from '../../../constants/dimensions';
import { AppContext } from '../../../contexts/appContext';
Expand Down Expand Up @@ -84,6 +86,29 @@ export default function FormattedLevelInfo({ level }: FormattedLevelInfoProps) {
return (<>
<div className='flex flex-col gap-4'>
<div className='flex flex-col gap-1'>
{level.isRanked && <>
<Link
className='flex gap-1 pl-1 pr-1 py-0.5 items-center border-2 rounded-md w-fit border-yellow-500 bg-yellow-200 text-sm font-medium text-black'
data-tooltip-content='This level contributes to your leaderboard ranking!'
data-tooltip-id='ranked-tooltip'
href='/ranked'
>
<svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' strokeWidth={2.5} stroke='currentColor' className='w-4 h-4'>
<path strokeLinecap='round' strokeLinejoin='round' d='M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z' />
</svg>
{/* <svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' strokeWidth={2} stroke='currentColor' className='w-4 h-4'>
<path strokeLinecap='round' strokeLinejoin='round' d='M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z' />
</svg> */}

<span className='leading-none pt-px'>Ranked</span>
</Link>
<StyledTooltip id='ranked-tooltip' />
</>}
{/* {level.isRanked &&
<div className='px-1 border-2 rounded-md w-fit border-yellow-500 bg-yellow-200 text-sm font-medium text-black'>
Ranked
</div>
} */}
<div className='flex justify-between w-full items-start gap-2'>
<div className='font-bold text-2xl overflow-hidden break-words'>{level.name}</div>
<div className='mt-1'>
Expand Down
2 changes: 1 addition & 1 deletion components/tables/dataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';

export interface TableColumn<T> {
id: string;
name: React.ReactNode;
name?: React.ReactNode;
selector: (row: T) => JSX.Element;
sortable?: boolean;
style?: React.CSSProperties | undefined;
Expand Down
1 change: 1 addition & 0 deletions models/db/level.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface Level {
height: number;
isDeleted: boolean;
isDraft: boolean;
isRanked: boolean;
leastMoves: number;
name: string;
slug: string;
Expand Down
1 change: 1 addition & 0 deletions models/db/user.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface User {
_id: Types.ObjectId;
avatarUpdatedAt?: number;
bio?: string;
calcRankedSolves: number;
calc_levels_created_count: number;
calc_records: number;
chapterUnlocked?: number; // chapter unlocked in the campaign
Expand Down
6 changes: 5 additions & 1 deletion models/schemas/levelSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AttemptContext } from './playAttemptSchema';
export const LEVEL_DEFAULT_PROJECTION = { _id: 1, name: 1, slug: 1, width: 1, height: 1, data: 1, leastMoves: 1, calc_difficulty_estimate: 1, userId: 1, calc_playattempts_unique_users_count: { $size: '$calc_playattempts_unique_users' }, };

// adds ts,calc_reviews_score_laplace and users solved
export const LEVEL_SEARCH_DEFAULT_PROJECTION = { _id: 1, ts: 1, name: 1, slug: 1, /*width: 1, height: 1, data: 1,*/ leastMoves: 1, calc_difficulty_estimate: 1, userId: 1, calc_playattempts_unique_users_count: { $size: '$calc_playattempts_unique_users' }, calc_reviews_score_laplace: 1, calc_stats_players_beaten: 1, calc_reviews_count: 1 };
export const LEVEL_SEARCH_DEFAULT_PROJECTION = { _id: 1, ts: 1, name: 1, slug: 1, /*width: 1, height: 1, data: 1,*/ leastMoves: 1, calc_difficulty_estimate: 1, userId: 1, calc_playattempts_unique_users_count: { $size: '$calc_playattempts_unique_users' }, calc_reviews_score_laplace: 1, calc_stats_players_beaten: 1, calc_reviews_count: 1, isRanked: 1 };

const LevelSchema = new mongoose.Schema<Level>(
{
Expand Down Expand Up @@ -78,6 +78,10 @@ const LevelSchema = new mongoose.Schema<Level>(
type: Boolean,
required: true,
},
isRanked: {
type: Boolean,
required: true,
},
leastMoves: {
type: Number,
required: true,
Expand Down
6 changes: 6 additions & 0 deletions models/schemas/userSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const UserSchema = new mongoose.Schema<User>({
maxlength: 256,
select: false
},
calcRankedSolves: {
type: Number,
required: true,
default: 0,
},
calc_levels_created_count: {
type: Number,
default: 0,
Expand Down Expand Up @@ -101,6 +106,7 @@ const UserSchema = new mongoose.Schema<User>({
},
});

UserSchema.index({ calcRankedSolves: -1 });
UserSchema.index({ score: -1 });
UserSchema.index({ name: 1 }, { unique: true });
UserSchema.index({ email: 1 }, { unique: true });
Expand Down
1 change: 1 addition & 0 deletions pages/api/collection-by-id/[id].ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export async function getCollections({ matchQuery, reqUser, includeDraft, popula
{
$project: {
leastMoves: 1,
isRanked: 1,
...(includeDraft ? {
isDraft: 1
} : {}),
Expand Down
1 change: 1 addition & 0 deletions pages/api/level-by-slug/[username]/[slugName].ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export async function getLevelByUrlPath(username: string, slugName: string, reqU
authorNote: 1,
calc_difficulty_estimate: 1,
calc_playattempts_just_beaten_count: 1,
isRanked: 1,
ts: 1,
}
},
Expand Down
2 changes: 2 additions & 0 deletions pages/profile/[name]/[[...tab]]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,8 @@ export default function ProfilePage({
levelsSolvedByDifficulty ? <PlayerRank levelsSolvedByDifficulty={levelsSolvedByDifficulty} user={user} /> : '...'
}
</h2>
{/* TODO: h2?? */}
<h2><span className='font-bold'>Ranked Solves:</span> {user.calcRankedSolves}</h2>
<h2><span className='font-bold'>Levels Solved:</span> {user.score}</h2>
<h2><span className='font-bold'>Levels Created:</span> {user.calc_levels_created_count}</h2>
{!user.hideStatus && <>
Expand Down
76 changes: 43 additions & 33 deletions pages/search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -326,12 +326,54 @@ export default function Search({ enrichedLevels, reqUser, searchAuthor, searchQu
}, [loading, queryDebounce]);

const columns = [
{
id: 'name',
name: 'Name',
grow: 2,
selector: (row: EnrichedLevel) => (

<div className='flex items-center gap-3 truncate'>
<PlayLaterToggleButton id={row._id.toString()} level={row} />
<div className='text-yellow-500 w-5'>
{row.isRanked &&
<svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' strokeWidth={2} stroke='currentColor' className='w-5 h-5'>
<path strokeLinecap='round' strokeLinejoin='round' d='M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z' />
</svg>
}
</div>
<FormattedLevelLink onClick={() => {
const q = getParsedUrlQuery(query);
const queryString = Object.keys(q).map(key => key + '=' + query[key]).join('&');
const ts = new Date();

// TODO: temp collection is a hack (doesn't represent a real collection so there are other UX problems)
// should make a new collection class to be used on the level page (with an href property, isInMemory, etc.)
const collectionTemp = {
createdAt: ts,
isPrivate: true,
levels: data, _id: new Types.ObjectId(),
name: 'Search',
slug: `../search${queryString ? `?${queryString}` : ''}`,
type: CollectionType.InMemory,
updatedAt: ts,
userId: { _id: new Types.ObjectId() } as Types.ObjectId & User,
} as EnrichedCollection;

sessionStorage.setItem('tempCollection', JSON.stringify(collectionTemp));
setTempCollection(collectionTemp);
}} id='search' level={row} />
</div>
),
sortable: true,
style: {
minWidth: '150px',
},
},
{
id: 'userId',
name: 'Author',
selector: (row: EnrichedLevel) => (
<div className='flex gap-3 truncate'>
<PlayLaterToggleButton id={row._id.toString()} level={row} />
<button
onClick={() => {
if (query.searchAuthor === row.userId.name) {
Expand Down Expand Up @@ -362,38 +404,6 @@ export default function Search({ enrichedLevels, reqUser, searchAuthor, searchQu
minWidth: '150px',
},
},
{
id: 'name',
name: 'Name',
grow: 2,
selector: (row: EnrichedLevel) => {
return <FormattedLevelLink onClick={() => {
const q = getParsedUrlQuery(query);
const queryString = Object.keys(q).map(key => key + '=' + query[key]).join('&');
const ts = new Date();

// TODO: temp collection is a hack (doesn't represent a real collection so there are other UX problems)
// should make a new collection class to be used on the level page (with an href property, isInMemory, etc.)
const collectionTemp = {
createdAt: ts,
isPrivate: true,
levels: data, _id: new Types.ObjectId(),
name: 'Search',
slug: `../search${queryString ? `?${queryString}` : ''}`,
type: CollectionType.InMemory,
updatedAt: ts,
userId: { _id: new Types.ObjectId() } as Types.ObjectId & User,
} as EnrichedCollection;

sessionStorage.setItem('tempCollection', JSON.stringify(collectionTemp));
setTempCollection(collectionTemp);
}} id='search' level={row} />;
},
sortable: true,
style: {
minWidth: '150px',
},
},
{
id: 'calcDifficultyEstimate',
name: 'Difficulty',
Expand Down
7 changes: 7 additions & 0 deletions pages/users/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,13 @@ export default function PlayersPage({ searchQuery, totalRows, users }: PlayersPr
selector: row => <FormattedUser id='users' size={Dimensions.AvatarSizeSmall} user={row} />,
sortable: true,
},
// TODO: default sort by this property
{
id: 'calcRankedSolves',
name: 'Ranked Solves',
selector: row => row.calcRankedSolves,
sortable: true,
},
{
id: 'score',
name: 'Solves',
Expand Down
53 changes: 53 additions & 0 deletions server/scripts/set-is-ranked.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// ts-node -r tsconfig-paths/register --files server/scripts/set-is-ranked.ts

import Level from '@root/models/db/level';
import { LevelModel } from '@root/models/mongoose';
import cliProgress from 'cli-progress';
import dotenv from 'dotenv';
import * as fs from 'fs';
import dbConnect from '../../lib/dbConnect';

'use strict';

dotenv.config();
console.log('loaded env vars');
const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);

async function setIsRanked() {
const filePath = './server/scripts/ranked_level_ids';

fs.readFile(filePath, 'utf8', async (err: NodeJS.ErrnoException | null, data: string) => {
if (err) {
console.error('Error reading the file:', err);

process.exit(0);
}

const rankedLevelIds = new Set(data.split('\n'));

const totalLevels = await LevelModel.countDocuments();

progressBar.start(totalLevels, 0);

for await (const level of LevelModel.find<Level>()) {
const isRanked = rankedLevelIds.has(level._id.toString());

await LevelModel.updateOne({ _id: level._id }, { $set: { isRanked } });
progressBar.increment();
}

progressBar.stop();

process.exit(0);
});
}

async function init() {
console.log('connecting to db...');
await dbConnect();
console.log('connected');

setIsRanked();
}

init();

0 comments on commit 29446d3

Please sign in to comment.