Merge branch 'release/10.2.0'
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
||||
DisclosureButton,
|
||||
DisclosurePanel,
|
||||
} from '@reach/disclosure';
|
||||
import { SNNote } from '@standardnotes/snjs/dist/@types';
|
||||
import { SNApplication, SNNote } from '@standardnotes/snjs/dist/@types';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { KeyboardModifier } from '@/services/ioService';
|
||||
import { FunctionComponent } from 'preact';
|
||||
@@ -35,6 +35,20 @@ const DeletePermanentlyButton = ({
|
||||
</button>
|
||||
);
|
||||
|
||||
const getWordCount = (text: string) => {
|
||||
if (text.trim().length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return text.split(/\s+/).length;
|
||||
};
|
||||
|
||||
const getParagraphCount = (text: string) => {
|
||||
if (text.trim().length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return text.replace(/\n$/gm, '').split(/\n/).length;
|
||||
};
|
||||
|
||||
const countNoteAttributes = (text: string) => {
|
||||
try {
|
||||
JSON.parse(text);
|
||||
@@ -44,12 +58,9 @@ const countNoteAttributes = (text: string) => {
|
||||
paragraphs: 'N/A',
|
||||
};
|
||||
} catch {
|
||||
const removeTags = text.replace(/<[^>]*>/g," ").replace(/\s+/g, ' ').trim();
|
||||
text = removeTags;
|
||||
|
||||
const characters = text.length;
|
||||
const words = text.split(" ")?.length;
|
||||
const paragraphs = text.replace(/\n$/gm, '').split(/\n/).length;
|
||||
const words = getWordCount(text);
|
||||
const paragraphs = getParagraphCount(text);
|
||||
|
||||
return {
|
||||
characters,
|
||||
@@ -73,7 +84,7 @@ const formatDate = (date: Date | undefined) => {
|
||||
return `${date.toDateString()} ${date.toLocaleTimeString()}`;
|
||||
};
|
||||
|
||||
const NoteAttributes: FunctionComponent<{ note: SNNote }> = ({ note }) => {
|
||||
const NoteAttributes: FunctionComponent<{ application: SNApplication, note: SNNote }> = ({ application, note }) => {
|
||||
const { words, characters, paragraphs } = useMemo(
|
||||
() => countNoteAttributes(note.text),
|
||||
[note.text]
|
||||
@@ -94,9 +105,12 @@ const NoteAttributes: FunctionComponent<{ note: SNNote }> = ({ note }) => {
|
||||
[note.created_at]
|
||||
);
|
||||
|
||||
const editor = application.componentManager.editorForNote(note);
|
||||
const format = editor?.package_info?.file_type || 'txt';
|
||||
|
||||
return (
|
||||
<div className="px-3 pt-1.5 pb-1 text-xs color-neutral font-medium">
|
||||
{typeof words === 'number' ? (
|
||||
{typeof words === 'number' && (format === 'txt' || format === 'md') ? (
|
||||
<>
|
||||
<div className="mb-1">
|
||||
{words} words · {characters} characters · {paragraphs} paragraphs
|
||||
@@ -473,7 +487,7 @@ export const NotesOptions = observer(
|
||||
{notes.length === 1 ? (
|
||||
<>
|
||||
<div className="min-h-1px my-2 bg-border"></div>
|
||||
<NoteAttributes note={notes[0]} />
|
||||
<NoteAttributes application={application} note={notes[0]} />
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
|
||||
@@ -180,15 +180,16 @@ const QuickSettingsMenu: FunctionComponent<MenuProps> = observer(
|
||||
}
|
||||
};
|
||||
|
||||
const handleQuickSettingsKeyDown: JSXInternal.KeyboardEventHandler<HTMLDivElement> =
|
||||
(event) => {
|
||||
quickSettingsKeyDownHandler(
|
||||
closeQuickSettingsMenu,
|
||||
event,
|
||||
quickSettingsMenuRef,
|
||||
themesMenuOpen
|
||||
);
|
||||
};
|
||||
const handleQuickSettingsKeyDown: JSXInternal.KeyboardEventHandler<
|
||||
HTMLDivElement
|
||||
> = (event) => {
|
||||
quickSettingsKeyDownHandler(
|
||||
closeQuickSettingsMenu,
|
||||
event,
|
||||
quickSettingsMenuRef,
|
||||
themesMenuOpen
|
||||
);
|
||||
};
|
||||
|
||||
const handlePanelKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (
|
||||
event
|
||||
@@ -222,54 +223,56 @@ const QuickSettingsMenu: FunctionComponent<MenuProps> = observer(
|
||||
<div className="px-3 mt-1 mb-2 font-semibold color-text uppercase">
|
||||
Quick Settings
|
||||
</div>
|
||||
<Disclosure open={themesMenuOpen} onChange={toggleThemesMenu}>
|
||||
<DisclosureButton
|
||||
onKeyDown={handleBtnKeyDown}
|
||||
onBlur={closeOnBlur}
|
||||
ref={themesButtonRef}
|
||||
className="sn-dropdown-item justify-between focus:bg-info-backdrop focus:shadow-none"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<Icon type="themes" className="color-neutral mr-2" />
|
||||
Themes
|
||||
</div>
|
||||
<Icon type="chevron-right" className="color-neutral" />
|
||||
</DisclosureButton>
|
||||
<DisclosurePanel
|
||||
onBlur={closeOnBlur}
|
||||
ref={themesMenuRef}
|
||||
onKeyDown={handlePanelKeyDown}
|
||||
style={{
|
||||
...themesMenuPosition,
|
||||
}}
|
||||
className={`${MENU_CLASSNAME} fixed sn-dropdown--animated`}
|
||||
>
|
||||
<div className="px-3 my-1 font-semibold color-text uppercase">
|
||||
Themes
|
||||
</div>
|
||||
<button
|
||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
|
||||
onClick={toggleDefaultTheme}
|
||||
{themes && themes.length ? (
|
||||
<Disclosure open={themesMenuOpen} onChange={toggleThemesMenu}>
|
||||
<DisclosureButton
|
||||
onKeyDown={handleBtnKeyDown}
|
||||
onBlur={closeOnBlur}
|
||||
ref={defaultThemeButtonRef}
|
||||
ref={themesButtonRef}
|
||||
className="sn-dropdown-item justify-between focus:bg-info-backdrop focus:shadow-none"
|
||||
>
|
||||
<div
|
||||
className={`pseudo-radio-btn ${
|
||||
defaultThemeOn ? 'pseudo-radio-btn--checked' : ''
|
||||
} mr-2`}
|
||||
></div>
|
||||
Default
|
||||
</button>
|
||||
{themes.map((theme) => (
|
||||
<ThemesMenuButton
|
||||
theme={theme}
|
||||
application={application}
|
||||
key={theme.uuid}
|
||||
<div className="flex items-center">
|
||||
<Icon type="themes" className="color-neutral mr-2" />
|
||||
Themes
|
||||
</div>
|
||||
<Icon type="chevron-right" className="color-neutral" />
|
||||
</DisclosureButton>
|
||||
<DisclosurePanel
|
||||
onBlur={closeOnBlur}
|
||||
ref={themesMenuRef}
|
||||
onKeyDown={handlePanelKeyDown}
|
||||
style={{
|
||||
...themesMenuPosition,
|
||||
}}
|
||||
className={`${MENU_CLASSNAME} fixed sn-dropdown--animated`}
|
||||
>
|
||||
<div className="px-3 my-1 font-semibold color-text uppercase">
|
||||
Themes
|
||||
</div>
|
||||
<button
|
||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
|
||||
onClick={toggleDefaultTheme}
|
||||
onBlur={closeOnBlur}
|
||||
/>
|
||||
))}
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
ref={defaultThemeButtonRef}
|
||||
>
|
||||
<div
|
||||
className={`pseudo-radio-btn ${
|
||||
defaultThemeOn ? 'pseudo-radio-btn--checked' : ''
|
||||
} mr-2`}
|
||||
></div>
|
||||
Default
|
||||
</button>
|
||||
{themes.map((theme) => (
|
||||
<ThemesMenuButton
|
||||
theme={theme}
|
||||
application={application}
|
||||
key={theme.uuid}
|
||||
onBlur={closeOnBlur}
|
||||
/>
|
||||
))}
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
) : null}
|
||||
{toggleableComponents.map((component) => (
|
||||
<Switch
|
||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
SNTheme,
|
||||
ComponentArea,
|
||||
CollectionSort,
|
||||
SNComponent,
|
||||
} from '@standardnotes/snjs';
|
||||
import template from './footer-view.pug';
|
||||
import { AppStateEvent, EventSource } from '@/ui_models/app_state';
|
||||
@@ -43,8 +42,6 @@ class FooterViewCtrl extends PureViewCtrl<
|
||||
}
|
||||
> {
|
||||
private $rootScope: ng.IRootScopeService;
|
||||
private themes: SNTheme[] = [];
|
||||
private toggleableComponents: SNComponent[] = [];
|
||||
private showSyncResolution = false;
|
||||
private unregisterComponent: any;
|
||||
private rootScopeListener2: any;
|
||||
@@ -76,8 +73,6 @@ class FooterViewCtrl extends PureViewCtrl<
|
||||
deinit() {
|
||||
for (const remove of this.observerRemovers) remove();
|
||||
this.observerRemovers.length = 0;
|
||||
this.themes.length = 0;
|
||||
this.toggleableComponents.length = 0;
|
||||
this.unregisterComponent();
|
||||
this.unregisterComponent = undefined;
|
||||
this.rootScopeListener2();
|
||||
@@ -276,30 +271,6 @@ class FooterViewCtrl extends PureViewCtrl<
|
||||
return !theme.errorDecrypting;
|
||||
}
|
||||
);
|
||||
|
||||
this.observerRemovers.push(
|
||||
this.application.streamItems(ContentType.Theme, async () => {
|
||||
const themes = this.application.getDisplayableItems(
|
||||
ContentType.Theme
|
||||
) as SNTheme[];
|
||||
this.themes = themes;
|
||||
})
|
||||
);
|
||||
|
||||
this.observerRemovers.push(
|
||||
this.application.streamItems(ContentType.Component, async () => {
|
||||
const toggleableComponents = (
|
||||
this.application.getDisplayableItems(
|
||||
ContentType.Component
|
||||
) as SNComponent[]
|
||||
).filter((component) =>
|
||||
[ComponentArea.EditorStack, ComponentArea.TagsList].includes(
|
||||
component.area
|
||||
)
|
||||
);
|
||||
this.toggleableComponents = toggleableComponents;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
registerComponentHandler() {
|
||||
@@ -400,11 +371,7 @@ class FooterViewCtrl extends PureViewCtrl<
|
||||
|
||||
quickSettingsPressed() {
|
||||
this.appState.accountMenu.closeAccountMenu();
|
||||
if (this.themes.length > 0 || this.toggleableComponents.length > 0) {
|
||||
this.appState.quickSettingsMenu.toggle();
|
||||
} else {
|
||||
this.appState.preferences.openPreferences();
|
||||
}
|
||||
this.appState.quickSettingsMenu.toggle();
|
||||
}
|
||||
|
||||
toggleSyncResolutionMenu() {
|
||||
|
||||
@@ -71,6 +71,8 @@
|
||||
|
||||
.sn-dropdown-popover {
|
||||
z-index: 3001;
|
||||
max-height: 40%;
|
||||
overflow: auto;
|
||||
|
||||
&[data-reach-listbox-popover] {
|
||||
background: var(--sn-stylekit-background-color);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "standard-notes-web",
|
||||
"version": "3.9.7",
|
||||
"version": "3.9.8",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -83,9 +83,9 @@
|
||||
"@reach/checkbox": "^0.16.0",
|
||||
"@reach/dialog": "^0.16.2",
|
||||
"@reach/listbox": "^0.16.2",
|
||||
"@standardnotes/features": "1.10.1",
|
||||
"@standardnotes/features": "1.10.2",
|
||||
"@standardnotes/sncrypto-web": "1.5.3",
|
||||
"@standardnotes/snjs": "2.19.6",
|
||||
"@standardnotes/snjs": "2.20.1",
|
||||
"mobx": "^6.3.5",
|
||||
"mobx-react-lite": "^3.2.2",
|
||||
"preact": "^10.5.15",
|
||||
|
||||
18
yarn.lock
18
yarn.lock
@@ -2587,10 +2587,10 @@
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.8.1"
|
||||
|
||||
"@standardnotes/features@1.10.1", "@standardnotes/features@^1.10.1":
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.10.1.tgz#5b5a59d2d69751ca040a270d04a08662b1591bfc"
|
||||
integrity sha512-fN7WyR8jeaAsDWkm4a6SSz3JZeaNfL+CkmWAYqxRI5XoXZlWy+kBVaYWaGe7vI4T6ncwdhxRTjE7mirG4PEQ6g==
|
||||
"@standardnotes/features@1.10.2", "@standardnotes/features@^1.10.2":
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.10.2.tgz#a0783f66c00e21cb7692edc0cea95ec25a0253a5"
|
||||
integrity sha512-Zh6EMjli4mL6jlXEhMyU3qYIKFJj5kuhbxtHXiErUGIDy+s1hHY+THFFO53Jdga2+8wgcATWlmSBY7dieVA8uA==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "3.8.3"
|
||||
"@standardnotes/common" "^1.2.1"
|
||||
@@ -2614,15 +2614,15 @@
|
||||
buffer "^6.0.3"
|
||||
libsodium-wrappers "^0.7.9"
|
||||
|
||||
"@standardnotes/snjs@2.19.6":
|
||||
version "2.19.6"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.19.6.tgz#a7bf568944f87ee83e39083ef6facc2ba0d56255"
|
||||
integrity sha512-OlImG2UHHgf4zo5QmMeeKb9xNoRWUVaxLdzfhpV95PQShp8Nrl4zsxrDKehh9d6zN5dM/iaxF897cI6bT9D1uw==
|
||||
"@standardnotes/snjs@2.20.1":
|
||||
version "2.20.1"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.20.1.tgz#4813adbfd16a1c373357bd4c7ece3085abf142b4"
|
||||
integrity sha512-wJILt7YerLFaZTKoIZaCkqSuvpVWoVKqCVjP0KNHrMknnbgMHZygHqrb9sr57JL1AcNCkAULEFS/kev2GYTG3Q==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.8.1"
|
||||
"@standardnotes/common" "^1.2.1"
|
||||
"@standardnotes/domain-events" "^2.5.1"
|
||||
"@standardnotes/features" "^1.10.1"
|
||||
"@standardnotes/features" "^1.10.2"
|
||||
"@standardnotes/settings" "^1.2.1"
|
||||
"@standardnotes/sncrypto-common" "^1.5.2"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user