Skip to content

Commit

Permalink
Merge branch 'develop' into wip/akirathan/11483-polyglot-lib
Browse files Browse the repository at this point in the history
  • Loading branch information
JaroslavTulach authored Dec 27, 2024
2 parents b4d3f98 + 0c41e8d commit 05197f5
Show file tree
Hide file tree
Showing 160 changed files with 3,727 additions and 1,730 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
previously allowed to use spaces in the argument definition without
parentheses. [This is now a syntax error.][11856]
- [Native libraries of projects can be added to `polyglot/lib` directory][11874]
- Symetric, transitive and reflexive [equality for intersection types][11897]

[11777]: https://github.com/enso-org/enso/pull/11777
[11600]: https://github.com/enso-org/enso/pull/11600
[11856]: https://github.com/enso-org/enso/pull/11856
[11874]: https://github.com/enso-org/enso/pull/11874
[11897]: https://github.com/enso-org/enso/pull/11897

# Next Release

Expand Down Expand Up @@ -136,6 +138,7 @@
- [Enhance Managed_Resource to allow implementation of in-memory caches][11577]
- [Added `add_group_number` to the in-memory database.[11818]
- [The reload button clears the HTTP cache.][11673]
- [SQL Server Support for Aggregate][11811]

[11235]: https://github.com/enso-org/enso/pull/11235
[11255]: https://github.com/enso-org/enso/pull/11255
Expand All @@ -146,6 +149,7 @@
[11577]: https://github.com/enso-org/enso/pull/11577
[11818]: https://github.com/enso-org/enso/pull/11818
[11673]: https://github.com/enso-org/enso/pull/11673
[11811]: https://github.com/enso-org/enso/pull/11811

#### Enso Language & Runtime

Expand Down
9 changes: 5 additions & 4 deletions app/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"./src/backendQuery": "./src/backendQuery.ts",
"./src/queryClient": "./src/queryClient.ts",
"./src/utilities/data/array": "./src/utilities/data/array.ts",
"./src/utilities/errors": "./src/utilities/errors.ts",
"./src/utilities/data/dateTime": "./src/utilities/data/dateTime.ts",
"./src/utilities/data/newtype": "./src/utilities/data/newtype.ts",
"./src/utilities/data/object": "./src/utilities/data/object.ts",
Expand All @@ -32,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
12 changes: 11 additions & 1 deletion app/common/src/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function createQueryClient<TStorageValue = string>(
storage: persisterStorage,
// Prefer online first and don't rely on the local cache if user is online
// fallback to the local cache only if the user is offline
maxAge: queryCore.onlineManager.isOnline() ? -1 : DEFAULT_QUERY_PERSIST_TIME_MS,
maxAge: DEFAULT_QUERY_PERSIST_TIME_MS,
buster: DEFAULT_BUSTER,
filters: { predicate: query => query.meta?.persist !== false },
prefix: 'enso:query-persist:',
Expand Down Expand Up @@ -130,8 +130,18 @@ export function createQueryClient<TStorageValue = string>(
defaultOptions: {
queries: {
...(persister != null ? { persister } : {}),
// Default set to 'always' to don't pause ongoing queries
// and make them fail.
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 @@ -970,9 +970,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 @@ -998,7 +996,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 @@ -1024,7 +1022,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 @@ -1514,18 +1512,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
17 changes: 14 additions & 3 deletions app/common/src/text/english.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"submit": "Submit",
"retry": "Retry",

"arbitraryFetchError": "An error occurred while fetching data",
"arbitraryFetchImageError": "An error occurred while fetching an image",

"createFolderError": "Could not create new folder",
"createProjectError": "Could not create new project",
"createDatalinkError": "Could not create new Datalink",
Expand All @@ -17,6 +21,9 @@
"deleteAssetError": "Could not delete '$0'",
"restoreAssetError": "Could not restore '$0'",

"refetchQueriesPending": "Getting latest updates...",
"refetchQueriesError": "Could not get latest updates. Some information may be outdated",

"localBackendDatalinkError": "Cannot create Datalinks on the local drive",
"localBackendSecretError": "Cannot create secrets on the local drive",
"offlineUploadFilesError": "Cannot upload files when offline",
Expand Down Expand Up @@ -52,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 Expand Up @@ -174,6 +184,7 @@
"getCustomerPortalUrlBackendError": "Could not get customer portal URL",
"duplicateLabelError": "This label already exists.",
"emptyStringError": "This value must not be empty.",
"resolveProjectAssetPathBackendError": "Could not get asset",

"directoryAssetType": "folder",
"directoryDoesNotExistError": "Unable to find directory. Does it exist?",
Expand Down Expand Up @@ -477,9 +488,9 @@
"hidePassword": "Hide password",
"showPassword": "Show password",
"copiedToClipboard": "Copied to clipboard",
"noResultsFound": "No results found.",
"youAreOffline": "You are offline.",
"cannotCreateAssetsHere": "You do not have the permissions to create assets here.",
"noResultsFound": "No results found",
"youAreOffline": "You are offline",
"cannotCreateAssetsHere": "You do not have the permissions to create assets here",
"enableVersionChecker": "Enable Version Checker",
"enableVersionCheckerDescription": "Show a dialog if the current version of the desktop app does not match the latest version.",
"disableAnimations": "Disable animations",
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}`),
)
}
52 changes: 52 additions & 0 deletions app/common/src/utilities/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* An error that occurs when a network request fails.
*
* This error is used to indicate that a network request failed due to a network error,
* such as a timeout or a connection error.
*/
export class NetworkError extends Error {
/**
* Create a new {@link NetworkError} with the specified message.
* @param message - The message to display when the error is thrown.
*/
constructor(message: string, options?: ErrorOptions) {
super(message, options)
this.name = 'NetworkError'
}
}

/**
* An error that occurs when the user is offline.
*
* This error is used to indicate that the user is offline, such as when they are
* not connected to the internet or when they are on an airplane.
*/
export class OfflineError extends Error {
/**
* Create a new {@link OfflineError} with the specified message.
* @param message - The message to display when the error is thrown.
*/
constructor(message: string = 'User is offline', options?: ErrorOptions) {
super(message, options)
this.name = 'OfflineError'
}
}

/**
* An error with a display message.
*
* This message can be shown to a user.
*/
export class ErrorWithDisplayMessage extends Error {
readonly displayMessage: string
/**
* Create a new {@link ErrorWithDisplayMessage} with the specified message and display message.
* @param message - The message to display when the error is thrown.
* @param options - The options to pass to the error.
*/
constructor(message: string, options: ErrorOptions & { displayMessage: string }) {
super(message, options)
this.name = 'ErrorWithDisplayMessage'
this.displayMessage = options.displayMessage
}
}
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 05197f5

Please sign in to comment.