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

@@ -0,0 +1,3 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.00002 2.5L1.66669 7.5L10 18.3333L18.3334 7.5L15 2.5H5.00002Z"/>
</svg>

After

Width:  |  Height:  |  Size: 156 B

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.525 15L10 12.875L6.47502 15L7.40835 10.9916L4.30002 8.29996L8.40002 7.94996L10 4.16663L11.6 7.94163L15.7 8.29163L12.5917 10.9833L13.525 15ZM10 1.66663C5.39169 1.66663 1.66669 5.41663 1.66669 9.99996C1.66669 12.2101 2.54466 14.3297 4.10746 15.8925C4.88129 16.6663 5.79994 17.2802 6.81099 17.699C7.82204 18.1177 8.90567 18.3333 10 18.3333C12.2102 18.3333 14.3298 17.4553 15.8926 15.8925C17.4554 14.3297 18.3334 12.2101 18.3334 9.99996C18.3334 8.90561 18.1178 7.82198 17.699 6.81093C17.2802 5.79988 16.6664 4.88122 15.8926 4.1074C15.1188 3.33358 14.2001 2.71975 13.189 2.30096C12.178 1.88217 11.0944 1.66663 10 1.66663V1.66663Z"/>
</svg>

After

Width:  |  Height:  |  Size: 721 B

View File

@@ -47,6 +47,7 @@ import CopyIcon from './ic-copy.svg'
import CreateAccountIllustration from './create-account-illustration.svg'
import DashboardIcon from './ic-dashboard.svg'
import DiamondIcon from './diamond-with-horizontal-lines.svg'
import DiamondFilledIcon from './ic-diamond-filled.svg'
import DownloadIcon from './ic-download.svg'
import DragIcon from './ic-drag.svg'
import DrawIcon from './ic-draw.svg'
@@ -167,6 +168,7 @@ import SNLogoIcon from './ic-standard-notes.svg'
import SortDescendingIcon from './ic-sort-descending.svg'
import SpreadsheetsIcon from './ic-spreadsheets.svg'
import StarFilledIcon from './ic-star-filled.svg'
import StarCircleFilled from './ic-star-circle-filled.svg'
import StarIcon from './ic-star.svg'
import StarVariantFilledIcon from './ic-star-variant-filled.svg'
import StrikethroughIcon from './ic-strikethrough.svg'
@@ -245,6 +247,7 @@ export {
CopyIcon,
CreateAccountIllustration,
DashboardIcon,
DiamondFilledIcon,
DiamondIcon,
DownloadIcon,
DragIcon,
@@ -365,6 +368,7 @@ export {
SNLogoIcon,
SortDescendingIcon,
SpreadsheetsIcon,
StarCircleFilled,
StarFilledIcon,
StarIcon,
StarVariantFilledIcon,

View File

@@ -40,6 +40,8 @@ export type IconType =
| 'color-fill'
| 'copy'
| 'dashboard'
| 'diamond-filled'
| 'diamond'
| 'download'
| 'drag'
| 'draw'
@@ -151,6 +153,7 @@ export type IconType =
| 'spreadsheets'
| 'standard-notes-2'
| 'standard-notes'
| 'star-circle-filled'
| 'star-filled'
| 'star-variant-filled'
| 'star'

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}
/>