Stage 17 (contour round 4a): quick fixes
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 12s
CI / ui (pull_request) Successful in 27s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 12s
CI / ui (pull_request) Successful in 27s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
- #4 bag label: '{n} in the bag' / 'Bag is empty' (was 'Bag {n}') - #6 allow a single trailing dot in display names (backend + UI regex + tests) - #1 double-tap zooms toward the tapped cell, not the top-left - #8 shuffle fires a short multi-pulse haptic - #11 highlighted/flashing tiles darken their bottom edge too (shadow joins the flash) - #13 toast slides up from the bottom and fades out - #7 hide the logout button (kept wired behind `hidden`) - #16 admin game seats: left-align numeric columns, clarify the 'Hints used' header
This commit is contained in:
@@ -9,8 +9,8 @@ describe('i18n catalog', () => {
|
||||
});
|
||||
|
||||
it('interpolates parameters', () => {
|
||||
expect(translate('en', 'game.bag', { n: 7 })).toBe('Bag 7');
|
||||
expect(translate('ru', 'game.bag', { n: 7 })).toBe('Мешок 7');
|
||||
expect(translate('en', 'game.bag', { n: 7 })).toBe('7 in the bag');
|
||||
expect(translate('ru', 'game.bag', { n: 7 })).toBe('7 в мешке');
|
||||
});
|
||||
|
||||
it('maps error codes to keys with a generic fallback', () => {
|
||||
|
||||
@@ -46,7 +46,8 @@ export const en = {
|
||||
'new.find': 'Find a game',
|
||||
'new.searching': 'Looking for an opponent…',
|
||||
|
||||
'game.bag': 'Bag {n}',
|
||||
'game.bag': '{n} in the bag',
|
||||
'game.bagEmpty': 'Bag is empty',
|
||||
'game.hints': 'Hints {n}',
|
||||
'game.yourTurn': 'Your turn',
|
||||
'game.waiting': "Waiting for {name}",
|
||||
|
||||
@@ -47,7 +47,8 @@ export const ru: Record<MessageKey, string> = {
|
||||
'new.find': 'Найти игру',
|
||||
'new.searching': 'Ищем соперника…',
|
||||
|
||||
'game.bag': 'Мешок {n}',
|
||||
'game.bag': '{n} в мешке',
|
||||
'game.bagEmpty': 'Мешок пуст',
|
||||
'game.hints': 'Подсказки {n}',
|
||||
'game.yourTurn': 'Ваш ход',
|
||||
'game.waiting': 'Ожидаем {name}',
|
||||
|
||||
@@ -12,7 +12,10 @@ describe('validDisplayName', () => {
|
||||
['Name P._Last', false],
|
||||
['Name Last', false],
|
||||
['_Name', false],
|
||||
['Name.', false],
|
||||
['Anna B.', true],
|
||||
['Name.', true],
|
||||
['Name..', false],
|
||||
['Name_', false],
|
||||
['Name2', false],
|
||||
['', false],
|
||||
['a'.repeat(33), false],
|
||||
|
||||
@@ -9,9 +9,10 @@ export const maxDisplayName = 32;
|
||||
export const maxAwayMinutes = 12 * 60;
|
||||
|
||||
// Unicode letters joined by single space / "." / "_" separators, where a "." or "_"
|
||||
// may be followed by a single space. No leading/trailing separator and no adjacent
|
||||
// separators except "<dot|underscore> <space>". Same rule as the Go displayNameRe.
|
||||
const displayNameRe = /^\p{L}+(?:(?:[._] ?| )\p{L}+)*$/u;
|
||||
// may be followed by a single space. No leading separator and no adjacent separators
|
||||
// except "<dot|underscore> <space>"; a single trailing "." is allowed (Stage 17). Same
|
||||
// rule as the Go displayNameRe.
|
||||
const displayNameRe = /^\p{L}+(?:(?:[._] ?| )\p{L}+)*\.?$/u;
|
||||
|
||||
/** displayNameError returns true when the trimmed name is a valid display name. */
|
||||
export function validDisplayName(raw: string): boolean {
|
||||
|
||||
Reference in New Issue
Block a user