refactor(web): use asterisk icon for upgrade indicator (#1318)

This commit is contained in:
Mo
2022-07-14 10:06:31 -05:00
committed by GitHub
parent 65c377ba62
commit a827274ac6
14 changed files with 149 additions and 199 deletions

View File

@@ -24,6 +24,7 @@ import {
transactionForDisassociateComponentWithCurrentNote,
} from '../NoteView/TransactionFunctions'
import { reloadFont } from '../NoteView/FontFunctions'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
type ChangeEditorMenuProps = {
application: WebApplication
@@ -202,7 +203,7 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
{group.icon && <Icon type={group.icon} className={`mr-2 ${group.iconClassName}`} />}
{item.name}
</div>
{!item.isEntitled && <Icon type="premium-feature" />}
{!item.isEntitled && <Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} />}
</div>
</MenuItem>
)

View File

@@ -1,190 +1,104 @@
import { FunctionComponent, useMemo } from 'react'
import { IconType } from '@standardnotes/snjs'
import {
AccessibilityIcon,
AccountCircleIcon,
AddIcon,
ArchiveIcon,
ArrowLeftIcon,
ArrowsSortDownIcon,
ArrowsSortUpIcon,
AttachmentFileIcon,
AuthenticatorIcon,
CheckBoldIcon,
CheckCircleIcon,
CheckIcon,
ChevronDownIcon,
ChevronRightIcon,
ClearCircleFilledIcon,
CloseIcon,
CloudOffIcon,
CodeIcon,
CopyIcon,
DashboardIcon,
DownloadIcon,
EditorIcon,
EmailIcon,
EyeIcon,
EyeOffIcon,
FileDocIcon,
FileIcon,
FileImageIcon,
FileMovIcon,
FileMusicIcon,
FileOtherIcon,
FilePdfIcon,
FilePptIcon,
FileXlsIcon,
FileZipIcon,
FolderIcon,
HashtagIcon,
HashtagOffIcon,
HelpIcon,
HistoryIcon,
InfoIcon,
KeyboardIcon,
LinkIcon,
LinkOffIcon,
ListBulleted,
ListedIcon,
LockFilledIcon,
LockIcon,
MarkdownIcon,
MenuArrowDownAlt,
MenuArrowDownIcon,
MenuArrowRightIcon,
MenuCloseIcon,
MoreIcon,
NotesFilledIcon,
NotesIcon,
PasswordIcon,
PencilFilledIcon,
PencilIcon,
PencilOffIcon,
PinFilledIcon,
PinIcon,
PlainTextIcon,
PremiumFeatureIcon,
RestoreIcon,
RichTextIcon,
SearchIcon,
SecurityIcon,
ServerIcon,
SettingsIcon,
SignInIcon,
SignOutIcon,
SortDescendingIcon,
SpreadsheetsIcon,
StarIcon,
SubtractIcon,
SyncIcon,
TasksIcon,
ThemesIcon,
TrashFilledIcon,
TrashIcon,
TrashSweepIcon,
TuneIcon,
UnarchiveIcon,
UnpinIcon,
UserAddIcon,
UserIcon,
UserSwitch,
WarningIcon,
WindowIcon,
} from '@standardnotes/icons'
import * as icons from '@standardnotes/icons'
export const ICONS = {
'account-circle': AccountCircleIcon,
'arrow-left': ArrowLeftIcon,
'arrows-sort-down': ArrowsSortDownIcon,
'arrows-sort-up': ArrowsSortUpIcon,
'attachment-file': AttachmentFileIcon,
'check-bold': CheckBoldIcon,
'check-circle': CheckCircleIcon,
'chevron-down': ChevronDownIcon,
'chevron-right': ChevronRightIcon,
'clear-circle-filled': ClearCircleFilledIcon,
'cloud-off': CloudOffIcon,
'eye-off': EyeOffIcon,
'file-doc': FileDocIcon,
'file-image': FileImageIcon,
'file-mov': FileMovIcon,
'file-music': FileMusicIcon,
'file-other': FileOtherIcon,
'file-pdf': FilePdfIcon,
'file-ppt': FilePptIcon,
'file-xls': FileXlsIcon,
'file-zip': FileZipIcon,
'hashtag-off': HashtagOffIcon,
'link-off': LinkOffIcon,
'list-bulleted': ListBulleted,
'lock-filled': LockFilledIcon,
'menu-arrow-down-alt': MenuArrowDownAlt,
'menu-arrow-down': MenuArrowDownIcon,
'menu-arrow-right': MenuArrowRightIcon,
'menu-close': MenuCloseIcon,
'notes-filled': NotesFilledIcon,
'pencil-filled': PencilFilledIcon,
'pencil-off': PencilOffIcon,
'pin-filled': PinFilledIcon,
'plain-text': PlainTextIcon,
'premium-feature': PremiumFeatureIcon,
'rich-text': RichTextIcon,
'sort-descending': SortDescendingIcon,
'trash-filled': TrashFilledIcon,
'trash-sweep': TrashSweepIcon,
'user-add': UserAddIcon,
'user-switch': UserSwitch,
accessibility: AccessibilityIcon,
add: AddIcon,
archive: ArchiveIcon,
authenticator: AuthenticatorIcon,
check: CheckIcon,
close: CloseIcon,
code: CodeIcon,
copy: CopyIcon,
dashboard: DashboardIcon,
download: DownloadIcon,
editor: EditorIcon,
email: EmailIcon,
eye: EyeIcon,
file: FileIcon,
folder: FolderIcon,
hashtag: HashtagIcon,
help: HelpIcon,
history: HistoryIcon,
info: InfoIcon,
keyboard: KeyboardIcon,
link: LinkIcon,
listed: ListedIcon,
lock: LockIcon,
markdown: MarkdownIcon,
more: MoreIcon,
notes: NotesIcon,
password: PasswordIcon,
pencil: PencilIcon,
pin: PinIcon,
restore: RestoreIcon,
search: SearchIcon,
security: SecurityIcon,
server: ServerIcon,
settings: SettingsIcon,
signIn: SignInIcon,
signOut: SignOutIcon,
spreadsheets: SpreadsheetsIcon,
star: StarIcon,
subtract: SubtractIcon,
sync: SyncIcon,
tasks: TasksIcon,
themes: ThemesIcon,
trash: TrashIcon,
tune: TuneIcon,
unarchive: UnarchiveIcon,
unpin: UnpinIcon,
user: UserIcon,
warning: WarningIcon,
window: WindowIcon,
'account-circle': icons.AccountCircleIcon,
'arrow-left': icons.ArrowLeftIcon,
'arrows-sort-down': icons.ArrowsSortDownIcon,
'arrows-sort-up': icons.ArrowsSortUpIcon,
'attachment-file': icons.AttachmentFileIcon,
'check-bold': icons.CheckBoldIcon,
'check-circle': icons.CheckCircleIcon,
'chevron-down': icons.ChevronDownIcon,
'chevron-right': icons.ChevronRightIcon,
'clear-circle-filled': icons.ClearCircleFilledIcon,
'cloud-off': icons.CloudOffIcon,
'diamond-filled': icons.DiamondFilledIcon,
'eye-off': icons.EyeOffIcon,
'file-doc': icons.FileDocIcon,
'file-image': icons.FileImageIcon,
'file-mov': icons.FileMovIcon,
'file-music': icons.FileMusicIcon,
'file-other': icons.FileOtherIcon,
'file-pdf': icons.FilePdfIcon,
'file-ppt': icons.FilePptIcon,
'file-xls': icons.FileXlsIcon,
'file-zip': icons.FileZipIcon,
'hashtag-off': icons.HashtagOffIcon,
'link-off': icons.LinkOffIcon,
'list-bulleted': icons.ListBulleted,
'lock-filled': icons.LockFilledIcon,
'menu-arrow-down-alt': icons.MenuArrowDownAlt,
'menu-arrow-down': icons.MenuArrowDownIcon,
'menu-arrow-right': icons.MenuArrowRightIcon,
'menu-close': icons.MenuCloseIcon,
'notes-filled': icons.NotesFilledIcon,
'pencil-filled': icons.PencilFilledIcon,
'pencil-off': icons.PencilOffIcon,
'pin-filled': icons.PinFilledIcon,
'plain-text': icons.PlainTextIcon,
'premium-feature': icons.PremiumFeatureIcon,
'rich-text': icons.RichTextIcon,
'sort-descending': icons.SortDescendingIcon,
'star-circle-filled': icons.StarCircleFilled,
'star-filled': icons.StarFilledIcon,
'star-variant-filled': icons.StarVariantFilledIcon,
'trash-filled': icons.TrashFilledIcon,
'trash-sweep': icons.TrashSweepIcon,
'user-add': icons.UserAddIcon,
'user-switch': icons.UserSwitch,
accessibility: icons.AccessibilityIcon,
add: icons.AddIcon,
archive: icons.ArchiveIcon,
asterisk: icons.AsteriskIcon,
authenticator: icons.AuthenticatorIcon,
check: icons.CheckIcon,
close: icons.CloseIcon,
code: icons.CodeIcon,
copy: icons.CopyIcon,
dashboard: icons.DashboardIcon,
diamond: icons.DiamondIcon,
download: icons.DownloadIcon,
editor: icons.EditorIcon,
email: icons.EmailIcon,
eye: icons.EyeIcon,
file: icons.FileIcon,
folder: icons.FolderIcon,
hashtag: icons.HashtagIcon,
help: icons.HelpIcon,
history: icons.HistoryIcon,
info: icons.InfoIcon,
keyboard: icons.KeyboardIcon,
link: icons.LinkIcon,
listed: icons.ListedIcon,
lock: icons.LockIcon,
markdown: icons.MarkdownIcon,
more: icons.MoreIcon,
notes: icons.NotesIcon,
password: icons.PasswordIcon,
pencil: icons.PencilIcon,
pin: icons.PinIcon,
restore: icons.RestoreIcon,
search: icons.SearchIcon,
security: icons.SecurityIcon,
server: icons.ServerIcon,
settings: icons.SettingsIcon,
signIn: icons.SignInIcon,
signOut: icons.SignOutIcon,
spreadsheets: icons.SpreadsheetsIcon,
star: icons.StarIcon,
subtract: icons.SubtractIcon,
sync: icons.SyncIcon,
tasks: icons.TasksIcon,
themes: icons.ThemesIcon,
trash: icons.TrashIcon,
tune: icons.TuneIcon,
unarchive: icons.UnarchiveIcon,
unpin: icons.UnpinIcon,
user: icons.UserIcon,
warning: icons.WarningIcon,
window: icons.WindowIcon,
}
type Props = {

View File

@@ -0,0 +1,4 @@
import { IconType } from '@standardnotes/snjs'
export const PremiumFeatureIconName: IconType = 'asterisk'
export const PremiumFeatureIconClass = 'text-info'

View File

@@ -12,6 +12,7 @@ import { sortThemes } from '@/Utils/SortThemes'
import PreferencesPane from '../PreferencesComponents/PreferencesPane'
import PreferencesGroup from '../PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '../PreferencesComponents/PreferencesSegment'
import { PremiumFeatureIconName } from '@/Components/Icon/PremiumFeatureIcon'
type Props = {
application: WebApplication
@@ -57,7 +58,7 @@ const Appearance: FunctionComponent<Props> = ({ application }) => {
themesAsItems.push({
label: theme.name as string,
value: theme.identifier,
icon: 'premium-feature',
icon: PremiumFeatureIconName,
})
}
})
@@ -86,7 +87,7 @@ const Appearance: FunctionComponent<Props> = ({ application }) => {
}
const changeAutoLightTheme = (value: string, item: DropdownItem) => {
if (item.icon === 'premium-feature') {
if (item.icon === PremiumFeatureIconName) {
premiumModal.activate(`${item.label} theme`)
} else {
application.setPreference(PrefKey.AutoLightThemeIdentifier, value as FeatureIdentifier).catch(console.error)
@@ -95,7 +96,7 @@ const Appearance: FunctionComponent<Props> = ({ application }) => {
}
const changeAutoDarkTheme = (value: string, item: DropdownItem) => {
if (item.icon === 'premium-feature') {
if (item.icon === PremiumFeatureIconName) {
premiumModal.activate(`${item.label} theme`)
} else {
application.setPreference(PrefKey.AutoDarkThemeIdentifier, value as FeatureIdentifier).catch(console.error)

View File

@@ -1,14 +1,16 @@
import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog'
import { FunctionComponent, useCallback, useRef } from 'react'
import Icon from '@/Components/Icon/Icon'
import { PremiumIllustration } from '@standardnotes/icons'
import { WebApplication } from '@/Application/Application'
import { openSubscriptionDashboard } from '@/Utils/ManageSubscription'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
import { loadPurchaseFlowUrl } from '../PurchaseFlow/PurchaseFlowFunctions'
type Props = {
application: WebApplication
featureName: string
hasSubscription: boolean
hasAccount: boolean
onClose: () => void
showModal: boolean
}
@@ -17,6 +19,7 @@ const PremiumFeaturesModal: FunctionComponent<Props> = ({
application,
featureName,
hasSubscription,
hasAccount,
onClose,
showModal,
}) => {
@@ -25,10 +28,12 @@ const PremiumFeaturesModal: FunctionComponent<Props> = ({
const handleClick = useCallback(() => {
if (hasSubscription) {
openSubscriptionDashboard(application)
} else if (hasAccount) {
void loadPurchaseFlowUrl(application)
} else if (window.plansUrl) {
window.location.assign(window.plansUrl)
}
}, [application, hasSubscription])
}, [application, hasSubscription, hasAccount])
return showModal ? (
<AlertDialog leastDestructiveRef={plansButtonRef} className="p-0">
@@ -44,14 +49,17 @@ const PremiumFeaturesModal: FunctionComponent<Props> = ({
<Icon className="text-neutral" type="close" />
</button>
</div>
<div className="flex items-center justify-center p-1" aria-hidden={true}>
<PremiumIllustration className="mb-2" />
<div
className="mx-auto mb-5 flex h-24 w-24 items-center justify-center rounded-[50%] bg-contrast"
aria-hidden={true}
>
<Icon className={`h-12 w-12 ${PremiumFeatureIconClass}`} type={PremiumFeatureIconName} />
</div>
<div className="mb-1 text-center text-lg font-bold">Enable Advanced Features</div>
</AlertDialogLabel>
<AlertDialogDescription className="mb-2 px-4.5 text-center text-sm text-passive-1">
In order to use <span className="font-semibold">{featureName}</span> and other advanced features, please
purchase a subscription or upgrade your current plan.
To take advantage of <span className="font-semibold">{featureName}</span> and other advanced features,
upgrade your current plan.
</AlertDialogDescription>
<div className="p-4">
<button
@@ -59,7 +67,7 @@ const PremiumFeaturesModal: FunctionComponent<Props> = ({
className="no-border w-full cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125"
ref={plansButtonRef}
>
{hasSubscription ? 'Upgrade Plan' : 'See Plans'}
Upgrade
</button>
</div>
</div>

View File

@@ -4,6 +4,7 @@ import { FunctionComponent, MouseEventHandler, useCallback } from 'react'
import Icon from '@/Components/Icon/Icon'
import { usePremiumModal } from '@/Hooks/usePremiumModal'
import Switch from '@/Components/Switch/Switch'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
type Props = {
application: WebApplication
@@ -44,7 +45,7 @@ const FocusModeSwitch: FunctionComponent<Props> = ({ application, onToggle, onCl
<Switch className="px-0" checked={isEnabled} />
) : (
<div title="Premium feature">
<Icon type="premium-feature" />
<Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} />
</div>
)}
</button>

View File

@@ -6,6 +6,7 @@ import { usePremiumModal } from '@/Hooks/usePremiumModal'
import Switch from '@/Components/Switch/Switch'
import { ThemeItem } from './ThemeItem'
import RadioIndicator from '../RadioIndicator/RadioIndicator'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
type Props = {
item: ThemeItem
@@ -57,7 +58,7 @@ const ThemesMenuButton: FunctionComponent<Props> = ({ application, item, onBlur
<Switch className="mr-2 px-0" checked={item.component?.active} />
{item.name}
</div>
{!canActivateTheme && <Icon type="premium-feature" />}
{!canActivateTheme && <Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} />}
</>
) : (
<>
@@ -73,7 +74,7 @@ const ThemesMenuButton: FunctionComponent<Props> = ({ application, item, onBlur
}}
></div>
) : (
<Icon type="premium-feature" />
<Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} />
)}
</>
)}

View File

@@ -7,6 +7,7 @@ import { previewHistoryEntryTitle } from './utils'
import { FeaturesClientInterface, RevisionListEntry } from '@standardnotes/snjs/dist/@types'
import { NoteHistoryController } from '@/Controllers/NoteHistory/NoteHistoryController'
import Spinner from '@/Components/Spinner/Spinner'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
type RemoteHistoryListProps = {
features: FeaturesClientInterface
@@ -47,7 +48,9 @@ const RemoteHistoryList: FunctionComponent<RemoteHistoryListProps> = ({ features
>
<div className="flex flex-grow items-center justify-between">
<div>{previewHistoryEntryTitle(entry)}</div>
{!features.hasMinimumRole(entry.required_role) && <Icon type="premium-feature" />}
{!features.hasMinimumRole(entry.required_role) && (
<Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} />
)}
</div>
</HistoryListItem>
))}

View File

@@ -10,6 +10,7 @@ import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { formatDateForContextMenu } from '@/Utils/DateUtils'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
type ContextMenuProps = {
navigationController: NavigationController
@@ -83,7 +84,7 @@ const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag
<Icon type="add" className="mr-2 text-neutral" />
Add subtag
</div>
{!isEntitledToFolders && <Icon type="premium-feature" />}
{!isEntitledToFolders && <Icon type={PremiumFeatureIconName} className={PremiumFeatureIconClass} />}
</MenuItem>
<MenuItem type={MenuItemType.IconButton} className={'py-1.5'} onClick={onClickRename}>
<Icon type="pencil-filled" className="mr-2 text-neutral" />

View File

@@ -40,6 +40,8 @@ const PremiumModalProvider: FunctionComponent<Props> = observer(
!viewControllerManager.subscriptionController.isUserSubscriptionCanceled,
)
const hasAccount = application.hasAccount()
const activate = useCallback(
(feature: string) => {
viewControllerManager.featuresController.showPremiumAlert(feature).catch(console.error)
@@ -58,6 +60,7 @@ const PremiumModalProvider: FunctionComponent<Props> = observer(
application={application}
featureName={featureName}
hasSubscription={hasSubscription}
hasAccount={hasAccount}
onClose={close}
showModal={!!featureName}
/>