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

Add "About app" modal #9833

Merged
merged 29 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0b5f077
WIP: Version page
somebody1234 May 1, 2024
e5d374b
Auto-generate version in development mode
somebody1234 May 1, 2024
20c1507
Also show Electron version
somebody1234 May 1, 2024
747c9b7
Switch to use actual versions
somebody1234 May 2, 2024
391c457
Merge branch 'develop' into wip/sb/about-app
somebody1234 May 2, 2024
f60a2cc
Also show user agent
somebody1234 May 2, 2024
9c6e95b
Add copy-paste support
somebody1234 May 2, 2024
0118cd3
Adjust styles
somebody1234 May 2, 2024
5a4d655
Fix lint errors
somebody1234 May 2, 2024
9401e6b
Switch to dialog components
somebody1234 May 2, 2024
5cf728c
Generate UI and string from array instead of generating string from UI
somebody1234 May 2, 2024
9eb4189
WIP: Fix "about" modal interactions
somebody1234 May 2, 2024
b3c61c4
Fix closing "about" modal
somebody1234 May 2, 2024
aa173e0
WIP: Menu entry for opening "about" modal
somebody1234 May 2, 2024
31c38db
Fix "about" button in system menu
somebody1234 May 2, 2024
c017e16
WIP: Refactor `TheModal` out of dashboard
somebody1234 May 2, 2024
7edd217
Add top right menu to authentication pages
somebody1234 May 2, 2024
960cf56
Fix modal not being closeable on auth pages
somebody1234 May 2, 2024
d3625e5
Fix lint errors
somebody1234 May 2, 2024
aa49c77
Fix E2E tests
somebody1234 May 3, 2024
4dc92b4
Fix logging out
somebody1234 May 3, 2024
49ae1b2
Merge branch 'develop' into wip/sb/about-app
somebody1234 May 9, 2024
beecc9e
Prettier
somebody1234 May 9, 2024
f67ba44
Merge branch 'develop' into wip/sb/about-app
somebody1234 May 10, 2024
1d0ae10
Prettier
somebody1234 May 10, 2024
0da91d4
Hide menu bar (was enabled for debugging purposes)
somebody1234 May 13, 2024
1340cfd
Address code review
somebody1234 May 14, 2024
034517e
Merge branch 'develop' into wip/sb/about-app
somebody1234 May 14, 2024
013a769
Use `useLayoutEffect`
somebody1234 May 14, 2024
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/ide-desktop/lib/client/src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import * as electron from 'electron'

import * as debug from 'debug'
import * as ipc from 'ipc'

// =================
Expand All @@ -23,6 +24,8 @@ const FILE_BROWSER_API_KEY = 'fileBrowserApi'

const NAVIGATION_API_KEY = 'navigationApi'

const VERSION_INFO_KEY = 'versionInfo'

// =============================
// === importProjectFromPath ===
// =============================
Expand Down Expand Up @@ -172,3 +175,5 @@ const FILE_BROWSER_API = {
electron.ipcRenderer.invoke(ipc.Channel.openFileBrowser, kind),
}
electron.contextBridge.exposeInMainWorld(FILE_BROWSER_API_KEY, FILE_BROWSER_API)

electron.contextBridge.exposeInMainWorld(VERSION_INFO_KEY, debug.VERSION_INFO)
15 changes: 15 additions & 0 deletions app/ide-desktop/lib/common/src/appConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import * as url from 'node:url'

import BUILD_INFO from '../../../../../build.json' assert { type: 'json' }

// ===============================
// === readEnvironmentFromFile ===
// ===============================
Expand Down Expand Up @@ -45,6 +47,10 @@ export async function readEnvironmentFromFile() {
if (!isProduction || entries.length > 0) {
Object.assign(process.env, variables)
}
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= BUILD_INFO.version
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= BUILD_INFO.commit
} catch (error) {
if (missingKeys.length !== 0) {
console.warn('Could not load `.env` file; disabling cloud backend.')
Expand Down Expand Up @@ -100,6 +106,12 @@ export function getDefines(serverPort = 8080) {
'process.env.ENSO_CLOUD_GOOGLE_ANALYTICS_TAG': stringify(
process.env.ENSO_CLOUD_GOOGLE_ANALYTICS_TAG
),
'process.env.ENSO_CLOUD_DASHBOARD_VERSION': stringify(
process.env.ENSO_CLOUD_DASHBOARD_VERSION
),
'process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH': stringify(
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH
),
/* eslint-enable @typescript-eslint/naming-convention */
}
}
Expand All @@ -119,12 +131,15 @@ const DUMMY_DEFINES = {
'process.env.ENSO_CLOUD_COGNITO_USER_POOL_WEB_CLIENT_ID': '',
'process.env.ENSO_CLOUD_COGNITO_DOMAIN': '',
'process.env.ENSO_CLOUD_COGNITO_REGION': '',
'process.env.ENSO_CLOUD_DASHBOARD_VERSION': '0.0.1-testing',
'process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH': 'abcdef0',
/* eslint-enable @typescript-eslint/naming-convention */
}

/** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */
export function loadTestEnvironmentVariables() {
for (const [k, v] of Object.entries(DUMMY_DEFINES)) {
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env[k.replace(/^process[.]env[.]/, '')] = v
}
}
2 changes: 1 addition & 1 deletion app/ide-desktop/lib/common/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"checkJs": false,
"skipLibCheck": false
},
"include": ["./src/"]
"include": ["./src/", "../types/"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const ACTION_TO_TEXT_ID: Readonly<Record<inputBindings.DashboardBindingKey, text
selectAdditionalRange: 'selectAdditionalRangeShortcut',
goBack: 'goBackShortcut',
goForward: 'goForwardShortcut',
aboutThisApp: 'aboutThisAppShortcut',
} satisfies { [Key in inputBindings.DashboardBindingKey]: `${Key}Shortcut` }

// =================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import CopyIcon from 'enso-assets/copy.svg'
import DataDownloadIcon from 'enso-assets/data_download.svg'
import DataUploadIcon from 'enso-assets/data_upload.svg'
import DuplicateIcon from 'enso-assets/duplicate.svg'
import LogoIcon from 'enso-assets/enso_logo.svg'
import OpenIcon from 'enso-assets/open.svg'
import PasteIcon from 'enso-assets/paste.svg'
import PenIcon from 'enso-assets/pen.svg'
Expand Down Expand Up @@ -121,4 +122,10 @@ export const BINDINGS = inputBindings.defineBindings({
rebindable: true,
icon: ArrowRightIcon,
},
aboutThisApp: {
name: 'About Enso',
bindings: ['Mod+/'],
rebindable: true,
icon: LogoIcon,
},
})
15 changes: 14 additions & 1 deletion app/ide-desktop/lib/dashboard/src/layouts/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import MenuEntry from '#/components/MenuEntry'
import Modal from '#/components/Modal'
import FocusArea from '#/components/styled/FocusArea'

import AboutModal from '#/modals/AboutModal'

import * as download from '#/utilities/download'
import * as github from '#/utilities/github'

Expand All @@ -42,14 +44,23 @@ export default function UserMenu(props: UserMenuProps) {
const navigate = navigateHooks.useNavigate()
const { signOut } = authProvider.useAuth()
const { user } = authProvider.useNonPartialUserSession()
const { unsetModal } = modalProvider.useSetModal()
const { setModal, unsetModal } = modalProvider.useSetModal()
const { getText } = textProvider.useText()
const toastAndLog = toastAndLogHooks.useToastAndLog()

React.useEffect(() => {
requestAnimationFrame(setInitialized.bind(null, true))
}, [])

const aboutThisAppMenuEntry = (
<MenuEntry
action="aboutThisApp"
doAction={() => {
setModal(<AboutModal supportsLocalBackend={supportsLocalBackend} />)
}}
/>
)

return (
<Modal hidden={hidden} className="absolute size-full overflow-hidden bg-dim">
<div
Expand Down Expand Up @@ -105,6 +116,7 @@ export default function UserMenu(props: UserMenuProps) {
setPage(pageSwitcher.Page.settings)
}}
/>
{aboutThisAppMenuEntry}
<MenuEntry
action="signOut"
doAction={() => {
Expand All @@ -126,6 +138,7 @@ export default function UserMenu(props: UserMenuProps) {
<aria.Text className="text">{getText('youAreNotLoggedIn')}</aria.Text>
</div>
<div className="flex flex-col">
{aboutThisAppMenuEntry}
<MenuEntry
action="signIn"
doAction={() => {
Expand Down
138 changes: 138 additions & 0 deletions app/ide-desktop/lib/dashboard/src/modals/AboutModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/** @file Modal for confirming delete of any type of asset. */
import * as React from 'react'

import LogoIcon from 'enso-assets/enso_logo.svg'

import * as textProvider from '#/providers/TextProvider'

import * as aria from '#/components/aria'
import Modal from '#/components/Modal'
import ButtonRow from '#/components/styled/ButtonRow'
import SvgMask from '#/components/SvgMask'
import UnstyledButton from '#/components/UnstyledButton'

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

/** The duration of time for which the "copy" button indicates that the text has
* successfully been copied. */
const CLEAR_COPIED_STATE_TIMEOUT_MS = 2_500

// ==================
// === AboutModal ===
// ==================

/** Props for a {@link AboutModal}. */
export interface AboutModalProps {
readonly supportsLocalBackend: boolean
}

/** A modal for confirming the deletion of an asset. */
export default function AboutModal(props: AboutModalProps) {
const { supportsLocalBackend } = props
const { getText } = textProvider.useText()
const [isCopied, setIsCopied] = React.useState(false)
const textContainerRef = React.useRef<HTMLTableSectionElement | null>(null)

const doCopy = () => {
const textContainer = textContainerRef.current
Copy link
Contributor

@MrFlashAccount MrFlashAccount May 2, 2024

Choose a reason for hiding this comment

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

I think it'd be easier to understand if we build a string from existing variables(window.versionInfo and so forth), instead of trying to extract the information from html.

if (textContainer == null) {
return
} else {
const firstChild = textContainer.children[0]
const lastChild = textContainer.children[textContainer.children.length - 1]
if (firstChild != null && lastChild != null) {
getSelection()?.setBaseAndExtent(firstChild, 0, lastChild, 2)
}
const texts = Array.from(
textContainer.children,
element => `${element.children[0]?.textContent} ${element.children[1]?.textContent}`
)
void navigator.clipboard.writeText(texts.join('\n'))
if (!isCopied) {
setIsCopied(true)
setTimeout(() => {
setIsCopied(false)
}, CLEAR_COPIED_STATE_TIMEOUT_MS)
}
return
}
}

return (
<Modal centered className="bg-dim">
Copy link
Contributor

Choose a reason for hiding this comment

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

Please stop copypasting between components. We have Dialog component, use it instead

<div
data-testid="about-modal"
className="pointer-events-auto relative flex w-[30rem] select-text flex-col gap-modal rounded-default p-modal-wide before:absolute before:inset before:h-full before:w-full before:rounded-default before:bg-selected-frame before:backdrop-blur-default"
onClick={event => {
event.stopPropagation()
}}
>
<div className="relative flex items-center gap-4">
<SvgMask src={LogoIcon} className="size-16 shrink-0" />
<div className="flex flex-col gap-3">
<div className="text-base font-semibold">
{supportsLocalBackend
? getText('appNameDesktopEdition')
: getText('appNameCloudEdition')}
</div>
<table>
<tbody ref={textContainerRef}>
{window.versionInfo != null ? (
<>
<tr>
<td className="whitespace-nowrap pr-cell-x">{getText('version')}</td>
<td>{window.versionInfo.version}</td>
</tr>
<tr>
<td className="whitespace-nowrap pr-cell-x">{getText('build')}</td>
<td>{window.versionInfo.build}</td>
</tr>
<tr>
<td className="whitespace-nowrap pr-cell-x">{getText('electronVersion')}</td>
<td>{window.versionInfo.electron}</td>
</tr>
<tr>
<td className="whitespace-nowrap pr-cell-x">{getText('chromeVersion')}</td>
<td>{window.versionInfo.chrome}</td>
</tr>
</>
) : (
<>
{process.env.ENSO_CLOUD_DASHBOARD_VERSION != null && (
<tr>
<td className="whitespace-nowrap pr-cell-x">{getText('version')}</td>
<td>{process.env.ENSO_CLOUD_DASHBOARD_VERSION}</td>
</tr>
)}
{process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH != null && (
<tr>
<td className="whitespace-nowrap pr-cell-x">{getText('build')}</td>
<td>{process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH}</td>
</tr>
)}
</>
)}
<tr>
<td className="whitespace-nowrap pr-cell-x">{getText('userAgent')}</td>
<td>{navigator.userAgent}</td>
</tr>
</tbody>
</table>
<ButtonRow>
<UnstyledButton
className="button relative bg-invite text-inversed active"
onPress={doCopy}
>
<aria.Text className="text">
{isCopied ? getText('copied') : getText('copy')}
</aria.Text>
</UnstyledButton>
</ButtonRow>
</div>
</div>
</div>
</Modal>
)
}
13 changes: 12 additions & 1 deletion app/ide-desktop/lib/dashboard/src/text/english.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@
"uploaded": "Uploaded",
"delete": "Delete",
"invite": "Invite",
"copy": "Copy",
"copied": "Copied",
"color": "Color",
"labels": "Labels",
"views": "Views",
Expand Down Expand Up @@ -276,6 +278,11 @@
"replyExclamation": "Reply!",
"lastModifiedOn": "last modified on $0",
"versionX": "Version $0",
"version": "Version",
"build": "Build",
"electronVersion": "Electron",
"chromeVersion": "Chrome",
"userAgent": "User Agent",
"compareWithLatest": "Compare with latest",
"compareVersionXWithLatest": "Compare version $0 with latest",
"onDateX": "on $0",
Expand Down Expand Up @@ -450,6 +457,7 @@
"selectAdditionalRangeShortcut": "Select Additional Range",
"goBackShortcut": "Go Back",
"goForwardShortcut": "Go Forward",
"aboutThisAppShortcut": "About Enso",

"moveToTrashShortcut": "Move To Trash",
"moveAllToTrashShortcut": "Move All To Trash",
Expand Down Expand Up @@ -513,5 +521,8 @@
"assetsTableContextMenuLabel": "Drive context menu",
"assetContextMenuLabel": "Asset context menu",
"labelContextMenuLabel": "Label context menu",
"settingsSidebarLabel": "Settings sidebar"
"settingsSidebarLabel": "Settings sidebar",

"appNameDesktopEdition": "Enso Desktop Edition",
"appNameCloudEdition": "Enso Cloud Edition"
}
6 changes: 5 additions & 1 deletion app/ide-desktop/lib/dashboard/src/text/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ interface PlaceholderOverrides {
readonly userIsAlreadyInTheOrganization: [string]
readonly youAreAlreadyAddingUser: [string]
readonly lastModifiedOn: [string]
readonly versionX: [number]
readonly versionX: [version: number | string]
readonly buildX: [build: string]
readonly electronVersionX: [electronVersion: string]
readonly chromeVersionX: [chromeVersion: string]
readonly userAgentX: [userAgent: string]
readonly compareVersionXWithLatest: [number]
readonly onDateX: [string]
readonly xUsersSelected: [number]
Expand Down
17 changes: 17 additions & 0 deletions app/ide-desktop/lib/types/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ interface NavigationApi {
readonly goForward: () => void
}

// ====================
// === Version Info ===
// ====================

/** Versions of the app, and selected software bundled with Electron. */
interface VersionInfo {
readonly version: string
readonly build: string
readonly electron: string
readonly chrome: string
}

// =====================================
// === Global namespace augmentation ===
// =====================================
Expand All @@ -86,6 +98,7 @@ declare global {
readonly backendApi?: BackendApi
readonly authenticationApi: AuthenticationApi
readonly navigationApi: NavigationApi
readonly versionInfo?: VersionInfo
}

namespace NodeJS {
Expand Down Expand Up @@ -142,6 +155,10 @@ declare global {
// @ts-expect-error The index signature is intentional to disallow unknown env vars.
readonly ENSO_CLOUD_GOOGLE_ANALYTICS_TAG?: string
// @ts-expect-error The index signature is intentional to disallow unknown env vars.
readonly ENSO_CLOUD_DASHBOARD_VERSION?: string
// @ts-expect-error The index signature is intentional to disallow unknown env vars.
readonly ENSO_CLOUD_DASHBOARD_COMMIT_HASH?: string
// @ts-expect-error The index signature is intentional to disallow unknown env vars.
readonly ENSO_SUPPORTS_VIBRANCY?: string

// === Electron watch script variables ===
Expand Down
Loading