feat: get editor icons and their colors from snjs (#828)

* feat: get editor icons and their colors from snjs

* feat: get icons and their tints from snjs

* fix: use IconType from snjs
This commit is contained in:
Vardan Hakobyan
2022-01-31 18:58:36 +04:00
committed by GitHub
parent e890e22630
commit bf382ce0f8
21 changed files with 126 additions and 104 deletions

View File

@@ -8,7 +8,8 @@ import {
} from '@reach/listbox';
import VisuallyHidden from '@reach/visually-hidden';
import { FunctionComponent } from 'preact';
import { IconType, Icon } from './Icon';
import { Icon } from './Icon';
import { IconType } from '@standardnotes/snjs';
export type DropdownItem = {
icon?: IconType;

View File

@@ -67,6 +67,7 @@ import MenuArrowDownAlt from '../../icons/ic-menu-arrow-down-alt.svg';
import MenuArrowRight from '../../icons/ic-menu-arrow-right.svg';
import { FunctionalComponent } from 'preact';
import { IconType } from '@standardnotes/snjs';
const ICONS = {
editor: EditorIcon,
@@ -137,8 +138,6 @@ const ICONS = {
'premium-feature': PremiumFeatureIcon,
};
export type IconType = keyof typeof ICONS;
type Props = {
type: IconType;
className?: string;

View File

@@ -1,5 +1,6 @@
import { FunctionComponent } from 'preact';
import { Icon, IconType } from './Icon';
import { Icon } from './Icon';
import { IconType } from '@standardnotes/snjs';
interface Props {
/**

View File

@@ -1,8 +1,9 @@
import { FunctionComponent, Ref } from 'preact';
import { JSXInternal } from 'preact/src/jsx';
import { forwardRef } from 'preact/compat';
import { Icon, IconType } from './Icon';
import { Icon } from './Icon';
import { IconButton } from './IconButton';
import { IconType } from '@standardnotes/snjs';
type ToggleProps = {
toggleOnIcon: IconType;

View File

@@ -1,4 +1,3 @@
import { getIconAndTintForEditor } from '@/preferences/panes/general-segments';
import { WebApplication } from '@/ui_models/application';
import {
CollectionSort,
@@ -74,7 +73,9 @@ export const NotesListItem: FunctionComponent<Props> = ({
const showModifiedDate = sortedBy === CollectionSort.UpdatedAt;
const editorForNote = application.componentManager.editorForNote(note);
const editorName = editorForNote?.name ?? 'Plain editor';
const [icon, tint] = getIconAndTintForEditor(editorForNote?.identifier);
const [icon, tint] = application.iconsController.getIconAndTintForEditor(
editorForNote?.identifier
);
return (
<div

View File

@@ -18,6 +18,7 @@ import {
} from '@reach/disclosure';
import {
ComponentArea,
IconType,
ItemMutator,
NoteMutator,
PrefKey,
@@ -27,7 +28,7 @@ import {
} from '@standardnotes/snjs';
import { FunctionComponent } from 'preact';
import { useEffect, useRef, useState } from 'preact/hooks';
import { Icon, IconType } from '../Icon';
import { Icon } from '../Icon';
import { PremiumModalProvider } from '../Premium';
import { createEditorMenuGroups } from './changeEditor/createEditorMenuGroups';
import { EditorAccordionMenu } from './changeEditor/EditorAccordionMenu';

View File

@@ -1,5 +1,6 @@
import { FunctionComponent } from 'preact';
import { Icon, IconType } from './Icon';
import { Icon } from './Icon';
import { IconType } from '@standardnotes/snjs';
type ButtonType = 'normal' | 'primary';

View File

@@ -29,7 +29,9 @@ export const Switch: FunctionalComponent<SwitchProps> = (
return (
<label
className={`sn-component flex justify-between items-center cursor-pointer px-3 ${className} ${isDisabled ? 'faded' : ''}`}
className={`sn-component flex justify-between items-center cursor-pointer px-3 ${className} ${
isDisabled ? 'faded' : ''
}`}
{...(props.role ? { role: props.role } : {})}
>
{props.children}
@@ -51,8 +53,9 @@ export const Switch: FunctionalComponent<SwitchProps> = (
/>
<span
aria-hidden
className={`sn-switch-handle ${checked ? 'sn-switch-handle--right' : ''
}`}
className={`sn-switch-handle ${
checked ? 'sn-switch-handle--right' : ''
}`}
/>
</CustomCheckboxContainer>
</label>

View File

@@ -1,8 +1,8 @@
import { Icon, IconType } from '@/components/Icon';
import { Icon } from '@/components/Icon';
import { FeaturesState } from '@/ui_models/app_state/features_state';
import { TagsState } from '@/ui_models/app_state/tags_state';
import '@reach/tooltip/styles.css';
import { SNSmartTag } from '@standardnotes/snjs';
import { SNSmartTag, IconType } from '@standardnotes/snjs';
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';

View File

@@ -6,8 +6,9 @@ import {
} from 'preact';
import { forwardRef, Ref } from 'preact/compat';
import { JSXInternal } from 'preact/src/jsx';
import { Icon, IconType } from '../Icon';
import { Icon } from '../Icon';
import { Switch, SwitchProps } from '../Switch';
import { IconType } from '@standardnotes/snjs';
export enum MenuItemType {
IconButton,

View File

@@ -1,4 +1,3 @@
import { IconType } from '@/components/Icon';
import { action, makeAutoObservable, observable } from 'mobx';
import { ExtensionsLatestVersions } from '@/preferences/panes/extensions-segments';
import {
@@ -6,6 +5,7 @@ import {
ContentType,
FeatureIdentifier,
SNComponent,
IconType,
} from '@standardnotes/snjs';
import { WebApplication } from '@/ui_models/application';

View File

@@ -1,5 +1,6 @@
import { Icon, IconType } from '@/components/Icon';
import { Icon } from '@/components/Icon';
import { FunctionComponent } from 'preact';
import { IconType } from '@standardnotes/snjs';
interface Props {
iconType: IconType;

View File

@@ -142,7 +142,8 @@ const AppearancePane: FunctionComponent<Props> = observer(({ application }) => {
<div className="flex flex-col">
<Subtitle>Use system color scheme</Subtitle>
<Text>
Automatically change active theme based on your system settings.
Automatically change active theme based on your system
settings.
</Text>
</div>
<Switch

View File

@@ -1,4 +1,7 @@
import { convertStringifiedBooleanToBoolean, isDesktopApplication } from '@/utils';
import {
convertStringifiedBooleanToBoolean,
isDesktopApplication,
} from '@/utils';
import { STRING_FAILED_TO_UPDATE_USER_SETTING } from '@/strings';
import { useCallback, useEffect, useState } from 'preact/hooks';
import { WebApplication } from '@/ui_models/application';

View File

@@ -5,7 +5,7 @@ import {
CloudProvider,
DropboxBackupFrequency,
GoogleDriveBackupFrequency,
OneDriveBackupFrequency
OneDriveBackupFrequency,
} from '@standardnotes/settings';
import { WebApplication } from '@/ui_models/application';
import { Button } from '@/components/Button';
@@ -20,9 +20,9 @@ type Props = {
};
export const CloudBackupProvider: FunctionComponent<Props> = ({
application,
providerName
}) => {
application,
providerName,
}) => {
const [authBegan, setAuthBegan] = useState(false);
const [successfullyInstalled, setSuccessfullyInstalled] = useState(false);
const [backupFrequency, setBackupFrequency] = useState<string | null>(null);
@@ -32,14 +32,13 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
event.stopPropagation();
try {
const shouldDisable = await application.alertService
.confirm(
'Are you sure you want to disable this integration?',
'Disable?',
'Disable',
ButtonType.Danger,
'Cancel'
);
const shouldDisable = await application.alertService.confirm(
'Are you sure you want to disable this integration?',
'Disable?',
'Disable',
ButtonType.Danger,
'Cancel'
);
if (shouldDisable) {
await application.deleteSetting(backupFrequencySettingName);
await application.deleteSetting(backupTokenSettingName);
@@ -54,7 +53,10 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
const installIntegration = (event: Event) => {
event.stopPropagation();
const authUrl = application.getCloudProviderIntegrationUrl(providerName, isDev);
const authUrl = application.getCloudProviderIntegrationUrl(
providerName,
isDev
);
openInNewTab(authUrl);
setAuthBegan(true);
};
@@ -62,7 +64,10 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
const performBackupNow = async () => {
// A backup is performed anytime the setting is updated with the integration token, so just update it here
try {
await application.updateSetting(backupFrequencySettingName, backupFrequency as string);
await application.updateSetting(
backupFrequencySettingName,
backupFrequency as string
);
application.alertService.alert(
'A backup has been triggered for this provider. Please allow a couple minutes for your backup to be processed.'
);
@@ -77,20 +82,24 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
[CloudProvider.Dropbox]: {
backupTokenSettingName: SettingName.DropboxBackupToken,
backupFrequencySettingName: SettingName.DropboxBackupFrequency,
defaultBackupFrequency: DropboxBackupFrequency.Daily
defaultBackupFrequency: DropboxBackupFrequency.Daily,
},
[CloudProvider.Google]: {
backupTokenSettingName: SettingName.GoogleDriveBackupToken,
backupFrequencySettingName: SettingName.GoogleDriveBackupFrequency,
defaultBackupFrequency: GoogleDriveBackupFrequency.Daily
defaultBackupFrequency: GoogleDriveBackupFrequency.Daily,
},
[CloudProvider.OneDrive]: {
backupTokenSettingName: SettingName.OneDriveBackupToken,
backupFrequencySettingName: SettingName.OneDriveBackupFrequency,
defaultBackupFrequency: OneDriveBackupFrequency.Daily
}
defaultBackupFrequency: OneDriveBackupFrequency.Daily,
},
};
const { backupTokenSettingName, backupFrequencySettingName, defaultBackupFrequency } = backupSettingsData[providerName];
const {
backupTokenSettingName,
backupFrequencySettingName,
defaultBackupFrequency,
} = backupSettingsData[providerName];
const getCloudProviderIntegrationTokenFromUrl = (url: URL) => {
const urlSearchParams = new URLSearchParams(url.search);
@@ -123,8 +132,14 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
if (!cloudProviderToken) {
throw new Error();
}
await application.updateSetting(backupTokenSettingName, cloudProviderToken);
await application.updateSetting(backupFrequencySettingName, defaultBackupFrequency);
await application.updateSetting(
backupTokenSettingName,
cloudProviderToken
);
await application.updateSetting(
backupFrequencySettingName,
defaultBackupFrequency
);
setBackupFrequency(defaultBackupFrequency);
@@ -174,15 +189,15 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
</div>
{authBegan && (
<div>
<p className='sk-panel-row'>
<p className="sk-panel-row">
Complete authentication from the newly opened window. Upon
completion, a confirmation code will be displayed. Enter this code
below:
</p>
<div className={`mt-1`}>
<input
className='sk-input sk-base center-text'
placeholder='Enter confirmation code'
className="sk-input sk-base center-text"
placeholder="Enter confirmation code"
value={confirmation}
onKeyPress={handleKeyPress}
onChange={handleChange}
@@ -193,8 +208,8 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
{shouldShowEnableButton && (
<div>
<Button
type='normal'
label='Enable'
type="normal"
label="Enable"
className={'px-1 text-xs min-w-40'}
onClick={installIntegration}
/>
@@ -204,15 +219,15 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
{backupFrequency && (
<div className={'flex flex-col items-end'}>
<Button
className='min-w-40 mb-2'
type='normal'
label='Perform Backup'
className="min-w-40 mb-2"
type="normal"
label="Perform Backup"
onClick={performBackupNow}
/>
<Button
className='min-w-40'
type='normal'
label='Disable'
className="min-w-40"
type="normal"
label="Disable"
onClick={disable}
/>
</div>

View File

@@ -4,26 +4,34 @@ import { useCallback, useEffect, useState } from 'preact/hooks';
import { WebApplication } from '@/ui_models/application';
import {
PreferencesGroup,
PreferencesSegment, Subtitle,
PreferencesSegment,
Subtitle,
Text,
Title
Title,
} from '@/preferences/components';
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
import { FeatureIdentifier } from '@standardnotes/features';
import { FeatureStatus } from '@standardnotes/snjs';
import { FunctionComponent } from 'preact';
import { CloudProvider, EmailBackupFrequency, SettingName } from '@standardnotes/settings';
import {
CloudProvider,
EmailBackupFrequency,
SettingName,
} from '@standardnotes/settings';
import { Switch } from '@/components/Switch';
import { convertStringifiedBooleanToBoolean } from '@/utils';
import { STRING_FAILED_TO_UPDATE_USER_SETTING } from '@/strings';
const providerData = [{
name: CloudProvider.Dropbox
}, {
name: CloudProvider.Google
}, {
name: CloudProvider.OneDrive
}
const providerData = [
{
name: CloudProvider.Dropbox,
},
{
name: CloudProvider.Google,
},
{
name: CloudProvider.OneDrive,
},
];
type Props = {
@@ -31,8 +39,10 @@ type Props = {
};
export const CloudLink: FunctionComponent<Props> = ({ application }) => {
const [isEntitledForCloudBackups, setIsEntitledForCloudBackups] = useState(false);
const [isFailedCloudBackupEmailMuted, setIsFailedCloudBackupEmailMuted] = useState(true);
const [isEntitledForCloudBackups, setIsEntitledForCloudBackups] =
useState(false);
const [isFailedCloudBackupEmailMuted, setIsFailedCloudBackupEmailMuted] =
useState(true);
const [isLoading, setIsLoading] = useState(false);
const loadIsFailedCloudBackupEmailMutedSetting = useCallback(async () => {
@@ -98,12 +108,12 @@ export const CloudLink: FunctionComponent<Props> = ({ application }) => {
A <span className={'font-bold'}>Plus</span> or{' '}
<span className={'font-bold'}>Pro</span> subscription plan is
required to enable Cloud Backups.{' '}
<a target='_blank' href='https://standardnotes.com/features'>
<a target="_blank" href="https://standardnotes.com/features">
Learn more
</a>
.
</Text>
<HorizontalSeparator classes='mt-3 mb-3' />
<HorizontalSeparator classes="mt-3 mb-3" />
</>
)}
<div
@@ -122,7 +132,10 @@ export const CloudLink: FunctionComponent<Props> = ({ application }) => {
<div>
{providerData.map(({ name }) => (
<>
<CloudBackupProvider application={application} providerName={name} />
<CloudBackupProvider
application={application}
providerName={name}
/>
<HorizontalSeparator classes={'mt-3 mb-3'} />
</>
))}

View File

@@ -1,5 +1,4 @@
import { Dropdown, DropdownItem } from '@/components/Dropdown';
import { IconType } from '@/components/Icon';
import { FeatureIdentifier, PrefKey } from '@standardnotes/snjs';
import {
PreferencesGroup,
@@ -27,31 +26,6 @@ type EditorOption = DropdownItem & {
value: FeatureIdentifier | 'plain-editor';
};
export const getIconAndTintForEditor = (
identifier: FeatureIdentifier | undefined
): [IconType, number] => {
switch (identifier) {
case FeatureIdentifier.BoldEditor:
case FeatureIdentifier.PlusEditor:
return ['rich-text', 1];
case FeatureIdentifier.MarkdownBasicEditor:
case FeatureIdentifier.MarkdownMathEditor:
case FeatureIdentifier.MarkdownMinimistEditor:
case FeatureIdentifier.MarkdownProEditor:
return ['markdown', 2];
case FeatureIdentifier.TokenVaultEditor:
return ['authenticator', 6];
case FeatureIdentifier.SheetsEditor:
return ['spreadsheets', 5];
case FeatureIdentifier.TaskEditor:
return ['tasks', 3];
case FeatureIdentifier.CodeEditor:
return ['code', 4];
default:
return ['plain-text', 1];
}
};
const makeEditorDefault = (
application: WebApplication,
component: SNComponent,
@@ -103,7 +77,8 @@ export const Defaults: FunctionComponent<Props> = ({ application }) => {
.componentsForArea(ComponentArea.Editor)
.map((editor): EditorOption => {
const identifier = editor.package_info.identifier;
const [iconType, tint] = getIconAndTintForEditor(identifier);
const [iconType, tint] =
application.iconsController.getIconAndTintForEditor(identifier);
return {
label: editor.name,

View File

@@ -1,4 +1,4 @@
import { Icon, IconType } from '@/components/Icon';
import { Icon } from '@/components/Icon';
import {
Disclosure,
DisclosureButton,
@@ -7,6 +7,7 @@ import {
import { FunctionComponent } from 'preact';
import { MouseEventHandler } from 'react';
import { useState, useRef, useEffect } from 'preact/hooks';
import { IconType } from '@standardnotes/snjs';
const DisclosureIconButton: FunctionComponent<{
className?: string;

View File

@@ -15,6 +15,7 @@ import {
SNApplication,
NoteGroupController,
removeFromArray,
IconsController,
} from '@standardnotes/snjs';
type WebServices = {
@@ -39,6 +40,7 @@ export class WebApplication extends SNApplication {
private webServices!: WebServices;
public noteControllerGroup: NoteGroupController;
private webEventObservers: WebEventObserver[] = [];
public iconsController: IconsController;
constructor(
deviceInterface: WebDeviceInterface,
@@ -64,6 +66,7 @@ export class WebApplication extends SNApplication {
);
deviceInterface.setApplication(this);
this.noteControllerGroup = new NoteGroupController(this);
this.iconsController = new IconsController();
}
/** @override */
@@ -76,6 +79,7 @@ export class WebApplication extends SNApplication {
}
this.webServices = {} as WebServices;
this.noteControllerGroup.deinit();
this.iconsController.deinit();
this.webEventObservers.length = 0;
/**
* Allow any pending renders to complete before destroying the global

View File

@@ -84,7 +84,7 @@
"@reach/tooltip": "^0.16.2",
"@standardnotes/components": "1.4.4",
"@standardnotes/features": "1.26.1",
"@standardnotes/snjs": "2.44.3",
"@standardnotes/snjs": "2.45.0",
"@standardnotes/settings": "^1.10.0",
"@standardnotes/sncrypto-web": "1.6.0",
"mobx": "^6.3.5",

View File

@@ -2636,10 +2636,10 @@
resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.10.0.tgz#b46d4805c9a1cfb3fa3d9b3915daae9dd5661789"
integrity sha512-FUy4RDI7nFnbOGAaX5wMvBz+6Yto5tXGXDAi7bnWALZByTIlN8bkFZ2CNk2skjpzeOxBjqhCRi+GRCcuMqZ70A==
"@standardnotes/settings@^1.11.0":
version "1.11.0"
resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.11.0.tgz#8bdb987a9ebb8b480ee9e32ad67c4e0ac7d9f0fe"
integrity sha512-4a4408m+afO00fsp/Pxcyx2N8BOWpvKHqv9u3PisKBy3xZTUdman7ah/KHPvUd7MFEYbY7frB/DoHN3tiokArQ==
"@standardnotes/settings@^1.11.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.11.1.tgz#62e0df52820534c67041c99ed7f2c3a277158f39"
integrity sha512-uZChaTlIV63fYn7ODzVd/IB0nvrgyo/DwVaNgkjjHd3doGYqBMzzdfhs0RT0Ffpy0LOQhLpLBYqyJAryl1c4EA==
"@standardnotes/sncrypto-common@^1.6.0":
version "1.6.0"
@@ -2655,16 +2655,16 @@
buffer "^6.0.3"
libsodium-wrappers "^0.7.9"
"@standardnotes/snjs@2.44.3":
version "2.44.3"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.44.3.tgz#bb16aca691d24f2a7fea6f94a929f0a241ebcfc0"
integrity sha512-54rjz1s+pDdB4fRS69QE3DLNbebC+1DCw/+j5/2EOqdelj+/Ce7bQyiNxwYj4cqE0Z0zYEeJl7oAxVecOBCQZw==
"@standardnotes/snjs@2.45.0":
version "2.45.0"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.45.0.tgz#d123434b959d279af2ffe00acf2e9e6784cf497c"
integrity sha512-gTlOG3wd4zYaBeReypQiz+ASEnVCKaB8kWtKF61nkV9j3vFgYh3krsvdhOi6lMXBk+CijEefeLhrmooOtv08Xg==
dependencies:
"@standardnotes/auth" "^3.15.3"
"@standardnotes/common" "^1.8.0"
"@standardnotes/domain-events" "^2.20.1"
"@standardnotes/features" "^1.26.1"
"@standardnotes/settings" "^1.11.0"
"@standardnotes/settings" "^1.11.1"
"@standardnotes/sncrypto-common" "^1.6.0"
"@svgr/babel-plugin-add-jsx-attribute@^5.4.0":