feat: add workspace switcher to lock screen (#969)

This commit is contained in:
Aman Harwara
2022-04-13 22:20:56 +05:30
committed by GitHub
parent 8e467f9e6d
commit 9ba7b875a8
15 changed files with 163 additions and 13 deletions

View File

@@ -10,6 +10,7 @@ type Props = {
onClick: () => void
onDelete: () => void
renameDescriptor: (label: string) => void
hideOptions: boolean
}
export const WorkspaceMenuItem: FunctionComponent<Props> = ({
@@ -17,6 +18,7 @@ export const WorkspaceMenuItem: FunctionComponent<Props> = ({
onClick,
onDelete,
renameDescriptor,
hideOptions,
}) => {
const [isRenaming, setIsRenaming] = useState(false)
const inputRef = useRef<HTMLInputElement>(null)
@@ -58,7 +60,7 @@ export const WorkspaceMenuItem: FunctionComponent<Props> = ({
) : (
<div>{descriptor.label}</div>
)}
{descriptor.primary && (
{descriptor.primary && !hideOptions && (
<div>
<button
className="w-5 h-5 p-0 mr-3 border-0 bg-transparent hover:bg-contrast cursor-pointer"

View File

@@ -13,10 +13,11 @@ type Props = {
mainApplicationGroup: ApplicationGroup
appState: AppState
isOpen: boolean
hideWorkspaceOptions?: boolean
}
export const WorkspaceSwitcherMenu: FunctionComponent<Props> = observer(
({ mainApplicationGroup, appState, isOpen }) => {
({ mainApplicationGroup, appState, isOpen, hideWorkspaceOptions = false }) => {
const [applicationDescriptors, setApplicationDescriptors] = useState<ApplicationDescriptor[]>(
[],
)
@@ -37,6 +38,7 @@ export const WorkspaceSwitcherMenu: FunctionComponent<Props> = observer(
{applicationDescriptors.map((descriptor) => (
<WorkspaceMenuItem
descriptor={descriptor}
hideOptions={hideWorkspaceOptions}
onDelete={() => {
appState.accountMenu.setSigningOut(true)
}}

View File

@@ -193,6 +193,8 @@ export class ApplicationView extends PureComponent<Props, State> {
<ChallengeModal
key={challenge.id}
application={this.application}
appState={this.appState}
mainApplicationGroup={this.props.mainApplicationGroup}
challenge={challenge}
onDismiss={this.removeChallenge}
/>

View File

@@ -14,6 +14,9 @@ import { useCallback, useEffect, useState } from 'preact/hooks'
import { Button } from '@/Components/Button/Button'
import { Icon } from '@/Components/Icon'
import { ChallengeModalPrompt } from './ChallengePrompt'
import { LockscreenWorkspaceSwitcher } from './LockscreenWorkspaceSwitcher'
import { ApplicationGroup } from '@/UIModels/ApplicationGroup'
import { AppState } from '@/UIModels/AppState'
type InputValue = {
prompt: ChallengePrompt
@@ -25,6 +28,8 @@ export type ChallengeModalValues = Record<ChallengePrompt['id'], InputValue>
type Props = {
application: WebApplication
appState: AppState
mainApplicationGroup: ApplicationGroup
challenge: Challenge
onDismiss: (challenge: Challenge) => Promise<void>
}
@@ -48,7 +53,13 @@ const validateValues = (
return undefined
}
export const ChallengeModal: FunctionComponent<Props> = ({ application, challenge, onDismiss }) => {
export const ChallengeModal: FunctionComponent<Props> = ({
application,
appState,
mainApplicationGroup,
challenge,
onDismiss,
}) => {
const [values, setValues] = useState<ChallengeModalValues>(() => {
const values = {} as ChallengeModalValues
for (const prompt of challenge.prompts) {
@@ -68,6 +79,7 @@ export const ChallengeModal: FunctionComponent<Props> = ({ application, challeng
ChallengeReason.ApplicationUnlock,
ChallengeReason.Migration,
].includes(challenge.reason)
const shouldShowWorkspaceSwitcher = challenge.reason === ChallengeReason.ApplicationUnlock
const submit = async () => {
const validatedValues = validateValues(values, challenge.prompts)
@@ -252,6 +264,12 @@ export const ChallengeModal: FunctionComponent<Props> = ({ application, challeng
Forgot passcode?
</Button>
)}
{shouldShowWorkspaceSwitcher && (
<LockscreenWorkspaceSwitcher
mainApplicationGroup={mainApplicationGroup}
appState={appState}
/>
)}
</DialogContent>
</DialogOverlay>
)

View File

@@ -0,0 +1,78 @@
import { ApplicationGroup } from '@/UIModels/ApplicationGroup'
import { AppState } from '@/UIModels/AppState'
import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle'
import { FunctionComponent } from 'preact'
import { useEffect, useRef, useState } from 'preact/hooks'
import { WorkspaceSwitcherMenu } from '@/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu'
import { Button } from '@/Components/Button/Button'
import { Icon } from '@/Components/Icon'
import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside'
type Props = {
mainApplicationGroup: ApplicationGroup
appState: AppState
}
export const LockscreenWorkspaceSwitcher: FunctionComponent<Props> = ({
mainApplicationGroup,
appState,
}) => {
const buttonRef = useRef<HTMLButtonElement>(null)
const menuRef = useRef<HTMLDivElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
const [isOpen, setIsOpen] = useState(false)
const [menuStyle, setMenuStyle] = useState<SubmenuStyle>()
useCloseOnClickOutside(containerRef, () => setIsOpen(false))
const toggleMenu = () => {
if (!isOpen) {
const menuPosition = calculateSubmenuStyle(buttonRef.current)
if (menuPosition) {
setMenuStyle(menuPosition)
}
}
setIsOpen(!isOpen)
}
useEffect(() => {
if (isOpen) {
const timeToWaitBeforeCheckingMenuCollision = 0
setTimeout(() => {
const newMenuPosition = calculateSubmenuStyle(buttonRef.current, menuRef.current)
if (newMenuPosition) {
setMenuStyle(newMenuPosition)
}
}, timeToWaitBeforeCheckingMenuCollision)
}
}, [isOpen])
return (
<div ref={containerRef}>
<Button
ref={buttonRef}
onClick={toggleMenu}
className="flex items-center justify-center min-w-76 mt-2"
>
<Icon type="user-switch" className="color-neutral mr-2" />
Switch workspace
</Button>
{isOpen && (
<div
ref={menuRef}
className="sn-dropdown max-h-120 min-w-68 py-2 fixed overflow-y-auto"
style={menuStyle}
>
<WorkspaceSwitcherMenu
mainApplicationGroup={mainApplicationGroup}
appState={appState}
isOpen={isOpen}
hideWorkspaceOptions={true}
/>
</div>
)}
</div>
)
}

View File

@@ -6,7 +6,12 @@ import { ApplicationEvent, PrefKey } from '@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent } from 'preact'
import { useCallback, useEffect, useMemo, useState } from 'preact/hooks'
import { PanelSide, ResizeFinishCallback, PanelResizer, PanelResizeType } from '@/Components/PanelResizer'
import {
PanelSide,
ResizeFinishCallback,
PanelResizer,
PanelResizeType,
} from '@/Components/PanelResizer'
type Props = {
application: WebApplication

View File

@@ -10,7 +10,12 @@ import { NoAccountWarning } from '@/Components/NoAccountWarning'
import { NotesList } from '@/Components/NotesList'
import { NotesListOptionsMenu } from '@/Components/NotesList/NotesListOptionsMenu'
import { SearchOptions } from '@/Components/SearchOptions'
import { PanelSide, ResizeFinishCallback, PanelResizer, PanelResizeType } from '@/Components/PanelResizer'
import {
PanelSide,
ResizeFinishCallback,
PanelResizer,
PanelResizeType,
} from '@/Components/PanelResizer'
import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure'
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'

View File

@@ -1,5 +1,8 @@
import { FunctionalComponent } from 'preact'
import { PreferencesGroup, PreferencesSegment } from '@/Components/Preferences/PreferencesComponents'
import {
PreferencesGroup,
PreferencesSegment,
} from '@/Components/Preferences/PreferencesComponents'
import { OfflineSubscription } from '@/Components/Preferences/Panes/Account/OfflineSubscription'
import { WebApplication } from '@/UIModels/Application'
import { observer } from 'mobx-react-lite'

View File

@@ -1,4 +1,8 @@
import { PreferencesGroup, PreferencesSegment, Title } from '@/Components/Preferences/PreferencesComponents'
import {
PreferencesGroup,
PreferencesSegment,
Title,
} from '@/Components/Preferences/PreferencesComponents'
import { WebApplication } from '@/UIModels/Application'
import { SubscriptionInformation } from './SubscriptionInformation'
import { NoSubscription } from './NoSubscription'

View File

@@ -17,7 +17,13 @@ import { JSXInternal } from 'preact/src/jsx'
import TargetedEvent = JSXInternal.TargetedEvent
import { AppState } from '@/UIModels/AppState'
import { observer } from 'mobx-react-lite'
import { PreferencesGroup, PreferencesSegment, Title, Text, Subtitle } from '@/Components/Preferences/PreferencesComponents'
import {
PreferencesGroup,
PreferencesSegment,
Title,
Text,
Subtitle,
} from '@/Components/Preferences/PreferencesComponents'
import { Button } from '@/Components/Button/Button'
type Props = {

View File

@@ -3,7 +3,13 @@ import { STRING_FAILED_TO_UPDATE_USER_SETTING } from '@/Strings'
import { useCallback, useEffect, useState } from 'preact/hooks'
import { WebApplication } from '@/UIModels/Application'
import { observer } from 'mobx-react-lite'
import { PreferencesGroup, PreferencesSegment, Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents'
import {
PreferencesGroup,
PreferencesSegment,
Subtitle,
Text,
Title,
} from '@/Components/Preferences/PreferencesComponents'
import { Dropdown, DropdownItem } from '@/Components/Dropdown'
import { Switch } from '@/Components/Switch'
import { HorizontalSeparator } from '@/Components/Shared/HorizontalSeparator'

View File

@@ -1,7 +1,12 @@
import { DisplayStringForContentType, SNComponent } from '@standardnotes/snjs'
import { Button } from '@/Components/Button/Button'
import { FunctionComponent } from 'preact'
import { Title, Text, Subtitle, PreferencesSegment } from '@/Components/Preferences/PreferencesComponents'
import {
Title,
Text,
Subtitle,
PreferencesSegment,
} from '@/Components/Preferences/PreferencesComponents'
export const ConfirmCustomExtension: FunctionComponent<{
component: SNComponent

View File

@@ -1,6 +1,10 @@
import { FunctionComponent } from 'preact'
import { SNComponent } from '@standardnotes/snjs'
import { PreferencesSegment, SubtitleLight, Title } from '@/Components/Preferences/PreferencesComponents'
import {
PreferencesSegment,
SubtitleLight,
Title,
} from '@/Components/Preferences/PreferencesComponents'
import { Switch } from '@/Components/Switch'
import { WebApplication } from '@/UIModels/Application'
import { useState } from 'preact/hooks'

View File

@@ -3,7 +3,12 @@ import { STRING_E2E_ENABLED, STRING_ENC_NOT_ENABLED, STRING_LOCAL_ENC_ENABLED }
import { AppState } from '@/UIModels/AppState'
import { observer } from 'mobx-react-lite'
import { ComponentChild, FunctionComponent } from 'preact'
import { PreferencesGroup, PreferencesSegment, Text, Title } from '@/Components/Preferences/PreferencesComponents'
import {
PreferencesGroup,
PreferencesSegment,
Text,
Title,
} from '@/Components/Preferences/PreferencesComponents'
const formatCount = (count: number, itemType: string) => `${count} / ${count} ${itemType}`

View File

@@ -1,5 +1,10 @@
import { FunctionComponent } from 'preact'
import { Title, Text, PreferencesGroup, PreferencesSegment } from '@/Components/Preferences/PreferencesComponents'
import {
Title,
Text,
PreferencesGroup,
PreferencesSegment,
} from '@/Components/Preferences/PreferencesComponents'
import { Switch } from '@/Components/Switch'
import { observer } from 'mobx-react-lite'
import { is2FAActivation, is2FADisabled, TwoFactorAuth } from './TwoFactorAuth'