Merge branch 'develop' into feat/enable-websocket-connection

This commit is contained in:
Antonella Sgarlatta
2021-10-04 17:50:11 -03:00
committed by GitHub
31 changed files with 463 additions and 210 deletions

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0001 16.0001C7.91675 16.0001 6.07508 14.9334 5.00008 13.3334C5.02508 11.6667 8.33342 10.7501 10.0001 10.7501C11.6667 10.7501 14.9751 11.6667 15.0001 13.3334C13.9251 14.9334 12.0834 16.0001 10.0001 16.0001ZM10.0001 4.16675C10.6631 4.16675 11.299 4.43014 11.7679 4.89898C12.2367 5.36782 12.5001 6.00371 12.5001 6.66675C12.5001 7.32979 12.2367 7.96568 11.7679 8.43452C11.299 8.90336 10.6631 9.16675 10.0001 9.16675C9.33704 9.16675 8.70116 8.90336 8.23232 8.43452C7.76347 7.96568 7.50008 7.32979 7.50008 6.66675C7.50008 6.00371 7.76347 5.36782 8.23232 4.89898C8.70116 4.43014 9.33704 4.16675 10.0001 4.16675ZM10.0001 1.66675C8.90573 1.66675 7.8221 1.8823 6.81105 2.30109C5.80001 2.71987 4.88135 3.3337 4.10753 4.10753C2.54472 5.67033 1.66675 7.78995 1.66675 10.0001C1.66675 12.2102 2.54472 14.3298 4.10753 15.8926C4.88135 16.6665 5.80001 17.2803 6.81105 17.6991C7.8221 18.1179 8.90573 18.3334 10.0001 18.3334C12.2102 18.3334 14.3298 17.4554 15.8926 15.8926C17.4554 14.3298 18.3334 12.2102 18.3334 10.0001C18.3334 5.39175 14.5834 1.66675 10.0001 1.66675Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,3 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.49995 17.0167L2.32495 11.8417L4.68328 9.48332L7.49995 12.3083L15.7333 4.06665L18.0916 6.42498L7.49995 17.0167Z" />
</svg>

Before

Width:  |  Height:  |  Size: 230 B

After

Width:  |  Height:  |  Size: 207 B

View File

@@ -1,3 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.5001 5.83345L7.50008 15.8334L2.91675 11.2501L4.09175 10.0751L7.50008 13.4751L16.3251 4.65845L17.5001 5.83345Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 229 B

After

Width:  |  Height:  |  Size: 206 B

View File

@@ -1,6 +1,5 @@
import { observer } from 'mobx-react-lite';
import { AppState } from '@/ui_models/app_state';
import { PasswordWizardType } from '@/types';
import { WebApplication } from '@/ui_models/application';
import { User } from '@standardnotes/snjs/dist/@types/services/api/responses';
@@ -10,22 +9,12 @@ type Props = {
}
const User = observer(({
appState,
application,
}: Props) => {
const { server, closeAccountMenu } = appState.accountMenu;
appState,
application,
}: Props) => {
const { server } = appState.accountMenu;
const user = application.getUser();
const openPasswordWizard = () => {
closeAccountMenu();
application.presentPasswordWizard(PasswordWizardType.ChangePassword);
};
const openSessionsModal = () => {
closeAccountMenu();
appState.openSessionsModal();
};
return (
<div className="sk-panel-section">
{appState.sync.errorMessage && (
@@ -56,12 +45,6 @@ const User = observer(({
</div>
</div>
<div className="sk-panel-row" />
<a className="sk-a info sk-panel-row condensed" onClick={openPasswordWizard}>
Change Password
</a>
<a className="sk-a info sk-panel-row condensed" onClick={openSessionsModal}>
Manage Sessions
</a>
</div>
);
});

View File

@@ -6,11 +6,6 @@ import { ConfirmSignoutContainer } from '@/components/ConfirmSignoutModal';
import Authentication from '@/components/AccountMenu/Authentication';
import Footer from '@/components/AccountMenu/Footer';
import User from '@/components/AccountMenu/User';
import Encryption from '@/components/AccountMenu/Encryption';
import Protections from '@/components/AccountMenu/Protections';
import PasscodeLock from '@/components/AccountMenu/PasscodeLock';
import DataBackup from '@/components/AccountMenu/DataBackup';
import ErrorReporting from '@/components/AccountMenu/ErrorReporting';
import { useEffect } from 'preact/hooks';
type Props = {
@@ -51,25 +46,12 @@ const AccountMenu = observer(({ application, appState }: Props) => {
application={application}
appState={appState}
/>
{!showLogin && !showRegister && (
{!showLogin && !showRegister && user && (
<div>
{user && (
<User
application={application}
appState={appState}
/>
)}
<Encryption appState={appState} />
<Protections application={application} />
<PasscodeLock
<User
application={application}
appState={appState}
/>
<DataBackup
application={application}
appState={appState}
/>
<ErrorReporting appState={appState} />
</div>
)}
</div>

View File

@@ -9,9 +9,9 @@ const baseClass = `rounded px-4 py-1.75 font-bold text-sm fit-content`;
type ButtonType = 'normal' | 'primary' | 'danger';
const buttonClasses: { [type in ButtonType]: string } = {
normal: `${baseClass} bg-default color-text border-solid border-gray-300 border-1 focus:bg-contrast hover:bg-contrast`,
normal: `${baseClass} bg-default color-text border-neutral border-solid border-gray-300 border-1 focus:bg-contrast hover:bg-contrast`,
primary: `${baseClass} no-border bg-info color-info-contrast hover:brightness-130 focus:brightness-130`,
danger: `${baseClass} bg-default color-danger border-solid border-gray-300 border-1 focus:bg-contrast hover:bg-contrast`,
danger: `${baseClass} bg-default color-danger border-neutral border-solid border-gray-300 border-1 focus:bg-contrast hover:bg-contrast`,
};
export const Button: FunctionComponent<{

View File

@@ -28,13 +28,13 @@ export const DecoratedInput: FunctionalComponent<Props> = ({
autocomplete = false,
}) => {
const baseClasses =
'rounded py-1.5 px-3 text-input my-1 h-8 flex flex-row items-center';
'rounded py-1.5 px-3 text-input my-1 h-8 flex flex-row items-center bg-contrast';
const stateClasses = disabled
? 'no-border bg-grey-5'
? 'no-border'
: 'border-solid border-1 border-gray-300';
const classes = `${baseClasses} ${stateClasses} ${className}`;
const inputBaseClasses = 'w-full no-border color-black focus:shadow-none';
const inputBaseClasses = 'w-full no-border color-text focus:shadow-none bg-contrast';
const inputStateClasses = disabled ? 'overflow-ellipsis' : '';
return (
<div className={`${classes} focus-within:ring-info`}>

View File

@@ -28,6 +28,7 @@ import DownloadIcon from '../../icons/ic-download.svg';
import InfoIcon from '../../icons/ic-info.svg';
import CheckIcon from '../../icons/ic-check.svg';
import CheckBoldIcon from '../../icons/ic-check-bold.svg';
import AccountCircleIcon from '../../icons/ic-account-circle.svg';
import { toDirective } from './utils';
import { FunctionalComponent } from 'preact';
@@ -61,7 +62,8 @@ const ICONS = {
download: DownloadIcon,
info: InfoIcon,
check: CheckIcon,
"check-bold": CheckBoldIcon,
'check-bold': CheckBoldIcon,
'account-circle': AccountCircleIcon,
};
export type IconType = keyof typeof ICONS;

View File

@@ -11,9 +11,9 @@ export const Input: FunctionalComponent<Props> = ({
disabled = false,
text,
}) => {
const base = `rounded py-1.5 px-3 text-input my-1 h-8`;
const base = `rounded py-1.5 px-3 text-input my-1 h-8 bg-contrast`;
const stateClasses = disabled
? 'no-border bg-grey-5'
? 'no-border'
: 'border-solid border-1 border-gray-300';
const classes = `${base} ${stateClasses} ${className}`;
return (

View File

@@ -33,11 +33,11 @@ export const ModalDialogLabel: FunctionComponent<{
}> = ({ children, closeDialog }) => (
<AlertDialogLabel className="">
<div className="px-4 py-4 flex flex-row items-center">
<div className="flex-grow color-black text-lg font-bold">{children}</div>
<div className="flex-grow color-text text-lg font-bold">{children}</div>
<IconButton
focusable={true}
title="Close"
className="color-grey-1 h-5 w-5"
className="color-neutral h-5 w-5"
icon="close"
onClick={() => closeDialog()}
/>
@@ -61,11 +61,11 @@ export const ModalDialogButtons: FunctionComponent = ({ children }) => (
<div className="px-4 py-4 flex flex-row justify-end items-center">
{children != undefined && Array.isArray(children)
? children.map((child, idx, arr) => (
<>
{child}
{idx < arr.length - 1 ? <div className="min-w-3" /> : undefined}
</>
))
<>
{child}
{idx < arr.length - 1 ? <div className="min-w-3" /> : undefined}
</>
))
: children}
</div>
</>

View File

@@ -1,7 +1,7 @@
import { RoundIconButton } from '@/components/RoundIconButton';
import { TitleBar, Title } from '@/components/TitleBar';
import { FunctionComponent } from 'preact';
import { AccountPreferences, General, HelpAndFeedback, Security } from './panes';
import { AccountPreferences, HelpAndFeedback, Listed, General, Security } from './panes';
import { observer } from 'mobx-react-lite';
import { PreferencesMenu } from './PreferencesMenu';
import { PreferencesMenuView } from './PreferencesMenuView';
@@ -41,7 +41,7 @@ const PaneSelector: FunctionComponent<
/>
);
case 'listed':
return null;
return <Listed application={props.application} />;
case 'shortcuts':
return null;
case 'accessibility':

View File

@@ -14,14 +14,15 @@ export const Text: FunctionComponent<{ className?: string }> = ({
}) => <p className={`${className} text-xs`}>{children}</p>;
const buttonClasses = `block bg-default color-text rounded border-solid \
border-1 border-gray-300 px-4 py-1.75 font-bold text-sm fit-content mt-3 \
focus:bg-contrast hover:bg-contrast `;
border-1 border-gray-300 px-4 py-1.75 font-bold text-sm fit-content \
focus:bg-contrast hover:bg-contrast border-neutral`;
export const LinkButton: FunctionComponent<{ label: string; link: string }> = ({
label,
link,
}) => (
<a target="_blank" className={buttonClasses} href={link}>
export const LinkButton: FunctionComponent<{
label: string;
link: string;
className?: string;
}> = ({ label, link, className }) => (
<a target="_blank" className={`${className} ${buttonClasses}`} href={link}>
{label}
</a>
);

View File

@@ -15,7 +15,7 @@ export const MenuItem: FunctionComponent<Props> = ({
onClick,
}) => (
<div
className={`preferences-menu-item ${selected ? 'selected' : ''}`}
className={`preferences-menu-item select-none ${selected ? 'selected' : ''}`}
onClick={(e) => {
e.preventDefault();
onClick();

View File

@@ -1,7 +1,7 @@
import { FunctionComponent } from 'preact';
export const PreferencesPane: FunctionComponent = ({ children }) => (
<div className="color-black flex-grow flex flex-row overflow-y-auto min-h-0">
<div className="color-foreground flex-grow flex flex-row overflow-y-auto min-h-0">
<div className="flex-grow flex flex-col py-6 items-center">
<div className="w-125 max-w-125 flex flex-col">
{children != undefined && Array.isArray(children)

View File

@@ -3,6 +3,7 @@ import { AppState } from '@/ui_models/app_state';
import { FunctionComponent } from 'preact';
import { PreferencesPane } from '../components';
import { ErrorReporting } from './general-segments';
import { Tools } from './general-segments/Tools';
interface GeneralProps {
appState: AppState;
@@ -11,6 +12,7 @@ interface GeneralProps {
export const General: FunctionComponent<GeneralProps> = (props) => (
<PreferencesPane>
<Tools application={props.application} />
<ErrorReporting appState={props.appState} />
</PreferencesPane>
);

View File

@@ -52,7 +52,11 @@ export const HelpAndFeedback: FunctionComponent = () => (
</PreferencesSegment>
<PreferencesSegment>
<Subtitle>Cant find your question here?</Subtitle>
<LinkButton label="Open FAQ" link="https://standardnotes.com/help" />
<LinkButton
className="mt-3"
label="Open FAQ"
link="https://standardnotes.com/help"
/>
</PreferencesSegment>
</PreferencesGroup>
<PreferencesGroup>
@@ -68,6 +72,7 @@ export const HelpAndFeedback: FunctionComponent = () => (
before advocating for a feature request.
</Text>
<LinkButton
className="mt-3"
label="Go to the forum"
link="https://forum.standardnotes.org/"
/>
@@ -82,6 +87,7 @@ export const HelpAndFeedback: FunctionComponent = () => (
group for discussions on security, themes, editors and more.
</Text>
<LinkButton
className="mt-3"
link="https://standardnotes.com/slack"
label="Join our Slack group"
/>
@@ -93,7 +99,7 @@ export const HelpAndFeedback: FunctionComponent = () => (
<Text>
Send an email to help@standardnotes.com and well sort it out.
</Text>
<LinkButton link="mailto: help@standardnotes.com" label="Email us" />
<LinkButton className="mt-3" link="mailto: help@standardnotes.com" label="Email us" />
</PreferencesSegment>
</PreferencesGroup>
</PreferencesPane>

View File

@@ -0,0 +1,116 @@
import {
PreferencesGroup,
PreferencesPane,
PreferencesSegment,
Title,
Subtitle,
Text,
LinkButton,
} from '../components';
import { observer } from 'mobx-react-lite';
import { WebApplication } from '@/ui_models/application';
import { ContentType, SNComponent } from '@standardnotes/snjs';
import { SNItem } from '@standardnotes/snjs/dist/@types/models/core/item';
import { useCallback, useEffect, useState } from 'preact/hooks';
import { BlogItem } from './listed/BlogItem';
type Props = {
application: WebApplication;
};
export const Listed = observer(({ application }: Props) => {
const [items, setItems] = useState<SNComponent[]>([]);
const [isDeleting, setIsDeleting] = useState(false);
const reloadItems = useCallback(() => {
const components = application
.getItems(ContentType.ActionsExtension)
.filter(
(item) => (item as SNComponent).package_info?.name === 'Listed'
) as SNComponent[];
setItems(components);
}, [application]);
useEffect(() => {
reloadItems();
}, [reloadItems]);
const disconnectListedBlog = (item: SNItem) => {
return new Promise((resolve, reject) => {
setIsDeleting(true);
application
.deleteItem(item)
.then(() => {
reloadItems();
setIsDeleting(false);
resolve(true);
})
.catch((err) => {
application.alertService.alert(err);
setIsDeleting(false);
console.error(err);
reject(err);
});
});
};
return (
<PreferencesPane>
{items.length > 0 && (
<PreferencesGroup>
<PreferencesSegment>
<Title>
Your {items.length === 1 ? 'Blog' : 'Blogs'} on Listed
</Title>
<div className="h-2 w-full" />
{items.map((item, index, array) => {
return (
<BlogItem
item={item}
showSeparator={index !== array.length - 1}
disabled={isDeleting}
disconnect={disconnectListedBlog}
key={item.uuid}
application={application}
/>
);
})}
</PreferencesSegment>
</PreferencesGroup>
)}
<PreferencesGroup>
<PreferencesSegment>
<Title>About Listed</Title>
<div className="h-2 w-full" />
<Subtitle>What is Listed?</Subtitle>
<Text>
Listed is a free blogging platform that allows you to create a
public journal published directly from your notes.{' '}
<a
target="_blank"
href="https://listed.to"
rel="noreferrer noopener"
>
Learn more
</a>
</Text>
</PreferencesSegment>
{items.length === 0 ? (
<PreferencesSegment>
<Subtitle>How to get started?</Subtitle>
<Text>
First, youll need to sign up for Listed. Once you have your
Listed account, follow the instructions to connect it with your
Standard Notes account.
</Text>
<LinkButton
className="min-w-20 mt-3"
link="https://listed.to"
label="Get started"
/>
</PreferencesSegment>
) : null}
</PreferencesGroup>
</PreferencesPane>
);
});

View File

@@ -0,0 +1,86 @@
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
import { Switch } from '@/components/Switch';
import {
PreferencesGroup,
PreferencesSegment,
Subtitle,
Text,
Title,
} from '@/preferences/components';
import { WebApplication } from '@/ui_models/application';
import { PrefKey } from '@standardnotes/snjs';
import { observer } from 'mobx-react-lite';
import { FunctionalComponent } from 'preact';
import { useState } from 'preact/hooks';
type Props = {
application: WebApplication;
};
export const Tools: FunctionalComponent<Props> = observer(
({ application }: Props) => {
const [monospaceFont, setMonospaceFont] = useState(() =>
application.getPreference(PrefKey.EditorMonospaceEnabled)
);
const [marginResizers, setMarginResizers] = useState(() =>
application.getPreference(PrefKey.EditorResizersEnabled)
);
const [spellcheck, setSpellcheck] = useState(() =>
application.getPreference(PrefKey.EditorSpellcheck)
);
const toggleMonospaceFont = () => {
setMonospaceFont(!monospaceFont);
application.setPreference(PrefKey.EditorMonospaceEnabled, !monospaceFont);
};
const toggleMarginResizers = () => {
setMarginResizers(!marginResizers);
application.setPreference(PrefKey.EditorResizersEnabled, !marginResizers);
};
const toggleSpellcheck = () => {
setSpellcheck(!spellcheck);
application.setPreference(PrefKey.EditorSpellcheck, !spellcheck);
};
return (
<PreferencesGroup>
<PreferencesSegment>
<Title>Tools</Title>
<div className="mt-2">
<div className="flex items-center justify-between">
<div className="flex flex-col">
<Subtitle>Monospace Font</Subtitle>
<Text>Toggles the font style in the Plain Text editor.</Text>
</div>
<Switch onChange={toggleMonospaceFont} checked={monospaceFont} />
</div>
<HorizontalSeparator classes="mt-5 mb-3" />
<div className="flex items-center justify-between">
<div className="flex flex-col">
<Subtitle>Margin Resizers</Subtitle>
<Text>Allows left and right editor margins to be resized.</Text>
</div>
<Switch
onChange={toggleMarginResizers}
checked={marginResizers}
/>
</div>
<HorizontalSeparator classes="mt-5 mb-3" />
<div className="flex items-center justify-between">
<div className="flex flex-col">
<Subtitle>Spellcheck</Subtitle>
<Text>
May degrade performance, especially with long notes. Available
in the Plain Text editor and most specialty editors.
</Text>
</div>
<Switch onChange={toggleSpellcheck} checked={spellcheck} />
</div>
</div>
</PreferencesSegment>
</PreferencesGroup>
);
}
);

View File

@@ -1 +1,2 @@
export * from './ErrorReporting';
export * from './Tools';

View File

@@ -1,4 +1,5 @@
export * from './HelpFeedback';
export * from './Security';
export * from './AccountPreferences';
export * from './Listed';
export * from './General';

View File

@@ -0,0 +1,110 @@
import { Button } from '@/components/Button';
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
import { LinkButton, Subtitle } from '@/preferences/components';
import { WebApplication } from '@/ui_models/application';
import {
Action,
ButtonType,
SNActionsExtension,
SNComponent,
SNItem,
} from '@standardnotes/snjs';
import { FunctionalComponent } from 'preact';
import { useEffect, useState } from 'preact/hooks';
type Props = {
item: SNComponent;
showSeparator: boolean;
disabled: boolean;
disconnect: (item: SNItem) => Promise<unknown>;
application: WebApplication;
};
export const BlogItem: FunctionalComponent<Props> = ({
item,
showSeparator,
disabled,
disconnect,
application,
}) => {
const [actions, setActions] = useState<Action[] | undefined>([]);
const [isLoadingActions, setIsLoadingActions] = useState(false);
const [isDisconnecting, setIsDisconnecting] = useState(false);
useEffect(() => {
const loadActions = async () => {
setIsLoadingActions(true);
application.actionsManager
.loadExtensionInContextOfItem(item as SNActionsExtension, item)
.then((extension) => {
setActions(extension?.actions);
})
.catch((err) => application.alertService.alert(err))
.finally(() => {
setIsLoadingActions(false);
});
};
if (!actions || actions.length === 0) loadActions();
}, [application.actionsManager, application.alertService, item, actions]);
const handleDisconnect = () => {
setIsDisconnecting(true);
application.alertService
.confirm(
'Disconnecting will result in loss of access to your blog. Ensure your Listed author key is backed up before uninstalling.',
`Disconnect blog "${item?.name}"?`,
'Disconnect',
ButtonType.Danger
)
.then(async (shouldDisconnect) => {
if (shouldDisconnect) {
await disconnect(item as SNItem);
}
})
.catch((err) => {
console.error(err);
application.alertService.alert(err);
})
.finally(() => {
setIsDisconnecting(false);
});
};
return (
<>
<Subtitle>{item?.name}</Subtitle>
<div className="flex">
{isLoadingActions ? (
<div className="sk-spinner small info"></div>
) : null}
{actions && actions?.length > 0 ? (
<>
<LinkButton
className="mr-2"
label="Open Blog"
link={
actions?.find((action: Action) => action.label === 'Open Blog')
?.url || ''
}
/>
<LinkButton
className="mr-2"
label="Settings"
link={
actions?.find((action: Action) => action.label === 'Settings')
?.url || ''
}
/>
<Button
type="danger"
label={isDisconnecting ? 'Disconnecting...' : 'Disconnect'}
disabled={disabled}
onClick={handleDisconnect}
/>
</>
) : null}
</div>
{showSeparator && <HorizontalSeparator classes="mt-5 mb-3" />}
</>
);
};

View File

@@ -15,7 +15,7 @@ const EncryptionEnabled: FunctionComponent<{ appState: AppState }> = observer(({
const archived = formatCount(count.archived, 'archived notes');
const deleted = formatCount(count.deleted, 'trashed notes');
const checkIcon = <Icon className="success min-w-5 min-h-5" type="check-bold" />;
const checkIcon = <Icon className="success min-w-4 min-h-4" type="check-bold" />;
const noteIcon = <Icon type="rich-text" className="min-w-5 min-h-5" />;
const tagIcon = <Icon type="hashtag" className="min-w-5 min-h-5" />;
const archiveIcon = <Icon type="archive" className="min-w-5 min-h-5" />;

View File

@@ -16,9 +16,8 @@ const DisclosureIconButton: FunctionComponent<{
<DisclosureButton
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
className={`no-border cursor-pointer bg-transparent hover:brightness-130 p-0 ${
className ?? ''
}`}
className={`no-border cursor-pointer bg-transparent hover:brightness-130 p-0 ${className ?? ''
}`}
>
<Icon type={icon} />
</DisclosureButton>
@@ -58,7 +57,7 @@ export const AuthAppInfoTooltip: FunctionComponent = () => {
/>
<DisclosurePanel>
<div
className={`bg-black color-white text-center rounded shadow-overlay
className={`bg-inverted-default color-inverted-default text-center rounded shadow-overlay
py-1.5 px-2 absolute w-103 -top-10 -left-51`}
>
Some apps, like Google Authenticator, do not back up and restore

View File

@@ -3,5 +3,5 @@ import { FunctionComponent } from 'preact';
export const Bullet: FunctionComponent<{ className?: string }> = ({
className = '',
}) => (
<div className={`min-w-1 min-h-1 rounded-full bg-black ${className} mr-2`} />
<div className={`min-w-1 min-h-1 rounded-full bg-inverted-default ${className} mr-2`} />
);

View File

@@ -59,42 +59,6 @@
.sn-component(ng-if='self.note')
#editor-menu-bar.sk-app-bar.no-edges
.left
.sk-app-bar-item(
click-outside=`self.setMenuState('showOptionsMenu', false)`,
is-open='self.state.showOptionsMenu',
ng-class="{'selected' : self.state.showOptionsMenu}",
ng-click="self.toggleMenu('showOptionsMenu')"
)
.sk-label Options
.sk-menu-panel.dropdown-menu(ng-if='self.state.showOptionsMenu')
.sk-menu-panel-section
.sk-menu-panel-header
.sk-menu-panel-header-title Global Display
menu-row(
action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMonospace)"
circle="self.state.monospaceFont ? 'success' : 'neutral'",
desc="'Toggles the font style for the default editor'",
disabled='self.state.editorComponent',
label="'Monospace Font'",
subtitle="self.state.editorComponent ? 'Not available with editor extensions' : null"
)
menu-row(
action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeySpellcheck)"
circle="self.state.spellcheck ? 'success' : 'neutral'",
desc="'Toggles spellcheck for the default editor'",
disabled='self.state.editorComponent',
label="'Spellcheck'",
subtitle=`
self.state.editorComponent
? 'Not available with editor extensions'
: (self.state.isDesktop ? 'May degrade editor performance' : null)
`)
menu-row(
action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMarginResizers)"
circle="self.state.marginResizersEnabled ? 'success' : 'neutral'",
desc="'Allows for editor left and right margins to be resized'",
label="'Margin Resizers'"
)
.sk-app-bar-item(
click-outside=`self.setMenuState('showEditorMenu', false)`
is-open='self.state.showEditorMenu',

View File

@@ -1,6 +1,4 @@
import {
STRING_SAVING_WHILE_DOCUMENT_HIDDEN,
} from './../../strings';
import { STRING_SAVING_WHILE_DOCUMENT_HIDDEN } from './../../strings';
import { Editor } from '@/ui_models/editor';
import { WebApplication } from '@/ui_models/application';
import { PanelPuppet, WebDirective } from '@/types';
@@ -61,7 +59,6 @@ type EditorState = {
isDesktop?: boolean;
syncTakingTooLong: boolean;
showActionsMenu: boolean;
showOptionsMenu: boolean;
showEditorMenu: boolean;
showHistoryMenu: boolean;
spellcheck: boolean;
@@ -202,7 +199,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
});
this.autorun(() => {
this.setState({
showProtectedWarning: this.appState.notes.showProtectedWarning
showProtectedWarning: this.appState.notes.showProtectedWarning,
});
});
}
@@ -216,7 +213,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
spellcheck: true,
syncTakingTooLong: false,
showActionsMenu: false,
showOptionsMenu: false,
showEditorMenu: false,
showHistoryMenu: false,
noteStatus: undefined,
@@ -272,11 +268,11 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
async handleEditorNoteChange() {
this.cancelPendingSetStatus();
const note = this.editor.note;
const showProtectedWarning = note.protected && !this.application.hasProtectionSources();
const showProtectedWarning =
note.protected && !this.application.hasProtectionSources();
this.setShowProtectedWarning(showProtectedWarning);
await this.setState({
showActionsMenu: false,
showOptionsMenu: false,
showEditorMenu: false,
showHistoryMenu: false,
noteStatus: undefined,
@@ -364,12 +360,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
}
closeAllMenus(exclude?: string) {
const allMenus = [
'showOptionsMenu',
'showEditorMenu',
'showActionsMenu',
'showHistoryMenu',
];
const allMenus = ['showEditorMenu', 'showActionsMenu', 'showHistoryMenu'];
const menuState: any = {};
for (const candidate of allMenus) {
if (candidate !== exclude) {
@@ -591,7 +582,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
}
clickedTextArea() {
this.setMenuState('showOptionsMenu', false);
this.closeAllMenus();
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
@@ -607,12 +598,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
this.lastEditorFocusEventSource = undefined;
}
selectedMenuItem(hide: boolean) {
if (hide) {
this.setMenuState('showOptionsMenu', false);
}
}
setShowProtectedWarning(show: boolean) {
this.appState.notes.setShowProtectedWarning(show);
}
@@ -757,13 +742,10 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
/** @components */
registerComponentHandler() {
this.unregisterComponent = this.application.componentManager!.registerHandler(
{
this.unregisterComponent =
this.application.componentManager!.registerHandler({
identifier: 'editor',
areas: [
ComponentArea.EditorStack,
ComponentArea.Editor,
],
areas: [ComponentArea.EditorStack, ComponentArea.Editor],
contextRequestHandler: (componentUuid) => {
const currentEditor = this.state.editorComponent;
if (
@@ -778,8 +760,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
this.closeAllMenus();
}
},
}
);
});
}
async reloadStackComponents() {
@@ -809,9 +790,8 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
}
async toggleStackComponentForCurrentItem(component: SNComponent) {
const hidden = this.application.componentManager!.isComponentHidden(
component
);
const hidden =
this.application.componentManager!.isComponentHidden(component);
if (hidden || !component.active) {
this.application.componentManager!.setComponentHidden(component, false);
await this.associateComponentWithCurrentNote(component);
@@ -844,16 +824,14 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
}
registerKeyboardShortcuts() {
this.removeTrashKeyObserver = this.application
.io
.addKeyObserver({
key: KeyboardKey.Backspace,
notTags: ['INPUT', 'TEXTAREA'],
modifiers: [KeyboardModifier.Meta],
onKeyDown: () => {
this.deleteNote(false);
},
});
this.removeTrashKeyObserver = this.application.io.addKeyObserver({
key: KeyboardKey.Backspace,
notTags: ['INPUT', 'TEXTAREA'],
modifiers: [KeyboardModifier.Meta],
onKeyDown: () => {
this.deleteNote(false);
},
});
}
setScrollPosition() {
@@ -883,39 +861,37 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
const editor = document.getElementById(
ElementIds.NoteTextEditor
)! as HTMLInputElement;
this.removeTabObserver = this.application
.io
.addKeyObserver({
element: editor,
key: KeyboardKey.Tab,
onKeyDown: (event) => {
if (document.hidden || this.note.locked || event.shiftKey) {
return;
}
event.preventDefault();
/** Using document.execCommand gives us undo support */
const insertSuccessful = document.execCommand(
'insertText',
false,
'\t'
);
if (!insertSuccessful) {
/** document.execCommand works great on Chrome/Safari but not Firefox */
const start = editor.selectionStart!;
const end = editor.selectionEnd!;
const spaces = ' ';
/** Insert 4 spaces */
editor.value =
editor.value.substring(0, start) +
spaces +
editor.value.substring(end);
/** Place cursor 4 spaces away from where the tab key was pressed */
editor.selectionStart = editor.selectionEnd = start + 4;
}
this.editorValues.text = editor.value;
this.save(this.note, copyEditorValues(this.editorValues), true);
},
});
this.removeTabObserver = this.application.io.addKeyObserver({
element: editor,
key: KeyboardKey.Tab,
onKeyDown: (event) => {
if (document.hidden || this.note.locked || event.shiftKey) {
return;
}
event.preventDefault();
/** Using document.execCommand gives us undo support */
const insertSuccessful = document.execCommand(
'insertText',
false,
'\t'
);
if (!insertSuccessful) {
/** document.execCommand works great on Chrome/Safari but not Firefox */
const start = editor.selectionStart!;
const end = editor.selectionEnd!;
const spaces = ' ';
/** Insert 4 spaces */
editor.value =
editor.value.substring(0, start) +
spaces +
editor.value.substring(end);
/** Place cursor 4 spaces away from where the tab key was pressed */
editor.selectionStart = editor.selectionEnd = start + 4;
}
this.editorValues.text = editor.value;
this.save(this.note, copyEditorValues(this.editorValues), true);
},
});
editor.addEventListener('scroll', this.setScrollPosition);
editor.addEventListener('input', this.resetScrollPosition);

View File

@@ -1,28 +1,37 @@
.sn-component
#footer-bar.sk-app-bar.no-edges.no-bottom-edge
.left
.sk-app-bar-item(
.sk-app-bar-item.ml-0(
click-outside='ctrl.clickOutsideAccountMenu()',
is-open='ctrl.showAccountMenu',
ng-click='ctrl.accountMenuPressed()'
)
.sk-app-bar-item-column
.sk-circle.small(
ng-class="ctrl.hasError ? 'danger' : (ctrl.user ? 'info' : 'neutral')"
)
.w-8.h-full.flex.items-center.justify-center.cursor-pointer.rounded-full(
ng-class="ctrl.showAccountMenu ? 'bg-border' : '' "
)
.w-5.h-5(
ng-class="ctrl.hasError ? 'danger' : (ctrl.user ? 'info' : 'neutral')"
)
.sk-app-bar-item-column
.sk-label.title(ng-class='{red: ctrl.hasError}') Account
icon(
type="account-circle"
class-name="hover:color-info w-5 h-5 max-h-5"
)
account-menu(
ng-click='$event.stopPropagation()',
app-state='ctrl.appState'
application='ctrl.application'
ng-if='ctrl.showAccountMenu',
)
.sk-app-bar-item(
.sk-app-bar-item.ml-0-important(
ng-click='ctrl.clickPreferences()'
ng-if='ctrl.appState.enableUnfinishedFeatures'
)
.sk-label.title Preferences
.w-8.h-full.flex.items-center.justify-center.cursor-pointer
.h-5
icon(
type="tune"
class-name="rounded hover:color-info"
)
.sk-app-bar-item
a.no-decoration.sk-label.title(
href='https://standardnotes.com/help',

View File

@@ -34,17 +34,6 @@
border-bottom: 2px solid var(--sn-stylekit-info-color);
}
}
svg {
width: 12px;
height: 12px;
fill: var(--sn-stylekit-secondary-foreground-color);
&:hover {
fill: var(--sn-stylekit-info-color) !important;
color: var(--sn-stylekit-info-color) !important;
}
}
}
#account-switcher-icon {

View File

@@ -115,7 +115,7 @@ p {
background-color: var(--sn-stylekit-background-color);
}
$footer-height: 32px;
$footer-height: 2rem;
#resizer-overlay {
position: absolute;

View File

@@ -154,6 +154,13 @@
@extend .font-bold;
}
.ml-0-important {
margin-left: 0rem !important;
}
.ml-3 {
margin-left: 0.75rem;
}
.ml-4 {
margin-left: 1rem;
}
@@ -222,10 +229,22 @@
min-height: 1.5rem;
}
.max-h-5 {
max-height: 1.25rem;
}
.border-danger {
border-color: var(--sn-stylekit-danger-color);
}
.bg-inverted-default {
background-color: var(--sn-stylekit-contrast-foreground-color);
}
.color-inverted-default {
color: var(--sn-stylekit-background-color);
}
.pt-1 {
padding-top: 0.25rem;
}
@@ -256,3 +275,7 @@
padding-top: 2.25rem;
padding-bottom: 2.25rem;
}
.select-none {
user-select: none;
}

View File

@@ -72,7 +72,7 @@
"@reach/dialog": "^0.13.0",
"@standardnotes/sncrypto-web": "1.5.2",
"@standardnotes/features": "1.6.1",
"@standardnotes/snjs": "2.14.6",
"@standardnotes/snjs": "2.14.8",
"mobx": "^6.3.2",
"mobx-react-lite": "^3.2.0",
"preact": "^10.5.12",