Skip to content

Commit

Permalink
Merge branch 'develop' into wip/sergeigarin/fix-selection-in-assets-t…
Browse files Browse the repository at this point in the history
…able

# Conflicts:
#	app/gui/src/dashboard/components/dashboard/AssetRow.tsx
  • Loading branch information
MrFlashAccount committed Dec 25, 2024
2 parents 6aac8f2 + 0c41e8d commit bb5507f
Show file tree
Hide file tree
Showing 94 changed files with 2,336 additions and 1,177 deletions.
8 changes: 4 additions & 4 deletions app/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@
"lint": "eslint ./src --cache --max-warnings=0"
},
"peerDependencies": {
"@tanstack/query-core": "5.54.1",
"@tanstack/vue-query": ">= 5.54.0 < 5.56.0"
"@tanstack/query-core": "5.59.20",
"@tanstack/vue-query": "5.59.20"
},
"dependencies": {
"@tanstack/query-persist-client-core": "^5.54.0",
"@tanstack/vue-query": ">= 5.54.0 < 5.56.0",
"@tanstack/query-persist-client-core": "5.59.20",
"@tanstack/vue-query": "5.59.20",
"lib0": "^0.2.85",
"react": "^18.3.1",
"vitest": "^1.3.1",
Expand Down
7 changes: 7 additions & 0 deletions app/common/src/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ export function createQueryClient<TStorageValue = string>(
networkMode: 'always',
refetchOnReconnect: 'always',
staleTime: DEFAULT_QUERY_STALE_TIME_MS,
// This allows to prefetch queries in the render phase. Enables returning
// a promise from the `useQuery` hook, which is useful for the `Await` component,
// which needs to prefetch the query in the render phase to be able to display
// the error boundary/suspense fallback.
// @see [experimental_prefetchInRender](https://tanstack.com/query/latest/docs/framework/react/guides/suspense#using-usequerypromise-and-reactuse-experimental)
// eslint-disable-next-line camelcase
experimental_prefetchInRender: true,
retry: (failureCount, error: unknown) => {
const statusesToIgnore = [403, 404]
const errorStatus =
Expand Down
38 changes: 23 additions & 15 deletions app/common/src/services/Backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -971,9 +971,7 @@ export function createSpecialLoadingAsset(directoryId: DirectoryId): SpecialLoad
return {
type: AssetType.specialLoading,
title: '',
id: LoadingAssetId(
createPlaceholderId(`${AssetType.specialLoading}-${uniqueString.uniqueString()}`),
),
id: LoadingAssetId(createPlaceholderId(`${AssetType.specialLoading}-${directoryId}`)),
modifiedAt: dateTime.toRfc3339(new Date()),
parentId: directoryId,
permissions: [],
Expand All @@ -999,7 +997,7 @@ export function createSpecialEmptyAsset(directoryId: DirectoryId): SpecialEmptyA
return {
type: AssetType.specialEmpty,
title: '',
id: EmptyAssetId(`${AssetType.specialEmpty}-${uniqueString.uniqueString()}`),
id: EmptyAssetId(`${AssetType.specialEmpty}-${directoryId}`),
modifiedAt: dateTime.toRfc3339(new Date()),
parentId: directoryId,
permissions: [],
Expand All @@ -1025,7 +1023,7 @@ export function createSpecialErrorAsset(directoryId: DirectoryId): SpecialErrorA
return {
type: AssetType.specialError,
title: '',
id: ErrorAssetId(`${AssetType.specialError}-${uniqueString.uniqueString()}`),
id: ErrorAssetId(`${AssetType.specialError}-${directoryId}`),
modifiedAt: dateTime.toRfc3339(new Date()),
parentId: directoryId,
permissions: [],
Expand Down Expand Up @@ -1515,18 +1513,28 @@ export function isNewTitleValid(
item: AnyAsset,
newTitle: string,
siblings?: readonly AnyAsset[] | null,
) {
return newTitle !== '' && newTitle !== item.title && isNewTitleUnique(item, newTitle, siblings)
}

/**
* Check whether a new title is unique among the siblings.
*/
export function isNewTitleUnique(
item: AnyAsset,
newTitle: string,
siblings?: readonly AnyAsset[] | null,
) {
siblings ??= []
return (
newTitle !== '' &&
newTitle !== item.title &&
siblings.every(sibling => {
const isSelf = sibling.id === item.id
const hasSameType = sibling.type === item.type
const hasSameTitle = sibling.title === newTitle
return !(!isSelf && hasSameType && hasSameTitle)
})
)

return siblings.every(sibling => {
if (sibling.id === item.id) {
return true
}

const hasSameTitle = sibling.title.toLowerCase() === newTitle.toLowerCase()
return !hasSameTitle
})
}

/** Network error class. */
Expand Down
3 changes: 3 additions & 0 deletions app/common/src/text/english.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
"otherUserIsUsingProjectError": "Someone else is using this project",
"localBackendNotDetectedError": "Could not detect the local backend",

"invalidInput": "Invalid input",
"nameShouldBeUnique": "Name must be unique",
"nameShouldNotContainInvalidCharacters": "Name should not contain invalid characters",
"invalidEmailValidationError": "Please enter a valid email address",

"projectHasNoSourceFilesPhrase": "project has no source files",
Expand Down
52 changes: 52 additions & 0 deletions app/common/src/text/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** @file Functions related to displaying text. */

import { unsafeKeys } from '../utilities/data/object'
import ENGLISH from './english.json' with { type: 'json' }

// =============
Expand Down Expand Up @@ -159,3 +160,54 @@ export interface Replacements
export const TEXTS: Readonly<Record<Language, Texts>> = {
[Language.english]: ENGLISH,
}
/**
* A function that gets localized text for a given key, with optional replacements.
* @param key - The key of the text to get.
* @param replacements - The replacements to insert into the text.
* If the text contains placeholders like `$0`, `$1`, etc.,
* they will be replaced with the corresponding replacement.
*/
export type GetText = <K extends TextId>(
dictionary: Texts,
key: K,
...replacements: Replacements[K]
) => string

/**
* Resolves the language texts based on the user's preferred language.
*/
export function resolveUserLanguage() {
const locale = navigator.language
const language =
unsafeKeys(LANGUAGE_TO_LOCALE).find(language => locale === LANGUAGE_TO_LOCALE[language]) ??
Language.english

return language
}

/**
* Gets the dictionary for a given language.
* @param language - The language to get the dictionary for.
* @returns The dictionary for the given language.
*/
export function getDictionary(language: Language) {
return TEXTS[language]
}

/**
* Gets the text for a given key, with optional replacements.
* @param dictionary - The dictionary to get the text from.
* @param key - The key of the text to get.
* @param replacements - The replacements to insert into the text.
* If the text contains placeholders like `$0`, `$1`, etc.,
* they will be replaced with the corresponding replacement.
*/
export const getText: GetText = (dictionary, key, ...replacements) => {
const template = dictionary[key]

return replacements.length === 0 ?
template
: template.replace(/[$]([$]|\d+)/g, (_match, placeholder: string) =>
placeholder === '$' ? '$' : String(replacements[Number(placeholder)] ?? `$${placeholder}`),
)
}
7 changes: 7 additions & 0 deletions app/gui/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ declare global {
* ATM only affects the framer-motion animations.
*/
readonly DISABLE_ANIMATIONS?: boolean
readonly featureFlags: FeatureFlags
readonly setFeatureFlags: (flags: Partial<FeatureFlags>) => void
/**
* Feature flags that override the default or stored feature flags.
* This is used by integration tests to set feature flags.
*/
readonly overrideFeatureFlags: Partial<FeatureFlags>
}

namespace NodeJS {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ function locateAssetRows(page: Page) {

/** Find assets table placeholder rows. */
function locateNonAssetRows(page: Page) {
return locateAssetsTable(page).locator('tbody tr:not([data-testid="asset-row"])')
return locateAssetsTable(page).locator(
'tbody tr:not([data-testid="asset-row"]):not([data-testid="dummy-row"])',
)
}

/** Find a "new secret" icon. */
Expand Down
Loading

0 comments on commit bb5507f

Please sign in to comment.