Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notification tray for displaying in-flight requests #12078

Open
wants to merge 91 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
8f98f4b
WIP
somebody1234 Jan 6, 2025
831341a
WIP: Initial implementation of transient notifications and notificati…
somebody1234 Jan 6, 2025
bf3dcae
WIP: Fix bugs
somebody1234 Jan 6, 2025
ac5a637
WIP
somebody1234 Jan 9, 2025
c4adc48
WIP: Debugging
somebody1234 Jan 10, 2025
1abd286
Remove unused dependency
somebody1234 Jan 10, 2025
5ea4c12
Add support for colors in `NotificationItem`
somebody1234 Jan 10, 2025
2c0fd30
help
somebody1234 Jan 13, 2025
4670466
WIP: Fix RAC error
somebody1234 Jan 14, 2025
23f4ffb
Fix issue with `GridList` crashing entire app
somebody1234 Jan 14, 2025
fd48790
Remove testing notification
somebody1234 Jan 14, 2025
5a764d1
Remove inaccurate notification count as mutations are batched
somebody1234 Jan 14, 2025
be00166
Implement notification for file uploads progress
somebody1234 Jan 14, 2025
c7bce7f
Merge branch 'develop' into wip/sb/loader-for-requests
somebody1234 Jan 19, 2025
79e556e
Fix Bazel on NixOS
somebody1234 Jan 16, 2025
a54aa72
Fix Bazel build
somebody1234 Jan 19, 2025
0406785
Initial UI for file upload notification
somebody1234 Jan 19, 2025
3311cd5
WIP: Make computed notifications permanent
somebody1234 Feb 3, 2025
12198bd
Merge branch 'develop' into wip/sb/loader-for-requests
somebody1234 Feb 3, 2025
1f6ad21
Make notifications persistent
somebody1234 Feb 3, 2025
97516df
Fix user bar styling
somebody1234 Feb 4, 2025
9ebacc6
Omit special assets from being selected
somebody1234 Feb 4, 2025
951abc7
Fix notification display
somebody1234 Feb 4, 2025
d37123b
Add timestamp to notifications
somebody1234 Feb 4, 2025
f75f803
Show timestamp in notification entry
somebody1234 Feb 4, 2025
127473a
Add button to Devtools to clear TanStack query cache and reload
somebody1234 Feb 4, 2025
d112658
Show red indicator on unread notifications; fix notification display;…
somebody1234 Feb 4, 2025
e13a547
pnpm i
somebody1234 Feb 4, 2025
b426bc7
Fix errors
somebody1234 Feb 4, 2025
b063193
Fix infinite loop when uploading file
somebody1234 Feb 5, 2025
e3951d9
Support indeterminate progress state for notifications
somebody1234 Feb 5, 2025
a775770
Disable dropzone in Trash and Recent categories
somebody1234 Feb 5, 2025
e5a207c
WIP: Fix "confirm delete" modal
somebody1234 Feb 5, 2025
161685f
Actually stop "confirm delete" modal from closing early when deleting…
somebody1234 Feb 5, 2025
08d3a2e
Add close button to notifications
somebody1234 Feb 6, 2025
14f4490
Switch `NotificationItem` to use `tailwind-variants`
somebody1234 Feb 6, 2025
8e11365
Use icon instead of button for notification icon
somebody1234 Feb 7, 2025
74f5c10
WIP: Show toast for some notifications
somebody1234 Feb 7, 2025
e4410b5
Fix crash
somebody1234 Feb 7, 2025
d99695b
Fix notification toasts
somebody1234 Feb 7, 2025
9a23d58
Remove `useUploadFileWithToastMutation` because notifications now upd…
somebody1234 Feb 7, 2025
25aa2f3
Distinguish hard delete and soft delete in notifications
somebody1234 Feb 7, 2025
a9eeba0
Fix detection for whether a notification is finished
somebody1234 Feb 7, 2025
d6d7718
Fix notification toast display and autoclose
somebody1234 Feb 7, 2025
a458cac
Fix layout of close button for notifications
somebody1234 Feb 7, 2025
b23f600
`ProgressBar` component
somebody1234 Feb 7, 2025
747dfc5
UI for indeterminate progress bar state
somebody1234 Feb 7, 2025
e09557e
Merge branch 'develop' into wip/sb/loader-for-requests
somebody1234 Feb 7, 2025
f7f4f41
Only show notifications for uploads by default
somebody1234 Feb 10, 2025
75116a3
Use central icon for inbox
somebody1234 Feb 10, 2025
1a3b488
Remove `color` prop from `Button` component
somebody1234 Feb 10, 2025
f2765a0
Merge branch 'develop' into wip/sb/loader-for-requests
somebody1234 Feb 17, 2025
1fc2be3
Clean up finished computed notification 1 minute after action finishes
somebody1234 Feb 17, 2025
cfa1fe4
Add and use `StatusBadge` component
somebody1234 Feb 17, 2025
df64295
Add stories for `ProgressBar`
somebody1234 Feb 17, 2025
22e967d
Add stories for `StatusBadge`
somebody1234 Feb 17, 2025
58d7a2b
Merge branch 'develop' into wip/sb/loader-for-requests
somebody1234 Feb 19, 2025
73e54bd
Merge branch 'develop' into wip/sb/loader-for-requests
somebody1234 Mar 3, 2025
99e1040
[ts] Fix type errors
somebody1234 Mar 3, 2025
0ad29b2
[ts] Fix lint errors
somebody1234 Mar 3, 2025
ffbd63d
[dashboard] Fix integration tests?
somebody1234 Mar 3, 2025
258f83d
Merge branch 'develop' into wip/sb/loader-for-requests
somebody1234 Mar 4, 2025
30553f0
Merge branch 'develop' into wip/sb/loader-for-requests
somebody1234 Mar 5, 2025
b78fb5f
[dashboard/DashboardTabBar] Fix background opacity
somebody1234 Mar 5, 2025
45bbfef
[dashboard/NotificationTray] Move to new folder
somebody1234 Mar 5, 2025
c2d82f8
[dashboard/computedNotificationHooks] Address CR
somebody1234 Mar 5, 2025
1d62760
[dashboard] Use `variants` prop instead of `_STYLES` constants directly
somebody1234 Mar 5, 2025
df79b1e
[dashboard/ProgressBar] Improve storybook
somebody1234 Mar 6, 2025
046352d
[dashboard/NotificationItem] Add storybook
somebody1234 Mar 6, 2025
eab2aa3
[dashboard/NotificationItem] Improve storybook
somebody1234 Mar 6, 2025
9b87099
[dashboard/Icon] Improve storybook
somebody1234 Mar 6, 2025
0964796
[dashboard/Breadcrumbs] Rename storybook stories
somebody1234 Mar 6, 2025
1005cec
[dashboard/Text] Improve storybook
somebody1234 Mar 6, 2025
7d06765
[dashboard/Text] Add `StoryVariants` component
somebody1234 Mar 7, 2025
4dcc6ab
[dashboard/ProgressBar] Use `StoryVariants`
somebody1234 Mar 7, 2025
cf1c192
[dashboard/Icon] Use `StoryVariants`
somebody1234 Mar 7, 2025
cc37b11
[dashboard/NotificationItem] Improve type safety in stories
somebody1234 Mar 7, 2025
8522ff3
[dashboard/Button] Fix type error in stories
somebody1234 Mar 7, 2025
00acf20
[dashboard/Check] Use `StoryVariants`
somebody1234 Mar 7, 2025
23e4e3f
[dashboard/Button] Use `StoryVariants`
somebody1234 Mar 7, 2025
7bfd64d
[dashboard/StatusBadge] Use `StoryVariants`
somebody1234 Mar 7, 2025
b668fa2
[dashboard/ComboBox] Use `StoryVariants`
somebody1234 Mar 7, 2025
5b203d3
[dashboard/DatePicker] Use `StoryVariants`
somebody1234 Mar 7, 2025
755621e
[dashboard/Selector] Use `StoryVariants`
somebody1234 Mar 7, 2025
6fa42fb
[dashboard/computedNotificationHooks] Fix type error
somebody1234 Mar 7, 2025
2c97e30
[dashboard/Category] Add `canUploadHere` field for drag and drop
somebody1234 Mar 7, 2025
bf3e46e
[dashboard/NotificationItem] Fix type error in stories
somebody1234 Mar 7, 2025
89c2207
[dashboard/Button] Prettier
somebody1234 Mar 7, 2025
c19a7c0
[dashboard/LocalStorage] Fix type errors
somebody1234 Mar 7, 2025
3bc14b5
[dashboard/LocalStorage] Fix type errors
somebody1234 Mar 7, 2025
17e4c94
[dashboard/LocalStorage] will this make lints pass?
somebody1234 Mar 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/common/src/services/Backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,11 @@ export function isErrorAssetId(id: string): id is ErrorAssetId {
return id.startsWith(`${AssetType.specialError}-`)
}

/** Whether a given {@link string} is a special frontend-only asset id. */
export function isSpecialAssetId(id: string) {
return isLoadingAssetId(id) || isEmptyAssetId(id) || isErrorAssetId(id)
}

/** Any object with a `type` field matching the given `AssetType`. */
interface HasType<Type extends AssetType> {
readonly type: Type
Expand Down
46 changes: 29 additions & 17 deletions app/common/src/text.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/** @file Functions related to displaying text. */

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

Expand Down Expand Up @@ -53,10 +52,6 @@ interface PlaceholderOverrides {
readonly youAreAlreadyAddingUser: [userEmail: string]
readonly lastModifiedOn: [dateString: string]
readonly versionX: [version: number | string]
readonly buildX: [build: string]
readonly electronVersionX: [electronVersion: string]
readonly chromeVersionX: [chromeVersion: string]
readonly userAgentX: [userAgent: string]
readonly compareVersionXWithLatest: [versionNumber: number]
readonly projectSessionX: [count: number]
readonly onDateX: [dateString: string]
Expand Down Expand Up @@ -134,7 +129,6 @@ interface PlaceholderOverrides {

readonly upgradeCTA: [plan: string]
readonly priceTemplate: [price: string, interval: string]
readonly months: [months: number]
readonly teamPlanSeatsDescription: [seats: number]
readonly tryFree: [days: number]
readonly organizationNameSettingsInputDescription: [howLong: number]
Expand All @@ -143,14 +137,33 @@ interface PlaceholderOverrides {
readonly xMinutes: [minutes: number]
readonly xAm: [hour: string]
readonly xPm: [hour: string]
readonly everyHourXMinute: [minute: string]
readonly groupNameSettingsInputDescription: [howLong: number]
readonly xIsUsingTheProject: [userName: string]
readonly xItemsCopied: [count: number]
readonly xItemsCut: [count: number]
readonly ordinalFallback: [number: number]
readonly deletingXAssetsNotification: [count: number]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove it

readonly permanentlyDeletingXAssetsNotification: [count: number]
readonly restoringXAssetsNotification: [count: number]
readonly copyingXAssetsNotification: [count: number]
readonly movingXAssetsNotification: [count: number]
readonly deletedXAssetsNotification: [count: number]
readonly permanentlyDeletedXAssetsNotification: [count: number]
readonly restoredXAssetsNotification: [count: number]
readonly copiedXAssetsNotification: [count: number]
readonly movedXAssetsNotification: [count: number]
readonly couldNotDeleteXAssetsNotification: [count: number]
readonly couldNotPermanentlyDeleteXAssetsNotification: [count: number]
readonly couldNotRestoreXAssetsNotification: [count: number]
readonly couldNotCopyXAssetsNotification: [count: number]
readonly couldNotMoveXAssetsNotification: [count: number]
readonly uploadedXFilesNotification: [count: number]
readonly uploadingXFilesWithProgressNotification: [
sentFiles: number,
totalFiles: number,
progressMb: string,
totalMb: string,
]
readonly dateXTimeX: [date: string, time: string]
readonly hourlyBetweenX: [startTime: string, endTime: string]
readonly projectSessionsOnX: [date: string]
readonly monthlyXthDay: [dateOrdinal: string]
readonly monthlyXthXDay: [weekOrdinal: string, dayOfWeek: string]
Expand All @@ -163,20 +176,21 @@ interface PlaceholderOverrides {

readonly arbitraryFieldTooLarge: [maxSize: string]
readonly arbitraryFieldTooSmall: [minSize: string]
readonly uploadLargeFileStatus: [uploadedParts: number, totalParts: number]
readonly uploadLargeFileStatus: [uploadedMb: string, totalMb: string]

readonly latestVersion: [version: string, date: string]
}

// This is intentionally unused. This line throws an error if `PlaceholderOverrides` ever becomes
// out of sync with `TextId`.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type SanityCheck<T extends TextId = keyof PlaceholderOverrides> = T

/** An tuple of `string` for placeholders for each {@link TextId}. */
export interface Replacements
extends PlaceholderOverrides,
Record<Exclude<TextId, keyof PlaceholderOverrides>, []> {}

// =================
// === Constants ===
// =================

export const TEXTS: Readonly<Record<Language, Texts>> = {
[Language.english]: ENGLISH,
}
Expand All @@ -193,9 +207,7 @@ export type GetText = <K extends TextId>(
...replacements: Replacements[K]
) => string

/**
* Resolves the language texts based on the user's preferred language.
*/
/** Resolves the language texts based on the user's preferred language. */
export function resolveUserLanguage() {
const locale = navigator.language
const language =
Expand Down
21 changes: 21 additions & 0 deletions app/common/src/text/english.json
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,24 @@
"specialEmptyAssetType": "special empty asset",
"specialErrorAssetType": "special error asset",

"deletingXAssetsNotification": "Deleting $0 asset(s)...",
"permanentlyDeletingXAssetsNotification": "Permanently deleting $0 asset(s)...",
"restoringXAssetsNotification": "Restoring $0 asset(s)...",
"copyingXAssetsNotification": "Copying $0 asset(s)...",
"movingXAssetsNotification": "Moving $0 asset(s)...",
"deletedXAssetsNotification": "Deleted $0 asset(s).",
"permanentlyDeletedXAssetsNotification": "Permanently deleted $0 asset(s).",
"restoredXAssetsNotification": "Restored $0 asset(s).",
"copiedXAssetsNotification": "Copied $0 asset(s).",
"movedXAssetsNotification": "Moved $0 asset(s).",
"couldNotDeleteXAssetsNotification": "Could not delete $0 asset(s).",
"couldNotPermanentlyDeleteXAssetsNotification": "Could not permanently delete $0 asset(s).",
"couldNotRestoreXAssetsNotification": "Could not restore $0 asset(s).",
"couldNotCopyXAssetsNotification": "Could not copy $0 asset(s).",
"couldNotMoveXAssetsNotification": "Could not move $0 asset(s).",
"uploadingXFilesWithProgressNotification": "Uploading $0/$1 file(s)... ($2/$3MB)",
"uploadedXFilesNotification": "Uploaded $0 file(s).",

"couldNotConnectToPM": "Could not connect to the Project Manager. Please try restarting Enso, or manually launching the Project Manager.",
"upgradeToUseCloud": "Upgrade your plan to use Enso Cloud.",

Expand Down Expand Up @@ -304,6 +322,7 @@
"forward": "Forward",
"more": "More",
"close": "Close",
"notifications": "Notifications",

"enterSecretPath": "Enter secret path",
"enterText": "Enter text",
Expand Down Expand Up @@ -530,6 +549,8 @@
"uploadLargeFileError": "Could not upload file",
"closeWindowDialogTitle": "Close window?",
"anUploadIsInProgress": "An upload is in progress.",
"youAreAllCaughtUp": "You're all caught up!",
"clearCacheAndReload": "Clear cache and reload",

"deleteLabelActionText": "delete the label '$0'",
"deleteSelectedAssetActionText": "delete '$0'",
Expand Down
89 changes: 49 additions & 40 deletions app/gui/integration-test/dashboard/actions/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ export interface MockParams {
readonly setupAPI?: SetupAPI | null | undefined
}

/** The type for the search query for the "list directory" endpoint. */
interface ListDirectoryQuery {
readonly parent_id?: string
readonly filter_by?: backend.FilterBy
readonly labels?: backend.LabelName[]
readonly recent_projects?: boolean
}

/**
* Setup function for the mock API.
* use it to setup the mock API with custom handlers.
Expand Down Expand Up @@ -285,6 +293,43 @@ async function mockApiInternal({ page, setupAPI }: MockParams) {
}
}

function listDirectory(query: ListDirectoryQuery) {
called('listDirectory', query)
const parentId = query.parent_id ?? defaultDirectoryId
let filteredAssets = assets.filter((asset) => asset.parentId === parentId)

switch (query.filter_by) {
case backend.FilterBy.active: {
filteredAssets = filteredAssets.filter((asset) => !deletedAssets.has(asset.id))
break
}
case backend.FilterBy.trashed: {
filteredAssets = assets.filter((asset) => deletedAssets.has(asset.id))
break
}
case backend.FilterBy.recent: {
filteredAssets = assets.filter((asset) => !deletedAssets.has(asset.id)).slice(0, 10)
break
}
case backend.FilterBy.all:
case null: {
// do nothing
break
}
case undefined: {
// do nothing
break
}
}
return filteredAssets.sort(
(a, b) => backend.ASSET_TYPE_ORDER[a.type] - backend.ASSET_TYPE_ORDER[b.type],
)
}

function listRootDirectory() {
return listDirectory({})
}

const addAsset = <T extends backend.AnyAsset>(asset: T) => {
assetMap.set(asset.id, asset)
assets = Array.from(assetMap.values())
Expand Down Expand Up @@ -780,49 +825,11 @@ async function mockApiInternal({ page, setupAPI }: MockParams) {
// === Endpoints returning arrays ===

await get(remoteBackendPaths.LIST_DIRECTORY_PATH + '*', (route, request) => {
/** The type for the search query for this endpoint. */
interface Query {
readonly parent_id?: string
readonly filter_by?: backend.FilterBy
readonly labels?: backend.LabelName[]
readonly recent_projects?: boolean
}
const query = Object.fromEntries(
new URL(request.url()).searchParams.entries(),
) as unknown as Query
) as unknown as ListDirectoryQuery
called('listDirectory', query)
const parentId = query.parent_id ?? defaultDirectoryId
let filteredAssets = assets.filter((asset) => asset.parentId === parentId)

// This lint rule is broken; there is clearly a case for `undefined` below.
switch (query.filter_by) {
case backend.FilterBy.active: {
filteredAssets = filteredAssets.filter((asset) => !deletedAssets.has(asset.id))
break
}
case backend.FilterBy.trashed: {
filteredAssets = assets.filter((asset) => deletedAssets.has(asset.id))
break
}
case backend.FilterBy.recent: {
filteredAssets = assets.filter((asset) => !deletedAssets.has(asset.id)).slice(0, 10)
break
}
case backend.FilterBy.all:
case null: {
// do nothing
break
}
case undefined: {
// do nothing
break
}
}
filteredAssets.sort(
(a, b) => backend.ASSET_TYPE_ORDER[a.type] - backend.ASSET_TYPE_ORDER[b.type],
)
const json: remoteBackend.ListDirectoryResponseBody = { assets: filteredAssets }

const json: remoteBackend.ListDirectoryResponseBody = { assets: listDirectory(query) }
route.fulfill({ json })
})
await get(remoteBackendPaths.LIST_FILES_PATH + '*', () => {
Expand Down Expand Up @@ -1463,6 +1470,8 @@ async function mockApiInternal({ page, setupAPI }: MockParams) {
currentOrganization = organization
},
currentOrganizationProfilePicture: () => currentOrganizationProfilePicture,
listDirectory,
listRootDirectory,
addAsset,
deleteAsset,
editAsset,
Expand Down
1 change: 0 additions & 1 deletion app/gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@
"@tanstack/react-query-devtools": "5.59.20",
"@testing-library/jest-dom": "6.6.3",
"@testing-library/react": "16.0.1",
"@testing-library/react-hooks": "8.0.1",
"@testing-library/user-event": "14.5.2",
"@tsconfig/node20": "^20.1.4",
"@types/css.escape": "^1.5.2",
Expand Down
4 changes: 0 additions & 4 deletions app/gui/src/dashboard/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,6 @@ import { CloudBrowserDisabledLayout } from '#/providers/AuthProvider'
import { useMutation } from '@tanstack/react-query'
import { useOffline } from './hooks/offlineHooks'

// ============================
// === Global configuration ===
// ============================

declare module '#/utilities/LocalStorage' {
/** */
interface LocalStorageData {
Expand Down
3 changes: 3 additions & 0 deletions app/gui/src/dashboard/assets/inbox.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading