Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/kavigupta/urbanstats into…
Browse files Browse the repository at this point in the history
… fix-navigation-flake
  • Loading branch information
lukebrody committed Feb 1, 2025
2 parents 218c8c3 + 069c13e commit 1bb81cf
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 10 deletions.
1 change: 1 addition & 0 deletions react/src/page_template/settings-vector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ type NotIncludedInSettingsVector = (
| StatCategorySavedIndeterminateKey
| StatCategoryExpandedKey
| 'theme' | 'colorblind_mode' | 'clean_background'
| 'juxtastatCompactEmoji'
)

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- No deprecations yet
Expand Down
2 changes: 2 additions & 0 deletions react/src/page_template/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export type SettingsDictionary = {
clean_background: boolean
temperature_unit: TemperatureUnit
mobile_article_pointers: MobileArticlePointers
juxtastatCompactEmoji: boolean
}
/* eslint-enable no-restricted-syntax */
& { [G in GroupIdentifier as StatGroupKey<G>]: boolean }
Expand Down Expand Up @@ -95,6 +96,7 @@ export const defaultSettingsList = [
...statPathsWithExtra.map(statPath => [`expanded__${statPath}`, false] as const),
['temperature_unit', 'fahrenheit'],
['mobile_article_pointers', 'pointer_in_class'],
['juxtastatCompactEmoji', false],
] as const

// Having a default settings object allows us to statically check that we have default values for all settings
Expand Down
105 changes: 97 additions & 8 deletions react/src/quiz/quiz-result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import React, { CSSProperties, ReactNode, useContext, useEffect, useRef, useStat
import { isFirefox, isMobile } from 'react-device-detect'

import { JuxtastatInfiniteButton } from '../components/quiz-panel'
import { CheckboxSetting } from '../components/sidebar'
import { Statistic } from '../components/table'
import { Navigator } from '../navigation/Navigator'
import { JuxtastatColors } from '../page_template/color-themes'
import { useColors, useJuxtastatColors } from '../page_template/colors'
import { Settings } from '../page_template/settings'
import { Settings, useSetting } from '../page_template/settings'
import { getVector, VectorSettingsDictionary } from '../page_template/settings-vector'
import { allGroups, allYears, statParents, StatPath } from '../page_template/statistic-tree'

Expand Down Expand Up @@ -185,7 +186,28 @@ interface ShareButtonProps {
medal?: Medal
}

function ShareButton({ buttonRef, todayName, correctPattern, quizKind, medal }: ShareButtonProps): ReactNode {
function ShareButton(props: ShareButtonProps): ReactNode {
const [compactRepr] = useSetting('juxtastatCompactEmoji')
const asb = <ActualShareButton {...props} compactRepr={compactRepr} />
if (props.correctPattern.length <= maxPerLine) {
return asb
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<CheckboxSetting
name="Compact emoji representation"
settingKey="juxtastatCompactEmoji"
testId="juxtastatCompactEmoji"
/>
</div>
<div className="gap_small"></div>
{asb}
</div>
)
}

function ActualShareButton({ buttonRef, todayName, correctPattern, quizKind, medal, compactRepr }: (ShareButtonProps & { compactRepr: boolean })): ReactNode {
const colors = useColors()
const juxtaColors = useJuxtastatColors()
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- We need to check the condition for browser compatibility.
Expand All @@ -198,7 +220,7 @@ function ShareButton({ buttonRef, todayName, correctPattern, quizKind, medal }:
style={buttonStyle(colors.hueColors.green)}
ref={buttonRef}
onClick={async () => {
const [text, url] = await summary(juxtaColors, todayName, correctPattern, quizKind, medal)
const [text, url] = await summary(juxtaColors, todayName, correctPattern, quizKind, medal, compactRepr)

async function copyToClipboard(): Promise<void> {
await navigator.clipboard.writeText(`${text}\n${url}`)
Expand Down Expand Up @@ -372,6 +394,7 @@ function summaryTexts(correctPattern: CorrectPattern, quizKind: QuizKind): [stri
}

export function Summary(props: { correctPattern: CorrectPattern, quizKind: QuizKind }): ReactNode {
const [compactRepr] = useSetting('juxtastatCompactEmoji')
const juxtaColors = useJuxtastatColors()
const [prefix, summaryText] = summaryTexts(props.correctPattern, props.quizKind)
const show = `${prefix} ${summaryText}`
Expand All @@ -380,7 +403,7 @@ export function Summary(props: { correctPattern: CorrectPattern, quizKind: QuizK
<span className="serif quiz_summary" id="quiz-result-summary-words">{show}</span>
<div id="quiz-result-summary-emoji">
{
redAndGreenSquares(juxtaColors, props.correctPattern).map((line, index) => (
redAndGreenSquares(juxtaColors, props.correctPattern, compactRepr).map((line, index) => (
<div className="serif quiz_summary" key={index}>{line}</div>
))
}
Expand All @@ -393,7 +416,7 @@ function renderMedalAsString(medal: Medal): string {
return ['🥇 Personal Best!', '🥈 Personal 2nd Best!', '🥉 Personal 3rd Best!'][medal - 1]
}

export async function summary(juxtaColors: JuxtastatColors, todayName: string | undefined, correctPattern: CorrectPattern, quizKind: QuizKind, medal: Medal | undefined): Promise<[string, string]> {
export async function summary(juxtaColors: JuxtastatColors, todayName: string | undefined, correctPattern: CorrectPattern, quizKind: QuizKind, medal: Medal | undefined, compactRepr: boolean): Promise<[string, string]> {
// wordle-style summary
const [, summaryText] = summaryTexts(correctPattern, quizKind)
let text = nameOfQuizKind(quizKind)
Expand All @@ -405,7 +428,7 @@ export async function summary(juxtaColors: JuxtastatColors, todayName: string |
text += '\n'
text += '\n'

text += redAndGreenSquares(juxtaColors, correctPattern).join('\n')
text += redAndGreenSquares(juxtaColors, correctPattern, compactRepr).join('\n')

if (medal !== undefined) {
text += '\n'
Expand Down Expand Up @@ -625,11 +648,77 @@ function settingsOverrides(questionStatPath?: StatPath): Partial<VectorSettingsD
])
}

export function redAndGreenSquares(juxtaColors: JuxtastatColors, correctPattern: CorrectPattern): string[] {
function emojiForCount(count: number): string[] {
if (count >= 10) {
return [...emojiForCount(count / 10), ...emojiForCount(count % 10)]
}
switch (count) {
case 0:
return ['0️⃣']
case 1:
return ['1️⃣']
case 2:
return ['2️⃣']
case 3:
return ['3️⃣']
case 4:
return ['4️⃣']
case 5:
return ['5️⃣']
case 6:
return ['6️⃣']
case 7:
return ['7️⃣']
case 8:
return ['8️⃣']
case 9:
return ['9️⃣']
}
throw new Error(`unexpected count ${count}`)
}

function toCompactRepresentation(correctPattern: CorrectPattern, correct: string, incorrect: string): string[] {
// RRGGG -> R<emoji 2>G<emoji 3>
const result: string[][] = [[]]
let currentSymbol: boolean | undefined = undefined
let currentCount = 0

const add = (symbol: boolean | undefined, count: number): void => {
if (symbol === undefined) {
return
}
const last = result[result.length - 1]
last.push(symbol ? correct : incorrect)
if (count > 1) {
last.push(...emojiForCount(count))
}
if (last.length >= maxPerLine) {
result.push([])
}
}

for (const x of correctPattern.map(t => t ? true : false)) {
if (x === currentSymbol) {
currentCount += 1
}
else {
add(currentSymbol, currentCount)
currentSymbol = x
currentCount = 1
}
}
add(currentSymbol, currentCount)
return result.map(line => line.join(''))
}

export function redAndGreenSquares(juxtaColors: JuxtastatColors, correctPattern: CorrectPattern, compactRepr: boolean): string[] {
if (compactRepr) {
return toCompactRepresentation(correctPattern, juxtaColors.correctEmoji, juxtaColors.incorrectEmoji)
}
if (correctPattern.length > maxPerLine) {
const lines = []
for (let i = 0; i < correctPattern.length; i += maxPerLine) {
lines.push(redAndGreenSquares(juxtaColors, correctPattern.slice(i, i + maxPerLine))[0])
lines.push(redAndGreenSquares(juxtaColors, correctPattern.slice(i, i + maxPerLine), compactRepr)[0])
}
return lines
}
Expand Down
2 changes: 1 addition & 1 deletion react/test/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ RUN pip3 cache purge
RUN dpkg --add-architecture i386
RUN apt-get -y update

RUN apt-get -y install chromium=130.*
RUN apt-get -y install chromium=132.*
RUN apt-get -y install xvfb

RUN apt-get -y install sqlite3
Expand Down
18 changes: 17 additions & 1 deletion react/test/quiz_infinite_test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Selector } from 'testcafe'

import { runQuery } from './quiz_test_template'
import { collectCorrectJuxtaInfiniteAnswersFixture, friendsText, provideAnswers, quizFixture, withMockedClipboard } from './quiz_test_utils'
import { collectCorrectJuxtaInfiniteAnswersFixture, friendsText, provideAnswers, quizFixture, quizScreencap, withMockedClipboard } from './quiz_test_utils'
import {
target,
safeReload,
Expand Down Expand Up @@ -149,6 +149,22 @@ test('19-correct', async (t) => {
])
// low bit order first: 1111,1111 1111,1111 1111,0000 000[0,0000] This becomes FF FF 0F 00
await t.expect(await juxtastatInfiniteTable()).eql(`7|${seedStr}|FFFF0F00|20|27\n`)
await quizScreencap(t)
await t.click(Selector('[data-test-id=juxtastatCompactEmoji]'))
await quizScreencap(t)
await safeReload(t) // Copied! -> Copy Link
await t.expect(await Selector('#quiz-result-summary-emoji').innerText).eql(
'🟩2️⃣0️⃣🟥7️⃣',
)
await t.expect(await copyLines(t)).eql([
'Juxtastat Infinite 20/∞',
'',
'🟩2️⃣0️⃣🟥7️⃣',
'',
'🥇 Personal Best!',
'',
`https://juxtastat.org/${param}`,
])
})

async function doNotReportPartial(t: TestController): Promise<void> {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 1bb81cf

Please sign in to comment.