Merge branch 'release/10.16.0'
This commit is contained in:
@@ -4,7 +4,6 @@ declare global {
|
||||
interface Window {
|
||||
bugsnagApiKey?: string;
|
||||
dashboardUrl?: string;
|
||||
defaultFilesHost: string;
|
||||
defaultSyncServer: string;
|
||||
devAccountEmail?: string;
|
||||
devAccountPassword?: string;
|
||||
@@ -30,7 +29,6 @@ import { isDev } from './utils';
|
||||
|
||||
const startApplication: StartApplication = async function startApplication(
|
||||
defaultSyncServerHost: string,
|
||||
defaultFilesHostHost: string,
|
||||
bridge: Bridge,
|
||||
enableUnfinishedFeatures: boolean,
|
||||
webSocketUrl: string
|
||||
@@ -40,7 +38,6 @@ const startApplication: StartApplication = async function startApplication(
|
||||
|
||||
const mainApplicationGroup = new ApplicationGroup(
|
||||
defaultSyncServerHost,
|
||||
defaultFilesHostHost,
|
||||
bridge,
|
||||
enableUnfinishedFeatures ? Runtime.Dev : Runtime.Prod,
|
||||
webSocketUrl
|
||||
@@ -75,7 +72,6 @@ const startApplication: StartApplication = async function startApplication(
|
||||
if (IsWebPlatform) {
|
||||
startApplication(
|
||||
window.defaultSyncServer,
|
||||
window.defaultFilesHost,
|
||||
new BrowserBridge(WebAppVersion),
|
||||
window.enabledUnfinishedFeatures,
|
||||
window.websocketUrl
|
||||
|
||||
@@ -24,23 +24,23 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
||||
({ application, appState, setMenuPane, closeMenu }) => {
|
||||
const [isSyncingInProgress, setIsSyncingInProgress] = useState(false);
|
||||
const [lastSyncDate, setLastSyncDate] = useState(
|
||||
formatLastSyncDate(application.getLastSyncDate() as Date)
|
||||
formatLastSyncDate(application.sync.getLastSyncDate() as Date)
|
||||
);
|
||||
|
||||
const doSynchronization = async () => {
|
||||
setIsSyncingInProgress(true);
|
||||
|
||||
application
|
||||
application.sync
|
||||
.sync({
|
||||
queueStrategy: SyncQueueStrategy.ForceSpawnNew,
|
||||
checkIntegrity: true,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.error) {
|
||||
if (res && (res as any).error) {
|
||||
throw new Error();
|
||||
} else {
|
||||
setLastSyncDate(
|
||||
formatLastSyncDate(application.getLastSyncDate() as Date)
|
||||
formatLastSyncDate(application.sync.getLastSyncDate() as Date)
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
removeFromArray,
|
||||
} from '@standardnotes/snjs';
|
||||
import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/constants';
|
||||
import { STRING_DEFAULT_FILE_ERROR } from '@/strings';
|
||||
import { alertDialog } from '@/services/alertService';
|
||||
import { WebAppEvent, WebApplication } from '@/ui_models/application';
|
||||
import { PureComponent } from '@/components/Abstract/PureComponent';
|
||||
@@ -51,17 +50,10 @@ export class ApplicationView extends PureComponent<Props, State> {
|
||||
appClass: '',
|
||||
challenges: [],
|
||||
};
|
||||
this.onDragDrop = this.onDragDrop.bind(this);
|
||||
this.onDragOver = this.onDragOver.bind(this);
|
||||
this.addDragDropHandlers();
|
||||
}
|
||||
|
||||
deinit() {
|
||||
(this.application as unknown) = undefined;
|
||||
window.removeEventListener('dragover', this.onDragOver, true);
|
||||
window.removeEventListener('drop', this.onDragDrop, true);
|
||||
(this.onDragDrop as unknown) = undefined;
|
||||
(this.onDragOver as unknown) = undefined;
|
||||
super.deinit();
|
||||
}
|
||||
|
||||
@@ -145,36 +137,11 @@ export class ApplicationView extends PureComponent<Props, State> {
|
||||
this.setState({ appClass });
|
||||
} else if (eventName === AppStateEvent.WindowDidFocus) {
|
||||
if (!(await this.application.isLocked())) {
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addDragDropHandlers() {
|
||||
/**
|
||||
* Disable dragging and dropping of files (but allow text) into main SN interface.
|
||||
* both 'dragover' and 'drop' are required to prevent dropping of files.
|
||||
* This will not prevent extensions from receiving drop events.
|
||||
*/
|
||||
window.addEventListener('dragover', this.onDragOver, true);
|
||||
window.addEventListener('drop', this.onDragDrop, true);
|
||||
}
|
||||
|
||||
onDragOver(event: DragEvent) {
|
||||
if (event.dataTransfer?.files.length) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
onDragDrop(event: DragEvent) {
|
||||
if (event.dataTransfer?.files.length) {
|
||||
event.preventDefault();
|
||||
void alertDialog({
|
||||
text: STRING_DEFAULT_FILE_ERROR,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async handleDemoSignInFromParams() {
|
||||
if (
|
||||
window.location.href.includes('demo') &&
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { MENU_MARGIN_FROM_APP_BORDER } from '@/constants';
|
||||
import {
|
||||
Disclosure,
|
||||
DisclosureButton,
|
||||
DisclosurePanel,
|
||||
} from '@reach/disclosure';
|
||||
import VisuallyHidden from '@reach/visually-hidden';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { Icon } from '../Icon';
|
||||
import { useCloseOnClickOutside } from '../utils';
|
||||
import { ChallengeReason, ContentType, SNFile } from '@standardnotes/snjs';
|
||||
import { confirmDialog } from '@/services/alertService';
|
||||
import { addToast, dismissToast, ToastType } from '@standardnotes/stylekit';
|
||||
import { parseFileName } from '@standardnotes/filepicker';
|
||||
import {
|
||||
PopoverFileItemAction,
|
||||
PopoverFileItemActionType,
|
||||
} from './PopoverFileItemAction';
|
||||
import { PopoverDragNDropWrapper } from './PopoverDragNDropWrapper';
|
||||
|
||||
type Props = {
|
||||
application: WebApplication;
|
||||
appState: AppState;
|
||||
onClickPreprocessing?: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const AttachedFilesButton: FunctionComponent<Props> = observer(
|
||||
({ application, appState, onClickPreprocessing }) => {
|
||||
const note = Object.values(appState.notes.selectedNotes)[0];
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [position, setPosition] = useState({
|
||||
top: 0,
|
||||
right: 0,
|
||||
});
|
||||
const [maxHeight, setMaxHeight] = useState<number | 'auto'>('auto');
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
useCloseOnClickOutside(containerRef, () => {
|
||||
setOpen(false);
|
||||
});
|
||||
|
||||
const [attachedFilesCount, setAttachedFilesCount] = useState(
|
||||
note ? application.items.getFilesForNote(note).length : 0
|
||||
);
|
||||
|
||||
const reloadAttachedFilesCount = useCallback(() => {
|
||||
setAttachedFilesCount(
|
||||
note ? application.items.getFilesForNote(note).length : 0
|
||||
);
|
||||
}, [application.items, note]);
|
||||
|
||||
useEffect(() => {
|
||||
const unregisterFileStream = application.streamItems(
|
||||
ContentType.File,
|
||||
() => {
|
||||
reloadAttachedFilesCount();
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unregisterFileStream();
|
||||
};
|
||||
}, [application, reloadAttachedFilesCount]);
|
||||
|
||||
const toggleAttachedFilesMenu = async () => {
|
||||
const rect = buttonRef.current?.getBoundingClientRect();
|
||||
if (rect) {
|
||||
const { clientHeight } = document.documentElement;
|
||||
const footerElementRect = document
|
||||
.getElementById('footer-bar')
|
||||
?.getBoundingClientRect();
|
||||
const footerHeightInPx = footerElementRect?.height;
|
||||
|
||||
if (footerHeightInPx) {
|
||||
setMaxHeight(
|
||||
clientHeight -
|
||||
rect.bottom -
|
||||
footerHeightInPx -
|
||||
MENU_MARGIN_FROM_APP_BORDER
|
||||
);
|
||||
}
|
||||
|
||||
setPosition({
|
||||
top: rect.bottom,
|
||||
right: document.body.clientWidth - rect.right,
|
||||
});
|
||||
|
||||
const newOpenState = !open;
|
||||
if (newOpenState && onClickPreprocessing) {
|
||||
await onClickPreprocessing();
|
||||
}
|
||||
|
||||
setOpen(newOpenState);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteFile = async (file: SNFile) => {
|
||||
const shouldDelete = await confirmDialog({
|
||||
text: `Are you sure you want to permanently delete "${file.nameWithExt}"?`,
|
||||
confirmButtonStyle: 'danger',
|
||||
});
|
||||
if (shouldDelete) {
|
||||
const deletingToastId = addToast({
|
||||
type: ToastType.Loading,
|
||||
message: `Deleting file "${file.nameWithExt}"...`,
|
||||
});
|
||||
await application.deleteItem(file);
|
||||
addToast({
|
||||
type: ToastType.Success,
|
||||
message: `Deleted file "${file.nameWithExt}"`,
|
||||
});
|
||||
dismissToast(deletingToastId);
|
||||
}
|
||||
};
|
||||
|
||||
const downloadFile = async (file: SNFile) => {
|
||||
appState.files.downloadFile(file);
|
||||
};
|
||||
|
||||
const attachFileToNote = async (file: SNFile) => {
|
||||
await application.items.associateFileWithNote(file, note);
|
||||
};
|
||||
|
||||
const detachFileFromNote = async (file: SNFile) => {
|
||||
await application.items.disassociateFileWithNote(file, note);
|
||||
};
|
||||
|
||||
const toggleFileProtection = async (file: SNFile) => {
|
||||
let result: SNFile | undefined;
|
||||
if (file.protected) {
|
||||
result = await application.protections.unprotectFile(file);
|
||||
} else {
|
||||
result = await application.protections.protectFile(file);
|
||||
}
|
||||
const isProtected = result ? result.protected : file.protected;
|
||||
return isProtected;
|
||||
};
|
||||
|
||||
const authorizeProtectedActionForFile = async (
|
||||
file: SNFile,
|
||||
challengeReason: ChallengeReason
|
||||
) => {
|
||||
const authorizedFiles =
|
||||
await application.protections.authorizeProtectedActionForFiles(
|
||||
[file],
|
||||
challengeReason
|
||||
);
|
||||
const isAuthorized =
|
||||
authorizedFiles.length > 0 && authorizedFiles.includes(file);
|
||||
return isAuthorized;
|
||||
};
|
||||
|
||||
const renameFile = async (file: SNFile, fileName: string) => {
|
||||
const { name, ext } = parseFileName(fileName);
|
||||
await application.items.renameFile(file, name, ext);
|
||||
};
|
||||
|
||||
const handleFileAction = async (action: PopoverFileItemAction) => {
|
||||
const file =
|
||||
action.type !== PopoverFileItemActionType.RenameFile
|
||||
? action.payload
|
||||
: action.payload.file;
|
||||
let isAuthorizedForAction = true;
|
||||
|
||||
if (
|
||||
file.protected &&
|
||||
action.type !== PopoverFileItemActionType.ToggleFileProtection
|
||||
) {
|
||||
isAuthorizedForAction = await authorizeProtectedActionForFile(
|
||||
file,
|
||||
ChallengeReason.AccessProtectedFile
|
||||
);
|
||||
}
|
||||
|
||||
if (!isAuthorizedForAction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case PopoverFileItemActionType.AttachFileToNote:
|
||||
await attachFileToNote(file);
|
||||
break;
|
||||
case PopoverFileItemActionType.DetachFileToNote:
|
||||
await detachFileFromNote(file);
|
||||
break;
|
||||
case PopoverFileItemActionType.DeleteFile:
|
||||
await deleteFile(file);
|
||||
break;
|
||||
case PopoverFileItemActionType.DownloadFile:
|
||||
await downloadFile(file);
|
||||
break;
|
||||
case PopoverFileItemActionType.ToggleFileProtection: {
|
||||
const isProtected = await toggleFileProtection(file);
|
||||
action.callback(isProtected);
|
||||
break;
|
||||
}
|
||||
case PopoverFileItemActionType.RenameFile:
|
||||
await renameFile(file, action.payload.name);
|
||||
break;
|
||||
}
|
||||
|
||||
application.sync.sync();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={containerRef}>
|
||||
<Disclosure open={open} onChange={toggleAttachedFilesMenu}>
|
||||
<DisclosureButton
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
setOpen(false);
|
||||
}
|
||||
}}
|
||||
ref={buttonRef}
|
||||
className={`sn-icon-button border-contrast ${
|
||||
attachedFilesCount > 0 ? 'py-1 px-3' : ''
|
||||
}`}
|
||||
>
|
||||
<VisuallyHidden>Attached files</VisuallyHidden>
|
||||
<Icon type="attachment-file" className="block" />
|
||||
{attachedFilesCount > 0 && (
|
||||
<span className="ml-2">{attachedFilesCount}</span>
|
||||
)}
|
||||
</DisclosureButton>
|
||||
<DisclosurePanel
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
setOpen(false);
|
||||
buttonRef.current?.focus();
|
||||
}
|
||||
}}
|
||||
ref={panelRef}
|
||||
style={{
|
||||
...position,
|
||||
maxHeight,
|
||||
}}
|
||||
className="sn-dropdown sn-dropdown--animated min-w-80 max-h-120 max-w-xs flex flex-col overflow-y-auto fixed"
|
||||
>
|
||||
{open && (
|
||||
<PopoverDragNDropWrapper
|
||||
application={application}
|
||||
appState={appState}
|
||||
note={note}
|
||||
fileActionHandler={handleFileAction}
|
||||
/>
|
||||
)}
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,195 @@
|
||||
import { ContentType, SNFile } from '@standardnotes/snjs';
|
||||
import { FilesIllustration } from '@standardnotes/stylekit';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { StateUpdater, useCallback, useEffect, useState } from 'preact/hooks';
|
||||
import { Button } from '../Button';
|
||||
import { Icon } from '../Icon';
|
||||
import { PopoverTabs, PopoverWrapperProps } from './PopoverDragNDropWrapper';
|
||||
import { PopoverFileItem } from './PopoverFileItem';
|
||||
import { PopoverFileItemActionType } from './PopoverFileItemAction';
|
||||
|
||||
type Props = PopoverWrapperProps & {
|
||||
currentTab: PopoverTabs;
|
||||
setCurrentTab: StateUpdater<PopoverTabs>;
|
||||
};
|
||||
|
||||
export const AttachedFilesPopover: FunctionComponent<Props> = observer(
|
||||
({
|
||||
application,
|
||||
appState,
|
||||
note,
|
||||
fileActionHandler,
|
||||
currentTab,
|
||||
setCurrentTab,
|
||||
}) => {
|
||||
const [attachedFiles, setAttachedFiles] = useState<SNFile[]>([]);
|
||||
const [allFiles, setAllFiles] = useState<SNFile[]>([]);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const filesList =
|
||||
currentTab === PopoverTabs.AttachedFiles ? attachedFiles : allFiles;
|
||||
|
||||
const filteredList =
|
||||
searchQuery.length > 0
|
||||
? filesList.filter(
|
||||
(file) => file.nameWithExt.toLowerCase().indexOf(searchQuery) !== -1
|
||||
)
|
||||
: filesList;
|
||||
|
||||
const reloadAttachedFiles = useCallback(() => {
|
||||
setAttachedFiles(
|
||||
application.items
|
||||
.getFilesForNote(note)
|
||||
.sort((a, b) => (a.created_at < b.created_at ? 1 : -1))
|
||||
);
|
||||
}, [application.items, note]);
|
||||
|
||||
const reloadAllFiles = useCallback(() => {
|
||||
setAllFiles(
|
||||
application
|
||||
.getItems(ContentType.File)
|
||||
.sort((a, b) => (a.created_at < b.created_at ? 1 : -1)) as SNFile[]
|
||||
);
|
||||
}, [application]);
|
||||
|
||||
useEffect(() => {
|
||||
const unregisterFileStream = application.streamItems(
|
||||
ContentType.File,
|
||||
() => {
|
||||
reloadAttachedFiles();
|
||||
reloadAllFiles();
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unregisterFileStream();
|
||||
};
|
||||
}, [application, reloadAllFiles, reloadAttachedFiles]);
|
||||
|
||||
const handleAttachFilesClick = async () => {
|
||||
const uploadedFiles = await appState.files.uploadNewFile();
|
||||
if (!uploadedFiles) {
|
||||
return;
|
||||
}
|
||||
if (currentTab === PopoverTabs.AttachedFiles) {
|
||||
uploadedFiles.forEach((file) => {
|
||||
fileActionHandler({
|
||||
type: PopoverFileItemActionType.AttachFileToNote,
|
||||
payload: file,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="flex border-0 border-b-1 border-solid border-main">
|
||||
<button
|
||||
className={`bg-default border-0 cursor-pointer px-3 py-2.5 relative focus:bg-info-backdrop focus:shadow-bottom ${
|
||||
currentTab === PopoverTabs.AttachedFiles
|
||||
? 'color-info font-medium shadow-bottom'
|
||||
: 'color-text'
|
||||
}`}
|
||||
onClick={() => {
|
||||
setCurrentTab(PopoverTabs.AttachedFiles);
|
||||
}}
|
||||
>
|
||||
Attached
|
||||
</button>
|
||||
<button
|
||||
className={`bg-default border-0 cursor-pointer px-3 py-2.5 relative focus:bg-info-backdrop focus:shadow-bottom ${
|
||||
currentTab === PopoverTabs.AllFiles
|
||||
? 'color-info font-medium shadow-bottom'
|
||||
: 'color-text'
|
||||
}`}
|
||||
onClick={() => {
|
||||
setCurrentTab(PopoverTabs.AllFiles);
|
||||
}}
|
||||
>
|
||||
All files
|
||||
</button>
|
||||
</div>
|
||||
<div className="min-h-0 max-h-110 overflow-y-auto">
|
||||
{filteredList.length > 0 || searchQuery.length > 0 ? (
|
||||
<div className="sticky top-0 left-0 p-3 bg-default border-0 border-b-1 border-solid border-main">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full rounded py-1.5 px-3 text-input bg-default border-solid border-1 border-main"
|
||||
placeholder="Search files..."
|
||||
value={searchQuery}
|
||||
onInput={(e) => {
|
||||
setSearchQuery((e.target as HTMLInputElement).value);
|
||||
}}
|
||||
/>
|
||||
{searchQuery.length > 0 && (
|
||||
<button
|
||||
className="flex absolute right-2 p-0 bg-transparent border-0 top-1/2 -translate-y-1/2 cursor-pointer"
|
||||
onClick={() => {
|
||||
setSearchQuery('');
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
type="clear-circle-filled"
|
||||
className="color-neutral"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{filteredList.length > 0 ? (
|
||||
filteredList.map((file: SNFile) => {
|
||||
return (
|
||||
<PopoverFileItem
|
||||
key={file.uuid}
|
||||
file={file}
|
||||
isAttachedToNote={attachedFiles.includes(file)}
|
||||
handleFileAction={fileActionHandler}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center w-full py-8">
|
||||
<div className="w-18 h-18 mb-2">
|
||||
<FilesIllustration
|
||||
style={{
|
||||
transform: 'scale(0.6)',
|
||||
transformOrigin: 'top left',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-medium mb-3">
|
||||
{searchQuery.length > 0
|
||||
? 'No result found'
|
||||
: currentTab === PopoverTabs.AttachedFiles
|
||||
? 'No files attached to this note'
|
||||
: 'No files found in this account'}
|
||||
</div>
|
||||
<Button type="normal" onClick={handleAttachFilesClick}>
|
||||
{currentTab === PopoverTabs.AttachedFiles ? 'Attach' : 'Upload'}{' '}
|
||||
files
|
||||
</Button>
|
||||
<div className="text-xs color-grey-0 mt-3">
|
||||
Or drop your files here
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{filteredList.length > 0 && (
|
||||
<button
|
||||
className="sn-dropdown-item py-3 border-0 border-t-1px border-solid border-main focus:bg-info-backdrop"
|
||||
onClick={handleAttachFilesClick}
|
||||
>
|
||||
<Icon type="add" className="mr-2 color-neutral" />
|
||||
{currentTab === PopoverTabs.AttachedFiles
|
||||
? 'Attach'
|
||||
: 'Upload'}{' '}
|
||||
files
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,143 @@
|
||||
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/constants';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { StreamingFileReader } from '@standardnotes/filepicker';
|
||||
import { SNNote } from '@standardnotes/snjs';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { AttachedFilesPopover } from './AttachedFilesPopover';
|
||||
import {
|
||||
PopoverFileItemAction,
|
||||
PopoverFileItemActionType,
|
||||
} from './PopoverFileItemAction';
|
||||
|
||||
export enum PopoverTabs {
|
||||
AttachedFiles,
|
||||
AllFiles,
|
||||
}
|
||||
|
||||
export type PopoverWrapperProps = {
|
||||
application: WebApplication;
|
||||
appState: AppState;
|
||||
note: SNNote;
|
||||
fileActionHandler: (action: PopoverFileItemAction) => Promise<boolean>;
|
||||
};
|
||||
|
||||
export const PopoverDragNDropWrapper: FunctionComponent<
|
||||
PopoverWrapperProps
|
||||
> = ({ fileActionHandler, appState, application, note }) => {
|
||||
const dropzoneRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [currentTab, setCurrentTab] = useState(PopoverTabs.AttachedFiles);
|
||||
const dragCounter = useRef(0);
|
||||
|
||||
const handleDrag = (event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
const handleDragIn = (event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
dragCounter.current = dragCounter.current + 1;
|
||||
|
||||
if (event.dataTransfer?.items.length) {
|
||||
setIsDragging(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragOut = (event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
dragCounter.current = dragCounter.current - 1;
|
||||
|
||||
if (dragCounter.current > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsDragging(false);
|
||||
};
|
||||
|
||||
const handleDrop = useCallback(
|
||||
(event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
setIsDragging(false);
|
||||
|
||||
if (event.dataTransfer?.items.length) {
|
||||
Array.from(event.dataTransfer.items).forEach(async (item) => {
|
||||
let fileOrHandle;
|
||||
if (StreamingFileReader.available()) {
|
||||
fileOrHandle =
|
||||
(await item.getAsFileSystemHandle()) as FileSystemFileHandle;
|
||||
} else {
|
||||
fileOrHandle = item.getAsFile();
|
||||
}
|
||||
if (fileOrHandle) {
|
||||
const uploadedFiles = await appState.files.uploadNewFile(
|
||||
fileOrHandle
|
||||
);
|
||||
if (!uploadedFiles) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTab === PopoverTabs.AttachedFiles) {
|
||||
uploadedFiles.forEach((file) => {
|
||||
fileActionHandler({
|
||||
type: PopoverFileItemActionType.AttachFileToNote,
|
||||
payload: file,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
event.dataTransfer.clearData();
|
||||
dragCounter.current = 0;
|
||||
}
|
||||
},
|
||||
[appState.files, currentTab, fileActionHandler]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const dropzoneElement = dropzoneRef.current;
|
||||
|
||||
if (dropzoneElement) {
|
||||
dropzoneElement.addEventListener('dragenter', handleDragIn);
|
||||
dropzoneElement.addEventListener('dragleave', handleDragOut);
|
||||
dropzoneElement.addEventListener('dragover', handleDrag);
|
||||
dropzoneElement.addEventListener('drop', handleDrop);
|
||||
}
|
||||
|
||||
return () => {
|
||||
dropzoneElement?.removeEventListener('dragenter', handleDragIn);
|
||||
dropzoneElement?.removeEventListener('dragleave', handleDragOut);
|
||||
dropzoneElement?.removeEventListener('dragover', handleDrag);
|
||||
dropzoneElement?.removeEventListener('drop', handleDrop);
|
||||
};
|
||||
}, [handleDrop]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dropzoneRef}
|
||||
className="focus:shadow-none"
|
||||
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
||||
style={{
|
||||
border: isDragging ? '2px dashed var(--sn-stylekit-info-color)' : '',
|
||||
}}
|
||||
>
|
||||
<AttachedFilesPopover
|
||||
application={application}
|
||||
appState={appState}
|
||||
note={note}
|
||||
fileActionHandler={fileActionHandler}
|
||||
currentTab={currentTab}
|
||||
setCurrentTab={setCurrentTab}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
import { KeyboardKey } from '@/services/ioService';
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker';
|
||||
import { SNFile } from '@standardnotes/snjs';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { ICONS } from '../Icon';
|
||||
import {
|
||||
PopoverFileItemAction,
|
||||
PopoverFileItemActionType,
|
||||
} from './PopoverFileItemAction';
|
||||
import { PopoverFileSubmenu } from './PopoverFileSubmenu';
|
||||
|
||||
const getIconForFileType = (fileType: string) => {
|
||||
let iconType = 'file-other';
|
||||
|
||||
if (fileType === 'pdf') {
|
||||
iconType = 'file-pdf';
|
||||
}
|
||||
|
||||
if (/^(docx?|odt)/.test(fileType)) {
|
||||
iconType = 'file-doc';
|
||||
}
|
||||
|
||||
if (/^pptx?/.test(fileType)) {
|
||||
iconType = 'file-ppt';
|
||||
}
|
||||
|
||||
if (/^(xlsx?|ods)/.test(fileType)) {
|
||||
iconType = 'file-xls';
|
||||
}
|
||||
|
||||
if (/^(jpe?g|a?png|webp|gif)/.test(fileType)) {
|
||||
iconType = 'file-image';
|
||||
}
|
||||
|
||||
if (/^(mov|mp4|mkv)/.test(fileType)) {
|
||||
iconType = 'file-mov';
|
||||
}
|
||||
|
||||
if (/^(wav|mp3|flac|ogg)/.test(fileType)) {
|
||||
iconType = 'file-music';
|
||||
}
|
||||
|
||||
if (/^(zip|rar|7z)/.test(fileType)) {
|
||||
iconType = 'file-zip';
|
||||
}
|
||||
|
||||
const IconComponent = ICONS[iconType as keyof typeof ICONS];
|
||||
|
||||
return <IconComponent className="flex-shrink-0" />;
|
||||
};
|
||||
|
||||
export type PopoverFileItemProps = {
|
||||
file: SNFile;
|
||||
isAttachedToNote: boolean;
|
||||
handleFileAction: (action: PopoverFileItemAction) => Promise<boolean>;
|
||||
};
|
||||
|
||||
export const PopoverFileItem: FunctionComponent<PopoverFileItemProps> = ({
|
||||
file,
|
||||
isAttachedToNote,
|
||||
handleFileAction,
|
||||
}) => {
|
||||
const [fileName, setFileName] = useState(file.nameWithExt);
|
||||
const [isRenamingFile, setIsRenamingFile] = useState(false);
|
||||
const fileNameInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isRenamingFile) {
|
||||
fileNameInputRef.current?.focus();
|
||||
}
|
||||
}, [isRenamingFile]);
|
||||
|
||||
const renameFile = async (file: SNFile, name: string) => {
|
||||
const didRename = await handleFileAction({
|
||||
type: PopoverFileItemActionType.RenameFile,
|
||||
payload: {
|
||||
file,
|
||||
name,
|
||||
},
|
||||
});
|
||||
if (didRename) {
|
||||
setIsRenamingFile(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileNameInput = (event: Event) => {
|
||||
setFileName((event.target as HTMLInputElement).value);
|
||||
};
|
||||
|
||||
const handleFileNameInputKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === KeyboardKey.Enter) {
|
||||
renameFile(file, fileName);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between p-3">
|
||||
<div className="flex items-center">
|
||||
{getIconForFileType(file.ext ?? '')}
|
||||
<div className="flex flex-col mx-4">
|
||||
{isRenamingFile ? (
|
||||
<input
|
||||
type="text"
|
||||
className="text-input px-1.5 py-1 mb-1 border-1 border-solid border-main bg-transparent color-foreground"
|
||||
value={fileName}
|
||||
ref={fileNameInputRef}
|
||||
onInput={handleFileNameInput}
|
||||
onKeyDown={handleFileNameInputKeyDown}
|
||||
/>
|
||||
) : (
|
||||
<div className="text-sm mb-1">{file.nameWithExt}</div>
|
||||
)}
|
||||
<div className="text-xs color-grey-0">
|
||||
{file.created_at.toLocaleString()} ·{' '}
|
||||
{formatSizeToReadableString(file.size)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PopoverFileSubmenu
|
||||
file={file}
|
||||
isAttachedToNote={isAttachedToNote}
|
||||
handleFileAction={handleFileAction}
|
||||
setIsRenamingFile={setIsRenamingFile}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { SNFile } from '@standardnotes/snjs';
|
||||
|
||||
export enum PopoverFileItemActionType {
|
||||
AttachFileToNote,
|
||||
DetachFileToNote,
|
||||
DeleteFile,
|
||||
DownloadFile,
|
||||
RenameFile,
|
||||
ToggleFileProtection,
|
||||
}
|
||||
|
||||
export type PopoverFileItemAction =
|
||||
| {
|
||||
type: Exclude<
|
||||
PopoverFileItemActionType,
|
||||
| PopoverFileItemActionType.RenameFile
|
||||
| PopoverFileItemActionType.ToggleFileProtection
|
||||
>;
|
||||
payload: SNFile;
|
||||
}
|
||||
| {
|
||||
type: PopoverFileItemActionType.ToggleFileProtection;
|
||||
payload: SNFile;
|
||||
callback: (isProtected: boolean) => void;
|
||||
}
|
||||
| {
|
||||
type: PopoverFileItemActionType.RenameFile;
|
||||
payload: {
|
||||
file: SNFile;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,188 @@
|
||||
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/constants';
|
||||
import {
|
||||
calculateSubmenuStyle,
|
||||
SubmenuStyle,
|
||||
} from '@/utils/calculateSubmenuStyle';
|
||||
import {
|
||||
Disclosure,
|
||||
DisclosureButton,
|
||||
DisclosurePanel,
|
||||
} from '@reach/disclosure';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import {
|
||||
StateUpdater,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'preact/hooks';
|
||||
import { Icon } from '../Icon';
|
||||
import { Switch } from '../Switch';
|
||||
import { useCloseOnBlur } from '../utils';
|
||||
import { PopoverFileItemProps } from './PopoverFileItem';
|
||||
import { PopoverFileItemActionType } from './PopoverFileItemAction';
|
||||
|
||||
type Props = Omit<PopoverFileItemProps, 'renameFile'> & {
|
||||
setIsRenamingFile: StateUpdater<boolean>;
|
||||
};
|
||||
|
||||
export const PopoverFileSubmenu: FunctionComponent<Props> = ({
|
||||
file,
|
||||
isAttachedToNote,
|
||||
handleFileAction,
|
||||
setIsRenamingFile,
|
||||
}) => {
|
||||
const menuContainerRef = useRef<HTMLDivElement>(null);
|
||||
const menuButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [isFileProtected, setIsFileProtected] = useState(file.protected);
|
||||
const [menuStyle, setMenuStyle] = useState<SubmenuStyle>({
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
maxHeight: 'auto',
|
||||
});
|
||||
const [closeOnBlur] = useCloseOnBlur(menuContainerRef, setIsMenuOpen);
|
||||
|
||||
const closeMenu = () => {
|
||||
setIsMenuOpen(false);
|
||||
};
|
||||
|
||||
const toggleMenu = () => {
|
||||
if (!isMenuOpen) {
|
||||
const menuPosition = calculateSubmenuStyle(menuButtonRef.current);
|
||||
if (menuPosition) {
|
||||
setMenuStyle(menuPosition);
|
||||
}
|
||||
}
|
||||
|
||||
setIsMenuOpen(!isMenuOpen);
|
||||
};
|
||||
|
||||
const recalculateMenuStyle = useCallback(() => {
|
||||
const newMenuPosition = calculateSubmenuStyle(
|
||||
menuButtonRef.current,
|
||||
menuRef.current
|
||||
);
|
||||
|
||||
if (newMenuPosition) {
|
||||
setMenuStyle(newMenuPosition);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMenuOpen) {
|
||||
setTimeout(() => {
|
||||
recalculateMenuStyle();
|
||||
});
|
||||
}
|
||||
}, [isMenuOpen, recalculateMenuStyle]);
|
||||
|
||||
return (
|
||||
<div ref={menuContainerRef}>
|
||||
<Disclosure open={isMenuOpen} onChange={toggleMenu}>
|
||||
<DisclosureButton
|
||||
ref={menuButtonRef}
|
||||
onBlur={closeOnBlur}
|
||||
className="w-7 h-7 p-1 rounded-full border-0 bg-transparent hover:bg-contrast cursor-pointer"
|
||||
>
|
||||
<Icon type="more" className="color-neutral" />
|
||||
</DisclosureButton>
|
||||
<DisclosurePanel
|
||||
ref={menuRef}
|
||||
style={{
|
||||
...menuStyle,
|
||||
position: 'fixed',
|
||||
}}
|
||||
className="sn-dropdown flex flex-col max-h-120 min-w-60 py-1 fixed overflow-y-auto"
|
||||
>
|
||||
{isMenuOpen && (
|
||||
<>
|
||||
{isAttachedToNote ? (
|
||||
<button
|
||||
onBlur={closeOnBlur}
|
||||
className="sn-dropdown-item focus:bg-info-backdrop"
|
||||
onClick={() => {
|
||||
handleFileAction({
|
||||
type: PopoverFileItemActionType.DetachFileToNote,
|
||||
payload: file,
|
||||
});
|
||||
closeMenu();
|
||||
}}
|
||||
>
|
||||
<Icon type="link-off" className="mr-2 color-neutral" />
|
||||
Detach from note
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onBlur={closeOnBlur}
|
||||
className="sn-dropdown-item focus:bg-info-backdrop"
|
||||
onClick={() => {
|
||||
handleFileAction({
|
||||
type: PopoverFileItemActionType.AttachFileToNote,
|
||||
payload: file,
|
||||
});
|
||||
closeMenu();
|
||||
}}
|
||||
>
|
||||
<Icon type="link" className="mr-2 color-neutral" />
|
||||
Attach to note
|
||||
</button>
|
||||
)}
|
||||
<div className="min-h-1px my-1 bg-border"></div>
|
||||
<button
|
||||
className="sn-dropdown-item justify-between focus:bg-info-backdrop"
|
||||
onClick={() => {
|
||||
handleFileAction({
|
||||
type: PopoverFileItemActionType.ToggleFileProtection,
|
||||
payload: file,
|
||||
callback: (isProtected: boolean) => {
|
||||
setIsFileProtected(isProtected);
|
||||
},
|
||||
});
|
||||
}}
|
||||
onBlur={closeOnBlur}
|
||||
>
|
||||
<span className="flex items-center">
|
||||
<Icon type="password" className="mr-2 color-neutral" />
|
||||
Password protection
|
||||
</span>
|
||||
<Switch
|
||||
className="px-0 pointer-events-none"
|
||||
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
||||
checked={isFileProtected}
|
||||
/>
|
||||
</button>
|
||||
<div className="min-h-1px my-1 bg-border"></div>
|
||||
<button
|
||||
onBlur={closeOnBlur}
|
||||
className="sn-dropdown-item focus:bg-info-backdrop"
|
||||
onClick={() => {
|
||||
handleFileAction({
|
||||
type: PopoverFileItemActionType.DownloadFile,
|
||||
payload: file,
|
||||
});
|
||||
closeMenu();
|
||||
}}
|
||||
>
|
||||
<Icon type="download" className="mr-2 color-neutral" />
|
||||
Download
|
||||
</button>
|
||||
<button
|
||||
onBlur={closeOnBlur}
|
||||
className="sn-dropdown-item focus:bg-info-backdrop"
|
||||
onClick={() => {
|
||||
setIsRenamingFile(true);
|
||||
}}
|
||||
>
|
||||
<Icon type="pencil" className="mr-2 color-neutral" />
|
||||
Rename
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
25
app/assets/javascripts/components/Bubble.tsx
Normal file
25
app/assets/javascripts/components/Bubble.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
interface BubbleProperties {
|
||||
label: string;
|
||||
selected: boolean;
|
||||
onSelect: () => void;
|
||||
}
|
||||
|
||||
const styles = {
|
||||
base: 'px-2 py-1.5 text-center rounded-full cursor-pointer transition border-1 border-solid active:border-info active:bg-info active:color-neutral-contrast',
|
||||
unselected: 'color-neutral border-secondary',
|
||||
selected: 'border-info bg-info color-neutral-contrast',
|
||||
};
|
||||
|
||||
const Bubble = ({ label, selected, onSelect }: BubbleProperties) => (
|
||||
<span
|
||||
role="tab"
|
||||
className={`bubble ${styles.base} ${
|
||||
selected ? styles.selected : styles.unselected
|
||||
}`}
|
||||
onClick={onSelect}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
|
||||
export default Bubble;
|
||||
@@ -256,7 +256,7 @@ export class Footer extends PureComponent<Props, State> {
|
||||
|
||||
updateSyncStatus() {
|
||||
const statusManager = this.application.getStatusManager();
|
||||
const syncStatus = this.application.getSyncStatus();
|
||||
const syncStatus = this.application.sync.getSyncStatus();
|
||||
const stats = syncStatus.getStats();
|
||||
if (syncStatus.hasError()) {
|
||||
statusManager.setMessage('Unable to Sync');
|
||||
@@ -290,7 +290,7 @@ export class Footer extends PureComponent<Props, State> {
|
||||
|
||||
updateLocalDataStatus() {
|
||||
const statusManager = this.application.getStatusManager();
|
||||
const syncStatus = this.application.getSyncStatus();
|
||||
const syncStatus = this.application.sync.getSyncStatus();
|
||||
const stats = syncStatus.getStats();
|
||||
const encryption = this.application.isEncryptionAvailable();
|
||||
if (stats.localDataDone) {
|
||||
@@ -312,7 +312,7 @@ export class Footer extends PureComponent<Props, State> {
|
||||
|
||||
findErrors() {
|
||||
this.setState({
|
||||
hasError: this.application.getSyncStatus().hasError(),
|
||||
hasError: this.application.sync.getSyncStatus().hasError(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ArrowLeftIcon,
|
||||
ArrowsSortDownIcon,
|
||||
ArrowsSortUpIcon,
|
||||
AttachmentFileIcon,
|
||||
AuthenticatorIcon,
|
||||
CheckBoldIcon,
|
||||
CheckCircleIcon,
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
ChevronRightIcon,
|
||||
CloseIcon,
|
||||
CloudOffIcon,
|
||||
ClearCircleFilledIcon,
|
||||
CodeIcon,
|
||||
CopyIcon,
|
||||
DashboardIcon,
|
||||
@@ -25,12 +27,22 @@ import {
|
||||
EmailIcon,
|
||||
EyeIcon,
|
||||
EyeOffIcon,
|
||||
FileDocIcon,
|
||||
FileImageIcon,
|
||||
FileMovIcon,
|
||||
FileMusicIcon,
|
||||
FileOtherIcon,
|
||||
FilePdfIcon,
|
||||
FilePptIcon,
|
||||
FileXlsIcon,
|
||||
FileZipIcon,
|
||||
HashtagIcon,
|
||||
HashtagOffIcon,
|
||||
HelpIcon,
|
||||
HistoryIcon,
|
||||
InfoIcon,
|
||||
KeyboardIcon,
|
||||
LinkIcon,
|
||||
LinkOffIcon,
|
||||
ListBulleted,
|
||||
ListedIcon,
|
||||
@@ -44,9 +56,9 @@ import {
|
||||
MoreIcon,
|
||||
NotesIcon,
|
||||
PasswordIcon,
|
||||
PencilOffIcon,
|
||||
PencilFilledIcon,
|
||||
PencilIcon,
|
||||
PencilOffIcon,
|
||||
PinFilledIcon,
|
||||
PinIcon,
|
||||
PlainTextIcon,
|
||||
@@ -75,17 +87,28 @@ import {
|
||||
WindowIcon,
|
||||
} from '@standardnotes/stylekit';
|
||||
|
||||
const 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,
|
||||
'cloud-off': CloudOffIcon,
|
||||
'clear-circle-filled': ClearCircleFilledIcon,
|
||||
'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,
|
||||
@@ -121,6 +144,7 @@ const ICONS = {
|
||||
history: HistoryIcon,
|
||||
info: InfoIcon,
|
||||
keyboard: KeyboardIcon,
|
||||
link: LinkIcon,
|
||||
listed: ListedIcon,
|
||||
lock: LockIcon,
|
||||
markdown: MarkdownIcon,
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
ItemMutator,
|
||||
ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction,
|
||||
NoteViewController,
|
||||
FeatureIdentifier,
|
||||
FeatureStatus,
|
||||
} from '@standardnotes/snjs';
|
||||
import { debounce, isDesktopApplication } from '@/utils';
|
||||
import { KeyboardModifier, KeyboardKey } from '@/services/ioService';
|
||||
@@ -37,6 +39,7 @@ import { ComponentView } from '../ComponentView';
|
||||
import { PanelSide, PanelResizer, PanelResizeType } from '../PanelResizer';
|
||||
import { ElementIds } from '@/element_ids';
|
||||
import { ChangeEditorButton } from '../ChangeEditorButton';
|
||||
import { AttachedFilesButton } from '../AttachedFilesPopover/AttachedFilesButton';
|
||||
|
||||
const MINIMUM_STATUS_DURATION = 400;
|
||||
const TEXTAREA_DEBOUNCE = 100;
|
||||
@@ -100,6 +103,7 @@ type State = {
|
||||
editorTitle: string;
|
||||
editorText: string;
|
||||
isDesktop?: boolean;
|
||||
isEntitledToFiles: boolean;
|
||||
lockText: string;
|
||||
marginResizersEnabled?: boolean;
|
||||
monospaceFont?: boolean;
|
||||
@@ -168,6 +172,9 @@ export class NoteView extends PureComponent<Props, State> {
|
||||
editorText: '',
|
||||
editorTitle: '',
|
||||
isDesktop: isDesktopApplication(),
|
||||
isEntitledToFiles:
|
||||
this.application.features.getFeatureStatus(FeatureIdentifier.Files) ===
|
||||
FeatureStatus.Entitled,
|
||||
lockText: 'Note Editing Disabled',
|
||||
noteStatus: undefined,
|
||||
noteLocked: this.controller.note.locked,
|
||||
@@ -321,6 +328,15 @@ export class NoteView extends PureComponent<Props, State> {
|
||||
/** @override */
|
||||
async onAppEvent(eventName: ApplicationEvent) {
|
||||
switch (eventName) {
|
||||
case ApplicationEvent.FeaturesUpdated:
|
||||
case ApplicationEvent.UserRolesChanged:
|
||||
this.setState({
|
||||
isEntitledToFiles:
|
||||
this.application.features.getFeatureStatus(
|
||||
FeatureIdentifier.Files
|
||||
) === FeatureStatus.Entitled,
|
||||
});
|
||||
break;
|
||||
case ApplicationEvent.PreferencesChanged:
|
||||
this.reloadPreferences();
|
||||
break;
|
||||
@@ -681,7 +697,7 @@ export class NoteView extends PureComponent<Props, State> {
|
||||
if (left !== undefined && left !== null) {
|
||||
await this.application.setPreference(PrefKey.EditorLeft, left);
|
||||
}
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
};
|
||||
|
||||
async reloadSpellcheck() {
|
||||
@@ -797,7 +813,7 @@ export class NoteView extends PureComponent<Props, State> {
|
||||
} else {
|
||||
await this.disassociateComponentWithCurrentNote(component);
|
||||
}
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
};
|
||||
|
||||
async disassociateComponentWithCurrentNote(component: SNComponent) {
|
||||
@@ -1027,6 +1043,18 @@ export class NoteView extends PureComponent<Props, State> {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{this.state.isEntitledToFiles &&
|
||||
window.enabledUnfinishedFeatures && (
|
||||
<div className="mr-3">
|
||||
<AttachedFilesButton
|
||||
application={this.application}
|
||||
appState={this.appState}
|
||||
onClickPreprocessing={
|
||||
this.ensureNoteIsInsertedBeforeUIAction
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="mr-3">
|
||||
<ChangeEditorButton
|
||||
application={this.application}
|
||||
|
||||
@@ -38,7 +38,6 @@ export const AddTagOption: FunctionComponent<Props> = observer(
|
||||
const menuPosition = calculateSubmenuStyle(menuButtonRef.current);
|
||||
if (menuPosition) {
|
||||
setMenuStyle(menuPosition);
|
||||
console.log(menuPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +52,6 @@ export const AddTagOption: FunctionComponent<Props> = observer(
|
||||
|
||||
if (newMenuPosition) {
|
||||
setMenuStyle(newMenuPosition);
|
||||
console.log(newMenuPosition);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ export const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
||||
|
||||
await application.runTransactionalMutations(transactions);
|
||||
/** Dirtying can happen above */
|
||||
application.sync();
|
||||
application.sync.sync();
|
||||
|
||||
setCurrentEditor(application.componentManager.editorForNote(note));
|
||||
};
|
||||
|
||||
@@ -59,11 +59,13 @@ export const createEditorMenuGroups = (
|
||||
feature.area === ComponentArea.Editor
|
||||
)
|
||||
.forEach((editorFeature) => {
|
||||
if (
|
||||
!editors.find(
|
||||
(editor) => editor.identifier === editorFeature.identifier
|
||||
)
|
||||
) {
|
||||
const notInstalled = !editors.find(
|
||||
(editor) => editor.identifier === editorFeature.identifier
|
||||
);
|
||||
const isExperimental = application.features.isExperimentalFeature(
|
||||
editorFeature.identifier
|
||||
);
|
||||
if (notInstalled && !isExperimental) {
|
||||
editorItems[getEditorGroup(editorFeature)].push({
|
||||
name: editorFeature.name as string,
|
||||
isEntitled: false,
|
||||
|
||||
@@ -48,13 +48,13 @@ export const NotesView: FunctionComponent<Props> = observer(
|
||||
selectPreviousNote,
|
||||
onFilterEnter,
|
||||
handleFilterTextChanged,
|
||||
onSearchInputBlur,
|
||||
clearFilterText,
|
||||
paginate,
|
||||
panelWidth,
|
||||
} = appState.notesView;
|
||||
|
||||
const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false);
|
||||
const [focusedSearch, setFocusedSearch] = useState(false);
|
||||
|
||||
const [closeDisplayOptMenuOnBlur] = useCloseOnBlur(
|
||||
displayOptionsMenuRef,
|
||||
@@ -130,6 +130,9 @@ export const NotesView: FunctionComponent<Props> = observer(
|
||||
setNoteFilterText((e.target as HTMLInputElement).value);
|
||||
};
|
||||
|
||||
const onSearchFocused = () => setFocusedSearch(true);
|
||||
const onSearchBlurred = () => setFocusedSearch(false);
|
||||
|
||||
const onNoteFilterKeyUp = (e: KeyboardEvent) => {
|
||||
if (e.key === KeyboardKey.Enter) {
|
||||
onFilterEnter();
|
||||
@@ -179,32 +182,38 @@ export const NotesView: FunctionComponent<Props> = observer(
|
||||
</button>
|
||||
</div>
|
||||
<div className="filter-section" role="search">
|
||||
<input
|
||||
type="text"
|
||||
id="search-bar"
|
||||
className="filter-bar"
|
||||
placeholder="Search"
|
||||
title="Searches notes in the currently selected tag"
|
||||
value={noteFilterText}
|
||||
onChange={onNoteFilterTextChange}
|
||||
onKeyUp={onNoteFilterKeyUp}
|
||||
onBlur={() => onSearchInputBlur()}
|
||||
/>
|
||||
{noteFilterText ? (
|
||||
<button
|
||||
onClick={clearFilterText}
|
||||
aria-role="button"
|
||||
id="search-clear-button"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
) : null}
|
||||
<div className="ml-2">
|
||||
<SearchOptions
|
||||
application={application}
|
||||
appState={appState}
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
id="search-bar"
|
||||
className="filter-bar"
|
||||
placeholder="Search"
|
||||
title="Searches notes in the currently selected tag"
|
||||
value={noteFilterText}
|
||||
onChange={onNoteFilterTextChange}
|
||||
onKeyUp={onNoteFilterKeyUp}
|
||||
onFocus={onSearchFocused}
|
||||
onBlur={onSearchBlurred}
|
||||
/>
|
||||
{noteFilterText && (
|
||||
<button
|
||||
onClick={clearFilterText}
|
||||
aria-role="button"
|
||||
id="search-clear-button"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(focusedSearch || noteFilterText) && (
|
||||
<div className="animate-fade-from-top">
|
||||
<SearchOptions
|
||||
application={application}
|
||||
appState={appState}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<NoAccountWarning appState={appState} />
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { Icon } from './Icon';
|
||||
import { useCloseOnBlur } from './utils';
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import VisuallyHidden from '@reach/visually-hidden';
|
||||
import {
|
||||
Disclosure,
|
||||
DisclosureButton,
|
||||
DisclosurePanel,
|
||||
} from '@reach/disclosure';
|
||||
import { Switch } from './Switch';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import Bubble from './Bubble';
|
||||
|
||||
type Props = {
|
||||
appState: AppState;
|
||||
@@ -23,94 +14,33 @@ export const SearchOptions = observer(({ appState }: Props) => {
|
||||
const { includeProtectedContents, includeArchived, includeTrashed } =
|
||||
searchOptions;
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [position, setPosition] = useState({
|
||||
top: 0,
|
||||
right: 0,
|
||||
});
|
||||
const [maxWidth, setMaxWidth] = useState<number | 'auto'>('auto');
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const [closeOnBlur, setLockCloseOnBlur] = useCloseOnBlur(
|
||||
panelRef as any,
|
||||
setOpen
|
||||
);
|
||||
|
||||
async function toggleIncludeProtectedContents() {
|
||||
setLockCloseOnBlur(true);
|
||||
try {
|
||||
await searchOptions.toggleIncludeProtectedContents();
|
||||
} finally {
|
||||
setLockCloseOnBlur(false);
|
||||
}
|
||||
await searchOptions.toggleIncludeProtectedContents();
|
||||
}
|
||||
|
||||
const updateWidthAndPosition = () => {
|
||||
const rect = buttonRef.current!.getBoundingClientRect();
|
||||
setMaxWidth(rect.right - 16);
|
||||
setPosition({
|
||||
top: rect.bottom,
|
||||
right: document.body.clientWidth - rect.right,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', updateWidthAndPosition);
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateWidthAndPosition);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Disclosure
|
||||
open={open}
|
||||
onChange={() => {
|
||||
updateWidthAndPosition();
|
||||
setOpen(!open);
|
||||
}}
|
||||
<div
|
||||
role="tablist"
|
||||
className="search-options justify-center"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
>
|
||||
<DisclosureButton
|
||||
ref={buttonRef}
|
||||
onBlur={closeOnBlur}
|
||||
className="border-0 p-0 bg-transparent cursor-pointer color-neutral hover:color-info"
|
||||
>
|
||||
<VisuallyHidden>Search options</VisuallyHidden>
|
||||
<Icon type="tune" className="block" />
|
||||
</DisclosureButton>
|
||||
<DisclosurePanel
|
||||
ref={panelRef}
|
||||
style={{
|
||||
...position,
|
||||
maxWidth,
|
||||
}}
|
||||
className="sn-dropdown sn-dropdown--animated w-80 fixed grid gap-2 py-2"
|
||||
onBlur={closeOnBlur}
|
||||
>
|
||||
<Switch
|
||||
className="h-10"
|
||||
checked={includeProtectedContents}
|
||||
onChange={toggleIncludeProtectedContents}
|
||||
onBlur={closeOnBlur}
|
||||
>
|
||||
<p className="capitalize">Include protected contents</p>
|
||||
</Switch>
|
||||
<Switch
|
||||
className="h-10"
|
||||
checked={includeArchived}
|
||||
onChange={searchOptions.toggleIncludeArchived}
|
||||
onBlur={closeOnBlur}
|
||||
>
|
||||
<p className="capitalize">Include archived notes</p>
|
||||
</Switch>
|
||||
<Switch
|
||||
className="h-10"
|
||||
checked={includeTrashed}
|
||||
onChange={searchOptions.toggleIncludeTrashed}
|
||||
onBlur={closeOnBlur}
|
||||
>
|
||||
<p className="capitalize">Include trashed notes</p>
|
||||
</Switch>
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
<Bubble
|
||||
label="Protected Contents"
|
||||
selected={includeProtectedContents}
|
||||
onSelect={toggleIncludeProtectedContents}
|
||||
/>
|
||||
|
||||
<Bubble
|
||||
label="Archived"
|
||||
selected={includeArchived}
|
||||
onSelect={searchOptions.toggleIncludeArchived}
|
||||
/>
|
||||
|
||||
<Bubble
|
||||
label="Trashed"
|
||||
selected={includeTrashed}
|
||||
onSelect={searchOptions.toggleIncludeTrashed}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { PureComponent } from './Abstract/PureComponent';
|
||||
import { Fragment } from 'preact';
|
||||
|
||||
type Props = {
|
||||
application: WebApplication;
|
||||
@@ -8,43 +7,13 @@ type Props = {
|
||||
};
|
||||
|
||||
export class SyncResolutionMenu extends PureComponent<Props> {
|
||||
private status: Partial<{
|
||||
backupFinished: boolean;
|
||||
resolving: boolean;
|
||||
attemptedResolution: boolean;
|
||||
success: boolean;
|
||||
fail: boolean;
|
||||
}> = {};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props, props.application);
|
||||
}
|
||||
|
||||
downloadBackup(encrypted: boolean) {
|
||||
this.props.application.getArchiveService().downloadBackup(encrypted);
|
||||
this.status.backupFinished = true;
|
||||
}
|
||||
|
||||
skipBackup() {
|
||||
this.status.backupFinished = true;
|
||||
}
|
||||
|
||||
async performSyncResolution() {
|
||||
this.status.resolving = true;
|
||||
await this.props.application.resolveOutOfSync();
|
||||
|
||||
this.status.resolving = false;
|
||||
this.status.attemptedResolution = true;
|
||||
if (this.props.application.isOutOfSync()) {
|
||||
this.status.fail = true;
|
||||
} else {
|
||||
this.status.success = true;
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
close = () => {
|
||||
this.props.close();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
@@ -59,13 +28,15 @@ export class SyncResolutionMenu extends PureComponent<Props> {
|
||||
<div className="sk-panel-content">
|
||||
<div className="sk-panel-section">
|
||||
<div className="sk-panel-row sk-p">
|
||||
We've detected that the data on the server may not match the
|
||||
data in the current application session.
|
||||
We've detected that the data in the current application session
|
||||
may not match the data on the server. An attempt was made to
|
||||
auto-resolve the issue, but it was unable to reconcile the
|
||||
differences.
|
||||
</div>
|
||||
<div className="sk-p sk-panel-row">
|
||||
<div className="sk-panel-column">
|
||||
<strong className="sk-panel-row">
|
||||
Option 1 — Restart App:
|
||||
Option 1 — Restart Application:
|
||||
</strong>
|
||||
<div className="sk-p">
|
||||
Quit the application and re-open it. Sometimes, this may
|
||||
@@ -76,108 +47,15 @@ export class SyncResolutionMenu extends PureComponent<Props> {
|
||||
<div className="sk-p sk-panel-row">
|
||||
<div className="sk-panel-column">
|
||||
<strong className="sk-panel-row">
|
||||
Option 2 (recommended) — Sign Out:
|
||||
Option 2 — Sign Out and Back In:
|
||||
</strong>
|
||||
<div className="sk-p">
|
||||
Sign out of your account, then sign back in. This will
|
||||
ensure your data is consistent with the server.
|
||||
</div>
|
||||
Be sure to download a backup of your data before doing so.
|
||||
</div>
|
||||
</div>
|
||||
<div className="sk-p sk-panel-row">
|
||||
<div className="sk-panel-column">
|
||||
<strong className="sk-panel-row">
|
||||
Option 3 — Sync Resolution:
|
||||
</strong>
|
||||
<div className="sk-p">
|
||||
We can attempt to reconcile changes by downloading all data
|
||||
from the server. No existing data will be overwritten. If
|
||||
the local contents of an item differ from what the server
|
||||
has, a conflicted copy will be created.
|
||||
ensure your data is consistent with the server. Be sure to
|
||||
download a backup of your data before doing so.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!this.status.backupFinished && (
|
||||
<Fragment>
|
||||
<div className="sk-p sk-panel-row">
|
||||
Please download a backup before we attempt to perform a full
|
||||
account sync resolution.
|
||||
</div>
|
||||
<div className="sk-panel-row">
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => this.downloadBackup(true)}
|
||||
className="sn-button small info"
|
||||
>
|
||||
Encrypted
|
||||
</button>
|
||||
<button
|
||||
onClick={() => this.downloadBackup(false)}
|
||||
className="sn-button small info"
|
||||
>
|
||||
Decrypted
|
||||
</button>
|
||||
<button
|
||||
onClick={this.skipBackup}
|
||||
className="sn-button small danger"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{this.status.backupFinished && (
|
||||
<div>
|
||||
{!this.status.resolving && !this.status.attemptedResolution && (
|
||||
<div className="sk-panel-row">
|
||||
<button
|
||||
onClick={this.performSyncResolution}
|
||||
className="sn-button small info"
|
||||
>
|
||||
Perform Sync Resolution
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{this.status.resolving && (
|
||||
<div className="sk-panel-row justify-left">
|
||||
<div className="sk-horizontal-group">
|
||||
<div className="sk-spinner small info" />
|
||||
<div className="sk-label">
|
||||
Attempting sync resolution...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{this.status.fail && (
|
||||
<div className="sk-panel-column">
|
||||
<div className="sk-panel-row sk-label danger">
|
||||
Sync Resolution Failed
|
||||
</div>
|
||||
<div className="sk-p sk-panel-row">
|
||||
We attempted to reconcile local content and server
|
||||
content, but were unable to do so. At this point, we
|
||||
recommend signing out of your account and signing back
|
||||
in. You may wish to download a data backup before doing
|
||||
so.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{this.status.success && (
|
||||
<div className="sk-panel-column">
|
||||
<div className="sk-panel-row sk-label success">
|
||||
Sync Resolution Success
|
||||
</div>
|
||||
<div className="sk-p sk-panel-row">
|
||||
Your local data is now in sync with the server. You may
|
||||
close this window.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -46,8 +46,13 @@ export function useCloseOnClickOutside(
|
||||
if (!container.current) {
|
||||
return;
|
||||
}
|
||||
const isDescendant = container.current.contains(event.target as Node);
|
||||
if (!isDescendant) {
|
||||
const isDescendantOfContainer = container.current.contains(
|
||||
event.target as Node
|
||||
);
|
||||
const isDescendantOfDialog = (event.target as HTMLElement).closest(
|
||||
'[role="dialog"]'
|
||||
);
|
||||
if (!isDescendantOfContainer && !isDescendantOfDialog) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,4 +13,6 @@ export const NOTES_LIST_SCROLL_THRESHOLD = 200;
|
||||
export const MILLISECONDS_IN_A_DAY = 1000 * 60 * 60 * 24;
|
||||
export const DAYS_IN_A_WEEK = 7;
|
||||
export const DAYS_IN_A_YEAR = 365;
|
||||
export const BYTES_IN_ONE_MEGABYTE = 1000000;
|
||||
|
||||
export const BYTES_IN_ONE_KILOBYTE = 1_000;
|
||||
export const BYTES_IN_ONE_MEGABYTE = 1_000_000;
|
||||
|
||||
@@ -74,7 +74,7 @@ export const Extensions: FunctionComponent<{
|
||||
|
||||
const confirmExtension = async () => {
|
||||
await application.insertItem(confirmableExtension as SNComponent);
|
||||
application.sync();
|
||||
application.sync.sync();
|
||||
setExtensions(loadExtensions(application));
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { WebApplication } from '@/ui_models/application';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { PreferencesPane } from '../components';
|
||||
import { ErrorReporting, Tools, Defaults } from './general-segments';
|
||||
import { ErrorReporting, Tools, Defaults, LabsPane } from './general-segments';
|
||||
import { ExtensionsLatestVersions } from '@/preferences/panes/extensions-segments';
|
||||
import { Advanced } from '@/preferences/panes/account';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
@@ -19,6 +19,7 @@ export const General: FunctionComponent<GeneralProps> = observer(
|
||||
<Tools application={application} />
|
||||
<Defaults application={application} />
|
||||
<ErrorReporting appState={appState} />
|
||||
<LabsPane application={application} />
|
||||
<Advanced
|
||||
application={application}
|
||||
appState={appState}
|
||||
|
||||
@@ -24,22 +24,22 @@ export const Sync: FunctionComponent<Props> = observer(
|
||||
({ application }: Props) => {
|
||||
const [isSyncingInProgress, setIsSyncingInProgress] = useState(false);
|
||||
const [lastSyncDate, setLastSyncDate] = useState(
|
||||
formatLastSyncDate(application.getLastSyncDate() as Date)
|
||||
formatLastSyncDate(application.sync.getLastSyncDate() as Date)
|
||||
);
|
||||
|
||||
const doSynchronization = async () => {
|
||||
setIsSyncingInProgress(true);
|
||||
|
||||
const response = await application.sync({
|
||||
const response = await application.sync.sync({
|
||||
queueStrategy: SyncQueueStrategy.ForceSpawnNew,
|
||||
checkIntegrity: true,
|
||||
});
|
||||
setIsSyncingInProgress(false);
|
||||
if (response && response.error) {
|
||||
application.alertService!.alert(STRING_GENERIC_SYNC_ERROR);
|
||||
if (response && (response as any).error) {
|
||||
application.alertService.alert(STRING_GENERIC_SYNC_ERROR);
|
||||
} else {
|
||||
setLastSyncDate(
|
||||
formatLastSyncDate(application.getLastSyncDate() as Date)
|
||||
formatLastSyncDate(application.sync.getLastSyncDate() as Date)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { FindNativeFeature } from '@standardnotes/features';
|
||||
import { Switch } from '@/components/Switch';
|
||||
import {
|
||||
PreferencesGroup,
|
||||
PreferencesSegment,
|
||||
Subtitle,
|
||||
Text,
|
||||
Title,
|
||||
} from '@/preferences/components';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { FeatureIdentifier, FeatureStatus } from '@standardnotes/snjs';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { useCallback, useEffect, useState } from 'preact/hooks';
|
||||
import { usePremiumModal } from '@/components/Premium';
|
||||
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
|
||||
|
||||
type ExperimentalFeatureItem = {
|
||||
identifier: FeatureIdentifier;
|
||||
name: string;
|
||||
description: string;
|
||||
isEnabled: boolean;
|
||||
isEntitled: boolean;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
application: WebApplication;
|
||||
};
|
||||
|
||||
export const LabsPane: FunctionComponent<Props> = ({ application }) => {
|
||||
const [experimentalFeatures, setExperimentalFeatures] = useState<
|
||||
ExperimentalFeatureItem[]
|
||||
>([]);
|
||||
|
||||
const reloadExperimentalFeatures = useCallback(() => {
|
||||
const experimentalFeatures = application.features
|
||||
.getExperimentalFeatures()
|
||||
.map((featureIdentifier) => {
|
||||
const feature = FindNativeFeature(featureIdentifier);
|
||||
return {
|
||||
identifier: featureIdentifier,
|
||||
name: feature?.name ?? featureIdentifier,
|
||||
description: feature?.description ?? '',
|
||||
isEnabled:
|
||||
application.features.isExperimentalFeatureEnabled(
|
||||
featureIdentifier
|
||||
),
|
||||
isEntitled:
|
||||
application.features.getFeatureStatus(featureIdentifier) ===
|
||||
FeatureStatus.Entitled,
|
||||
};
|
||||
});
|
||||
setExperimentalFeatures(experimentalFeatures);
|
||||
}, [application.features]);
|
||||
|
||||
useEffect(() => {
|
||||
reloadExperimentalFeatures();
|
||||
}, [reloadExperimentalFeatures]);
|
||||
|
||||
const premiumModal = usePremiumModal();
|
||||
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Labs</Title>
|
||||
<div>
|
||||
{experimentalFeatures.map(
|
||||
(
|
||||
{ identifier, name, description, isEnabled, isEntitled },
|
||||
index: number
|
||||
) => {
|
||||
const toggleFeature = () => {
|
||||
if (!isEntitled) {
|
||||
premiumModal.activate(name);
|
||||
return;
|
||||
}
|
||||
|
||||
application.features.toggleExperimentalFeature(identifier);
|
||||
reloadExperimentalFeatures();
|
||||
};
|
||||
|
||||
const showHorizontalSeparator =
|
||||
experimentalFeatures.length > 1 &&
|
||||
index !== experimentalFeatures.length - 1;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<Subtitle>{name}</Subtitle>
|
||||
<Text>{description}</Text>
|
||||
</div>
|
||||
<Switch onChange={toggleFeature} checked={isEnabled} />
|
||||
</div>
|
||||
{showHorizontalSeparator && (
|
||||
<HorizontalSeparator classes="mt-5 mb-3" />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
)}
|
||||
{experimentalFeatures.length === 0 && (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<Text>No experimental features available.</Text>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './ErrorReporting';
|
||||
export * from './Tools';
|
||||
export * from './Defaults';
|
||||
export * from './Labs';
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Bridge } from './services/bridge';
|
||||
|
||||
export type StartApplication = (
|
||||
defaultSyncServerHost: string,
|
||||
defaultFilesHostHost: string,
|
||||
bridge: Bridge,
|
||||
enableUnfinishedFeatures: boolean,
|
||||
webSocketUrl: string
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
} from 'mobx';
|
||||
import { ActionsMenuState } from './actions_menu_state';
|
||||
import { FeaturesState } from './features_state';
|
||||
import { FilesState } from './files_state';
|
||||
import { NotesState } from './notes_state';
|
||||
import { NotesViewState } from './notes_view_state';
|
||||
import { NoteTagsState } from './note_tags_state';
|
||||
@@ -89,6 +90,7 @@ export class AppState {
|
||||
readonly tags: TagsState;
|
||||
readonly notesView: NotesViewState;
|
||||
readonly subscription: SubscriptionState;
|
||||
readonly files: FilesState;
|
||||
|
||||
isSessionsModalVisible = false;
|
||||
|
||||
@@ -139,6 +141,7 @@ export class AppState {
|
||||
this,
|
||||
this.appEventObserverRemovers
|
||||
);
|
||||
this.files = new FilesState(application);
|
||||
this.addAppEventObserver();
|
||||
this.streamNotesAndTags();
|
||||
this.onVisibilityChange = () => {
|
||||
@@ -381,7 +384,7 @@ export class AppState {
|
||||
}
|
||||
break;
|
||||
case ApplicationEvent.SyncStatusChanged:
|
||||
this.sync.update(this.application.getSyncStatus());
|
||||
this.sync.update(this.application.sync.getSyncStatus());
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
142
app/assets/javascripts/ui_models/app_state/files_state.ts
Normal file
142
app/assets/javascripts/ui_models/app_state/files_state.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import {
|
||||
ClassicFileReader,
|
||||
StreamingFileReader,
|
||||
StreamingFileSaver,
|
||||
ClassicFileSaver,
|
||||
} from '@standardnotes/filepicker';
|
||||
import { SNFile } from '@standardnotes/snjs';
|
||||
import { addToast, dismissToast, ToastType } from '@standardnotes/stylekit';
|
||||
|
||||
import { WebApplication } from '../application';
|
||||
|
||||
export class FilesState {
|
||||
constructor(private application: WebApplication) {}
|
||||
|
||||
public async downloadFile(file: SNFile): Promise<void> {
|
||||
let downloadingToastId = '';
|
||||
|
||||
try {
|
||||
const saver = StreamingFileSaver.available()
|
||||
? new StreamingFileSaver(file.nameWithExt)
|
||||
: new ClassicFileSaver();
|
||||
|
||||
const isUsingStreamingSaver = saver instanceof StreamingFileSaver;
|
||||
|
||||
if (isUsingStreamingSaver) {
|
||||
await saver.selectFileToSaveTo();
|
||||
}
|
||||
|
||||
downloadingToastId = addToast({
|
||||
type: ToastType.Loading,
|
||||
message: `Downloading file...`,
|
||||
});
|
||||
|
||||
await this.application.files.downloadFile(
|
||||
file,
|
||||
async (decryptedBytes: Uint8Array) => {
|
||||
if (isUsingStreamingSaver) {
|
||||
await saver.pushBytes(decryptedBytes);
|
||||
} else {
|
||||
saver.saveFile(file.nameWithExt, decryptedBytes);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (isUsingStreamingSaver) {
|
||||
await saver.finish();
|
||||
}
|
||||
|
||||
addToast({
|
||||
type: ToastType.Success,
|
||||
message: 'Successfully downloaded file',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
addToast({
|
||||
type: ToastType.Error,
|
||||
message: 'There was an error while downloading the file',
|
||||
});
|
||||
}
|
||||
|
||||
if (downloadingToastId.length > 0) {
|
||||
dismissToast(downloadingToastId);
|
||||
}
|
||||
}
|
||||
|
||||
public async uploadNewFile(fileOrHandle?: File | FileSystemFileHandle) {
|
||||
let toastId = '';
|
||||
|
||||
try {
|
||||
const minimumChunkSize = this.application.files.minimumChunkSize();
|
||||
|
||||
const picker = StreamingFileReader.available()
|
||||
? StreamingFileReader
|
||||
: ClassicFileReader;
|
||||
|
||||
const selectedFiles =
|
||||
fileOrHandle instanceof File
|
||||
? [fileOrHandle]
|
||||
: StreamingFileReader.available() &&
|
||||
fileOrHandle instanceof FileSystemFileHandle
|
||||
? await StreamingFileReader.getFilesFromHandles([fileOrHandle])
|
||||
: await picker.selectFiles();
|
||||
|
||||
const uploadedFiles: SNFile[] = [];
|
||||
|
||||
for (const file of selectedFiles) {
|
||||
const operation = await this.application.files.beginNewFileUpload();
|
||||
|
||||
const onChunk = async (
|
||||
chunk: Uint8Array,
|
||||
index: number,
|
||||
isLast: boolean
|
||||
) => {
|
||||
await this.application.files.pushBytesForUpload(
|
||||
operation,
|
||||
chunk,
|
||||
index,
|
||||
isLast
|
||||
);
|
||||
};
|
||||
|
||||
toastId = addToast({
|
||||
type: ToastType.Loading,
|
||||
message: `Uploading file "${file.name}"...`,
|
||||
});
|
||||
|
||||
const fileResult = await picker.readFile(
|
||||
file,
|
||||
minimumChunkSize,
|
||||
onChunk
|
||||
);
|
||||
|
||||
const uploadedFile = await this.application.files.finishUpload(
|
||||
operation,
|
||||
fileResult.name,
|
||||
fileResult.ext
|
||||
);
|
||||
|
||||
uploadedFiles.push(uploadedFile);
|
||||
|
||||
dismissToast(toastId);
|
||||
addToast({
|
||||
type: ToastType.Success,
|
||||
message: `Uploaded file "${uploadedFile.nameWithExt}"`,
|
||||
});
|
||||
}
|
||||
|
||||
return uploadedFiles;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
if (toastId.length > 0) {
|
||||
dismissToast(toastId);
|
||||
}
|
||||
addToast({
|
||||
type: ToastType.Error,
|
||||
message: 'There was an error while uploading the file',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,7 @@ export class NoteTagsState {
|
||||
|
||||
if (activeNote) {
|
||||
await this.application.addTagHierarchyToNote(activeNote, tag);
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
this.reloadTags();
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,7 @@ export class NoteTagsState {
|
||||
await this.application.changeItem(tag.uuid, (mutator) => {
|
||||
mutator.removeItemAsRelationship(activeNote);
|
||||
});
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
this.reloadTags();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ export class NotesState {
|
||||
mutate,
|
||||
false
|
||||
);
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
}
|
||||
|
||||
setHideSelectedNotePreviews(hide: boolean): void {
|
||||
@@ -403,7 +403,7 @@ export class NotesState {
|
||||
},
|
||||
false
|
||||
);
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
}
|
||||
|
||||
async addTagToSelectedNotes(tag: SNTag): Promise<void> {
|
||||
@@ -419,7 +419,7 @@ export class NotesState {
|
||||
});
|
||||
})
|
||||
);
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
}
|
||||
|
||||
async removeTagFromSelectedNotes(tag: SNTag): Promise<void> {
|
||||
@@ -429,7 +429,7 @@ export class NotesState {
|
||||
mutator.removeItemAsRelationship(note);
|
||||
}
|
||||
});
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
}
|
||||
|
||||
isTagInSelectedNotes(tag: SNTag): boolean {
|
||||
@@ -453,7 +453,7 @@ export class NotesState {
|
||||
})
|
||||
) {
|
||||
this.application.emptyTrash();
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -559,10 +559,6 @@ export class NotesViewState {
|
||||
this.reloadNotes();
|
||||
};
|
||||
|
||||
onSearchInputBlur = () => {
|
||||
this.appState.searchOptions.refreshIncludeProtectedContents();
|
||||
};
|
||||
|
||||
clearFilterText = () => {
|
||||
this.setNoteFilterText('');
|
||||
this.onFilterEnter();
|
||||
|
||||
@@ -208,7 +208,7 @@ export class TagsState {
|
||||
|
||||
this.assignParent(createdTag.uuid, parent.uuid);
|
||||
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
|
||||
runInAction(() => {
|
||||
this.selected = createdTag as SNTag;
|
||||
@@ -364,7 +364,7 @@ export class TagsState {
|
||||
await this.application.setTagParent(futureParent, tag);
|
||||
}
|
||||
|
||||
await this.application.sync();
|
||||
await this.application.sync.sync();
|
||||
}
|
||||
|
||||
get rootTags(): SNTag[] {
|
||||
@@ -507,7 +507,7 @@ export class TagsState {
|
||||
}
|
||||
|
||||
const insertedTag = await this.application.createTagOrSmartView(newTitle);
|
||||
this.application.sync();
|
||||
this.application.sync.sync();
|
||||
runInAction(() => {
|
||||
this.selected = insertedTag as SNTag;
|
||||
});
|
||||
|
||||
@@ -48,7 +48,6 @@ export class WebApplication extends SNApplication {
|
||||
platform: Platform,
|
||||
identifier: string,
|
||||
defaultSyncServerHost: string,
|
||||
defaultFilesHostHost: string,
|
||||
public bridge: Bridge,
|
||||
webSocketUrl: string,
|
||||
runtime: Runtime
|
||||
@@ -61,7 +60,6 @@ export class WebApplication extends SNApplication {
|
||||
alertService: new AlertService(),
|
||||
identifier,
|
||||
defaultHost: defaultSyncServerHost,
|
||||
defaultFilesHost: defaultFilesHostHost,
|
||||
appVersion: bridge.appVersion,
|
||||
webSocketUrl: webSocketUrl,
|
||||
runtime,
|
||||
|
||||
@@ -21,7 +21,6 @@ import { InternalEventBus } from '@standardnotes/services';
|
||||
export class ApplicationGroup extends SNApplicationGroup {
|
||||
constructor(
|
||||
private defaultSyncServerHost: string,
|
||||
private defaultFilesHostHost: string,
|
||||
private bridge: Bridge,
|
||||
private runtime: Runtime,
|
||||
private webSocketUrl: string
|
||||
@@ -52,7 +51,6 @@ export class ApplicationGroup extends SNApplicationGroup {
|
||||
platform,
|
||||
descriptor.identifier,
|
||||
this.defaultSyncServerHost,
|
||||
this.defaultFilesHostHost,
|
||||
this.bridge,
|
||||
this.webSocketUrl,
|
||||
this.runtime
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Platform, platformFromString } from '@standardnotes/snjs';
|
||||
import { IsDesktopPlatform, IsWebPlatform } from '@/version';
|
||||
import { EMAIL_REGEX } from '../constants';
|
||||
import {
|
||||
BYTES_IN_ONE_KILOBYTE,
|
||||
BYTES_IN_ONE_MEGABYTE,
|
||||
EMAIL_REGEX,
|
||||
} from '../constants';
|
||||
export { isMobile } from './isMobile';
|
||||
|
||||
declare const process: {
|
||||
|
||||
@@ -52,11 +52,11 @@
|
||||
|
||||
.filter-section {
|
||||
clear: left;
|
||||
height: 28px;
|
||||
margin-top: 14px;
|
||||
max-height: 80px;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.filter-bar {
|
||||
background-color: var(--sn-stylekit-contrast-background-color);
|
||||
@@ -71,6 +71,20 @@
|
||||
border-color: transparent;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.search-options {
|
||||
margin-top: 10px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat( 3, 1fr );
|
||||
gap: .5rem;
|
||||
|
||||
font-size: var(--sn-stylekit-font-size-p);
|
||||
white-space: nowrap;
|
||||
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#search-clear-button {
|
||||
@@ -86,9 +100,9 @@
|
||||
line-height: 17px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
top: 20%;
|
||||
transform: translateY(-50%);
|
||||
right: 36px;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
transition: background-color 0.15s linear;
|
||||
|
||||
@@ -258,6 +258,11 @@
|
||||
margin-right: 3rem;
|
||||
}
|
||||
|
||||
.mx-4 {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.my-0\.5 {
|
||||
margin-top: 0.125rem;
|
||||
margin-bottom: 0.125rem;
|
||||
@@ -328,6 +333,10 @@
|
||||
width: 0.75rem;
|
||||
}
|
||||
|
||||
.w-18 {
|
||||
width: 4.5rem;
|
||||
}
|
||||
|
||||
.w-26 {
|
||||
width: 6.5rem;
|
||||
}
|
||||
@@ -428,6 +437,10 @@
|
||||
max-height: 1.25rem;
|
||||
}
|
||||
|
||||
.max-h-110 {
|
||||
max-height: 27.5rem;
|
||||
}
|
||||
|
||||
.border-danger {
|
||||
border-color: var(--sn-stylekit-danger-color);
|
||||
}
|
||||
@@ -436,6 +449,10 @@
|
||||
border-color: var(--sn-stylekit-neutral-contrast-color);
|
||||
}
|
||||
|
||||
.border-secondary {
|
||||
border-color: var(--sn-stylekit-secondary-border-color);
|
||||
}
|
||||
|
||||
.sn-component .border-r-1px {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
@@ -513,6 +530,11 @@
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.px-1\.5 {
|
||||
padding-left: 0.375rem;
|
||||
padding-right: 0.375rem;
|
||||
}
|
||||
|
||||
.px-2\.5 {
|
||||
padding-left: 0.625rem;
|
||||
padding-right: 0.625rem;
|
||||
@@ -572,6 +594,10 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.top-30\% {
|
||||
top: 30%;
|
||||
}
|
||||
@@ -636,6 +662,10 @@
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.right-2 {
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.-right-2 {
|
||||
right: -0.5rem;
|
||||
}
|
||||
@@ -902,6 +932,10 @@
|
||||
var(--sn-stylekit-info-color) -1px -1px 0px 0px inset;
|
||||
}
|
||||
|
||||
.focus\:shadow-bottom:focus {
|
||||
box-shadow: currentcolor 0px -1px 0px 0px inset, currentcolor 0px 1px 0px 0px;
|
||||
}
|
||||
|
||||
.bg-note-size-warning {
|
||||
background-color: rgba(235, 173, 0, 0.08);
|
||||
}
|
||||
@@ -938,3 +972,44 @@
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.color-neutral-contrast {
|
||||
color: var(--sn-stylekit-neutral-contrast-color);
|
||||
}
|
||||
|
||||
.active\:bg-info:active {
|
||||
background-color: var(--sn-stylekit-info-color);
|
||||
}
|
||||
|
||||
.active\:border-info:active {
|
||||
border-color: var(--sn-stylekit-info-color);
|
||||
}
|
||||
|
||||
.active\:color-neutral-contrast:active {
|
||||
color: var(--sn-stylekit-neutral-contrast-color);
|
||||
}
|
||||
|
||||
.transition {
|
||||
transition-property: color, background-color, border-color,
|
||||
text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter,
|
||||
backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
.animate-fade-from-top {
|
||||
animation: fade-from-top 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fade-from-top {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-20%);
|
||||
}
|
||||
75% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
}
|
||||
|
||||
30
package.json
30
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "standard-notes-web",
|
||||
"version": "3.12.1",
|
||||
"version": "3.13.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -27,21 +27,22 @@
|
||||
"@babel/preset-typescript": "^7.16.7",
|
||||
"@reach/disclosure": "^0.16.2",
|
||||
"@reach/visually-hidden": "^0.16.0",
|
||||
"@standardnotes/responses": "^1.1.7",
|
||||
"@standardnotes/services": "^1.4.0",
|
||||
"@standardnotes/stylekit": "5.14.0",
|
||||
"@standardnotes/responses": "1.3.7",
|
||||
"@standardnotes/services": "1.5.9",
|
||||
"@standardnotes/stylekit": "5.15.0",
|
||||
"@svgr/webpack": "^6.2.1",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/lodash": "^4.14.179",
|
||||
"@types/react": "^17.0.39",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"@types/react": "^17.0.40",
|
||||
"@types/wicg-file-system-access": "^2020.9.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
||||
"@typescript-eslint/parser": "^5.14.0",
|
||||
"apply-loader": "^2.0.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.2.3",
|
||||
"connect": "^3.7.0",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"css-loader": "^6.7.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"dotenv": "^16.0.0",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
@@ -71,22 +72,23 @@
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bugsnag/js": "^7.16.1",
|
||||
"@bugsnag/js": "^7.16.2",
|
||||
"@reach/alert": "^0.16.0",
|
||||
"@reach/alert-dialog": "^0.16.2",
|
||||
"@reach/checkbox": "^0.16.0",
|
||||
"@reach/dialog": "^0.16.2",
|
||||
"@reach/listbox": "^0.16.2",
|
||||
"@reach/tooltip": "^0.16.2",
|
||||
"@standardnotes/components": "1.7.9",
|
||||
"@standardnotes/features": "1.34.1",
|
||||
"@standardnotes/settings": "^1.11.5",
|
||||
"@standardnotes/components": "1.7.11",
|
||||
"@standardnotes/features": "1.34.8",
|
||||
"@standardnotes/filepicker": "1.8.0",
|
||||
"@standardnotes/settings": "1.12.0",
|
||||
"@standardnotes/sncrypto-web": "1.7.3",
|
||||
"@standardnotes/snjs": "2.73.2",
|
||||
"@standardnotes/snjs": "2.79.5",
|
||||
"mobx": "^6.4.2",
|
||||
"mobx-react-lite": "^3.3.0",
|
||||
"preact": "^10.6.6",
|
||||
"qrcode.react": "^1.0.1",
|
||||
"qrcode.react": "^2.0.0",
|
||||
"react-dnd": "^15.1.1",
|
||||
"react-dnd-html5-backend": "^15.1.2",
|
||||
"react-dnd-touch-backend": "^15.1.1"
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
"binary": "21df7e16f57f6aede482b1281ff1c6d2ad9747fc9c3059e483aea7a03a3a8224"
|
||||
},
|
||||
"org.standardnotes.theme-dynamic": {
|
||||
"version": "1.0.3",
|
||||
"base64": "81d1465772d0233e3a9648ed0e0ea289f51d1dcf12fef8b29b25488268256521",
|
||||
"binary": "42d28f3598669b801821879f0b7e26389300f151bfbc566b11c214e6091719ed"
|
||||
"version": "1.0.4",
|
||||
"base64": "92038664655c6cb1d6b422b9546da1a98da58b0251394cf2a485aed9b1774c07",
|
||||
"binary": "d05185aca67768f35227565dacce030fdff233563f119781baaf4e95f9198115"
|
||||
},
|
||||
"org.standardnotes.code-editor": {
|
||||
"version": "1.3.12",
|
||||
@@ -95,8 +95,8 @@
|
||||
"binary": "88d4b9a6ff94b1f6cba884787e051cda3cd8073022984b63f7d3b13157aa6e2a"
|
||||
},
|
||||
"org.standardnotes.markdown-visual-editor": {
|
||||
"version": "1.0.1",
|
||||
"base64": "b0984c4a35b45eb4035a2dcdc0ba71b7b68b5dffcdeea53d0c5fbcbc0ff85894",
|
||||
"binary": "d8ac1028cf50d60f7a80010dd1c6047f57e01cd066ea163f169d514d1e7d2e71"
|
||||
"version": "1.0.2",
|
||||
"base64": "40e25db3e2b2d3ce18088ff80b5039a9f32e9a2151d08af7a29a5beb520edc3f",
|
||||
"binary": "9327b7f312974c754f979493dfd6f3652f6b98c2be44d6b82b9d3e27d5a901c3"
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,25 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.f7535a78.chunk.css",
|
||||
"main.js": "./static/js/main.7192e457.chunk.js",
|
||||
"main.js.map": "./static/js/main.7192e457.chunk.js.map",
|
||||
"runtime-main.js": "./static/js/runtime-main.54ae45a0.js",
|
||||
"runtime-main.js.map": "./static/js/runtime-main.54ae45a0.js.map",
|
||||
"main.js": "./static/js/main.18531fb0.chunk.js",
|
||||
"main.js.map": "./static/js/main.18531fb0.chunk.js.map",
|
||||
"runtime-main.js": "./static/js/runtime-main.0f6dab54.js",
|
||||
"runtime-main.js.map": "./static/js/runtime-main.0f6dab54.js.map",
|
||||
"static/css/2.4ff8d936.chunk.css": "./static/css/2.4ff8d936.chunk.css",
|
||||
"static/js/2.2cfce575.chunk.js": "./static/js/2.2cfce575.chunk.js",
|
||||
"static/js/2.2cfce575.chunk.js.map": "./static/js/2.2cfce575.chunk.js.map",
|
||||
"static/js/3.bf2ee733.chunk.js": "./static/js/3.bf2ee733.chunk.js",
|
||||
"static/js/3.bf2ee733.chunk.js.map": "./static/js/3.bf2ee733.chunk.js.map",
|
||||
"static/js/2.6a3180fe.chunk.js": "./static/js/2.6a3180fe.chunk.js",
|
||||
"static/js/2.6a3180fe.chunk.js.map": "./static/js/2.6a3180fe.chunk.js.map",
|
||||
"index.html": "./index.html",
|
||||
"static/css/2.4ff8d936.chunk.css.map": "./static/css/2.4ff8d936.chunk.css.map",
|
||||
"static/css/main.f7535a78.chunk.css.map": "./static/css/main.f7535a78.chunk.css.map",
|
||||
"static/js/2.2cfce575.chunk.js.LICENSE.txt": "./static/js/2.2cfce575.chunk.js.LICENSE.txt",
|
||||
"static/js/2.6a3180fe.chunk.js.LICENSE.txt": "./static/js/2.6a3180fe.chunk.js.LICENSE.txt",
|
||||
"static/js/main.18531fb0.chunk.js.LICENSE.txt": "./static/js/main.18531fb0.chunk.js.LICENSE.txt",
|
||||
"static/media/main.scss": "./static/media/roboto-vietnamese-400-normal.c0bec65d.woff2"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/runtime-main.54ae45a0.js",
|
||||
"static/js/runtime-main.0f6dab54.js",
|
||||
"static/css/2.4ff8d936.chunk.css",
|
||||
"static/js/2.2cfce575.chunk.js",
|
||||
"static/js/2.6a3180fe.chunk.js",
|
||||
"static/css/main.f7535a78.chunk.css",
|
||||
"static/js/main.7192e457.chunk.js"
|
||||
"static/js/main.18531fb0.chunk.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="A lightweight WYSIWYG markdown editor, derivated from Milkdown editor"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>Markdown Visual</title><link href="./static/css/2.4ff8d936.chunk.css" rel="stylesheet"><link href="./static/css/main.f7535a78.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],f=0,p=[];f<i.length;f++)a=i[f],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&p.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(s&&s(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var c=t[i];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{3:"bf2ee733"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./",a.oe=function(e){throw console.error(e),e};var i=this["webpackJsonp@standardnotes/markdown-visual"]=this["webpackJsonp@standardnotes/markdown-visual"]||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var s=c;t()}([])</script><script src="./static/js/2.2cfce575.chunk.js"></script><script src="./static/js/main.7192e457.chunk.js"></script></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="A lightweight WYSIWYG markdown editor, derivated from Milkdown editor"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>Markdown Visual</title><link href="./static/css/2.4ff8d936.chunk.css" rel="stylesheet"><link href="./static/css/main.f7535a78.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,a,l=r[0],i=r[1],f=r[2],c=0,s=[];c<l.length;c++)a=l[c],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var i=t[l];0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./";var l=this["webpackJsonp@standardnotes/markdown-visual"]=this["webpackJsonp@standardnotes/markdown-visual"]||[],i=l.push.bind(l);l.push=r,l=l.slice();for(var f=0;f<l.length;f++)r(l[f]);var p=i;t()}([])</script><script src="./static/js/2.6a3180fe.chunk.js"></script><script src="./static/js/main.18531fb0.chunk.js"></script></body></html>
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Determine if an object is a Buffer
|
||||
*
|
||||
* @author Feross Aboukhadijeh <https://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prism: Lightweight, robust, elegant syntax highlighting
|
||||
*
|
||||
* @license MIT <https://opensource.org/licenses/MIT>
|
||||
* @author Lea Verou <https://lea.verou.me>
|
||||
* @namespace
|
||||
* @public
|
||||
*/
|
||||
|
||||
/** @license React v0.20.2
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* @license
|
||||
* Lodash (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
|
||||
* Build: `lodash include="includes,merge,filter,map,remove,find,omit,pull,cloneDeep,pick,uniq,sortedIndexBy,mergeWith"`
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
!function(e){function r(r){for(var n,a,l=r[0],i=r[1],f=r[2],c=0,s=[];c<l.length;c++)a=l[c],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var i=t[l];0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./";var l=this["webpackJsonp@standardnotes/markdown-visual"]=this["webpackJsonp@standardnotes/markdown-visual"]||[],i=l.push.bind(l);l.push=r,l=l.slice();for(var f=0;f<l.length;f++)r(l[f]);var p=i;t()}([]);
|
||||
//# sourceMappingURL=runtime-main.0f6dab54.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "@standardnotes/markdown-visual",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"author": "Johnny Almonte <johnny@standardnotes.com>",
|
||||
"description": "A lightweight WYSIWYG markdown editor, derivated from Milkdown editor",
|
||||
"description": "A lightweight WYSIWYG markdown editor for Standard Notes, derived from Milkdown.",
|
||||
"keywords": [
|
||||
"Standard Notes",
|
||||
"Standard Notes Extensions"
|
||||
@@ -39,7 +39,6 @@
|
||||
"@milkdown/plugin-clipboard": "^5.5.0",
|
||||
"@milkdown/plugin-cursor": "^5.5.0",
|
||||
"@milkdown/plugin-diagram": "^5.5.0",
|
||||
"@milkdown/plugin-emoji": "^5.5.0",
|
||||
"@milkdown/plugin-history": "^5.5.0",
|
||||
"@milkdown/plugin-indent": "^5.5.0",
|
||||
"@milkdown/plugin-listener": "^5.5.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.section.tags,
|
||||
navigation {
|
||||
flex: none !important;
|
||||
width: 120px !important;
|
||||
width: 190px !important;
|
||||
transition: width 0.25s;
|
||||
}
|
||||
|
||||
@@ -10,21 +10,21 @@ navigation {
|
||||
.section.tags:hover,
|
||||
navigation:hover {
|
||||
flex: initial;
|
||||
width: 180px !important;
|
||||
width: 250px !important;
|
||||
transition: width 0.25s;
|
||||
}
|
||||
|
||||
.section.notes,
|
||||
notes-view {
|
||||
flex: none !important;
|
||||
width: 200px !important;
|
||||
width: 270px !important;
|
||||
transition: width 0.25s;
|
||||
}
|
||||
|
||||
.section.notes:hover,
|
||||
notes-view:hover {
|
||||
flex: initial;
|
||||
width: 350px !important;
|
||||
width: 420px !important;
|
||||
transition: width 0.25s;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "AAAA;;UAEW;EACT,IAAI,EAAE,eAAe;EACrB,KAAK,EAAE,gBAAgB;EACvB,UAAU,EAAE,WAAW;;;AAGzB;;gBAEiB;EACf,IAAI,EAAE,OAAO;EACb,KAAK,EAAE,gBAAgB;EACvB,UAAU,EAAE,WAAW;;;AAGzB;UACW;EACT,IAAI,EAAE,eAAe;EACrB,KAAK,EAAE,gBAAgB;EACvB,UAAU,EAAE,WAAW;;;AAGzB;gBACiB;EACf,IAAI,EAAE,OAAO;EACb,KAAK,EAAE,gBAAgB;EACvB,UAAU,EAAE,WAAW",
|
||||
"sources": ["../src/main.scss"],
|
||||
"names": [],
|
||||
"file": "dist.css"
|
||||
}
|
||||
{"version":3,"sourceRoot":"","sources":["../src/main.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA","file":"dist.css"}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sn-theme-dynamic",
|
||||
"version": "1.0.3",
|
||||
"name": "@standardnotes/dynamic-theme",
|
||||
"version": "1.0.4",
|
||||
"main": "dist/dist.css",
|
||||
"devDependencies": {
|
||||
"grunt": "^1.0.1",
|
||||
|
||||
265
yarn.lock
265
yarn.lock
@@ -1751,10 +1751,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@bugsnag/browser@^7.16.1":
|
||||
version "7.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@bugsnag/browser/-/browser-7.16.1.tgz#652aa3ed64e51ba0015878d252a08917429bba03"
|
||||
integrity sha512-Tq9fWpwmqdOsbedYL67GzsTKrG5MERIKtnKCi5FyvFjTj143b6as0pwj7LWQ+Eh8grWlR7S11+VvJmb8xnY8Tg==
|
||||
"@bugsnag/browser@^7.16.2":
|
||||
version "7.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@bugsnag/browser/-/browser-7.16.2.tgz#b6fc7ebaeae4800d195b660abc770caf33670e03"
|
||||
integrity sha512-iBbAmjTDe0I6WPTHi3wIcmKu3ykydtT6fc8atJA65rzgDLMlTM1Wnwz4Ny1cn0bVouLGa48BRiOJ27Rwy7QRYA==
|
||||
dependencies:
|
||||
"@bugsnag/core" "^7.16.1"
|
||||
|
||||
@@ -1774,18 +1774,18 @@
|
||||
resolved "https://registry.yarnpkg.com/@bugsnag/cuid/-/cuid-3.0.0.tgz#2ee7642a30aee6dc86f5e7f824653741e42e5c35"
|
||||
integrity sha512-LOt8aaBI+KvOQGneBtpuCz3YqzyEAehd1f3nC5yr9TIYW1+IzYKa2xWS4EiMz5pPOnRPHkyyS5t/wmSmN51Gjg==
|
||||
|
||||
"@bugsnag/js@^7.16.1":
|
||||
version "7.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@bugsnag/js/-/js-7.16.1.tgz#4a4ec2c7f3e047333e7d15eb53cb11f165b7067f"
|
||||
integrity sha512-yb83OmsbIMDJhX3hHhbHl5StN72feqdr/Ctq7gqsdcfOHNb2121Edf2EbegPJKZhFqSik66vWwiVbGJ6CdS/UQ==
|
||||
"@bugsnag/js@^7.16.2":
|
||||
version "7.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@bugsnag/js/-/js-7.16.2.tgz#fb15ec9cc5980f0b210aecc7b740274e50400a91"
|
||||
integrity sha512-AzV0PtG3SZt+HnA2JmRJeI60aDNZsIJbEEAZIWZeATvWBt5RdVdsWKllM1SkTvURfxfdAVd4Xry3BgVrh8nEbg==
|
||||
dependencies:
|
||||
"@bugsnag/browser" "^7.16.1"
|
||||
"@bugsnag/node" "^7.16.1"
|
||||
"@bugsnag/browser" "^7.16.2"
|
||||
"@bugsnag/node" "^7.16.2"
|
||||
|
||||
"@bugsnag/node@^7.16.1":
|
||||
version "7.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@bugsnag/node/-/node-7.16.1.tgz#473bb6eeb346b418295b49e4c4576e0004af4901"
|
||||
integrity sha512-9zBA1IfDTbLKMoDltdhELpTd1e+b5+vUW4j40zGA+4SYIe64XNZKShfqRdvij7embvC1iHQ9UpuPRSk60P6Dng==
|
||||
"@bugsnag/node@^7.16.2":
|
||||
version "7.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@bugsnag/node/-/node-7.16.2.tgz#8ac1b41786306d8917fb9fe222ada74fe0c4c6d5"
|
||||
integrity sha512-V5pND701cIYGzjjTwt0tuvAU1YyPB9h7vo5F/DzrDHRPmCINA/oVbc0Twco87knc2VPe8ntGFqTicTY65iOWzg==
|
||||
dependencies:
|
||||
"@bugsnag/core" "^7.16.1"
|
||||
byline "^5.0.0"
|
||||
@@ -2313,10 +2313,10 @@
|
||||
dependencies:
|
||||
"@standardnotes/common" "^1.15.3"
|
||||
|
||||
"@standardnotes/auth@^3.17.3":
|
||||
version "3.17.3"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.17.3.tgz#a00f10faa0fb2a7dd76509d3b678f85818aad63c"
|
||||
integrity sha512-tb5ylXuDBPhgeZZynNsMk83N74NMMV9z6M9hyrwuK5HbKWM5r5L9U8lwFawG8flqTKpYzPeWxmaRFZT/5qR22Q==
|
||||
"@standardnotes/auth@^3.17.4":
|
||||
version "3.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.17.4.tgz#ab2449a280ee6ec794fe397c9d8387e105c6c644"
|
||||
integrity sha512-0710hUiYoRFjABfUFPlyOIyCMx0gC0rlJtFdPYK7WHXf0bfxO0JiSXeWbNSvV0QVGqHIkcUjGmdyE6cJEKTh9g==
|
||||
dependencies:
|
||||
"@standardnotes/common" "^1.15.3"
|
||||
jsonwebtoken "^8.5.1"
|
||||
@@ -2326,60 +2326,66 @@
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.15.3.tgz#0b8ce48b81b260abe2d405431fb04aacb44b5a01"
|
||||
integrity sha512-9oh/W3sFQYyA5Vabcbu6BUkLVkFq/25Q5EK9KCd4aT9QnDJ9JQlTtzDmTk1jYuM6rnccsJ6SW2pcWjbi9FVniw==
|
||||
|
||||
"@standardnotes/components@1.7.9":
|
||||
version "1.7.9"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/components/-/components-1.7.9.tgz#41e5fdbcee250b9b3c18045dad8998c6f668307b"
|
||||
integrity sha512-/+Paw6ry/IS9ldYUM/lgC4O6qwl1fukWvNw65IMKyB9LMY3+xTh/I2BfnWynP117pVPxtu3/2+FBEnx04KvQwg==
|
||||
"@standardnotes/components@1.7.11":
|
||||
version "1.7.11"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/components/-/components-1.7.11.tgz#791c1e1bef5bc223f34c423e4ddd443fe8a699d3"
|
||||
integrity sha512-V8gtuLMbn0ldRQLZj0iKrm5PRufHdRGbSQ32/u0w1M6dTE42wKUYuMNhPwkn9cNEaYzhhcHRD01L/lYEepBZBQ==
|
||||
|
||||
"@standardnotes/domain-events@^2.23.21":
|
||||
version "2.23.21"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.23.21.tgz#bbf752ee7a0fd08b9fb675e81b46d3c10bbf89e9"
|
||||
integrity sha512-YPpwy+QrDziBOpjt5cOIZwY47fOddN3038+NTRSqxi4h/D8hU+U5O8dGl2XktENEq9DqVJ78OVmWBjkA2FlsEQ==
|
||||
"@standardnotes/domain-events@^2.24.5":
|
||||
version "2.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.24.5.tgz#f4699b2241e0e51829d88ec8afe1b8d00d5fe37d"
|
||||
integrity sha512-NrbeaQ0Yl+56cMlXOLSISHCpkiRTAcmmRtIEPAqn0V7RBeRXqKfy6Fo5OUPSuGtYinQfbKBLi5rxIC/rqhMZFg==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.17.3"
|
||||
"@standardnotes/features" "^1.34.1"
|
||||
"@standardnotes/auth" "^3.17.4"
|
||||
"@standardnotes/features" "^1.34.8"
|
||||
|
||||
"@standardnotes/features@1.34.1", "@standardnotes/features@^1.34.1":
|
||||
version "1.34.1"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.34.1.tgz#da2e37a437ba274dfbdb9e0f5ab9de3ee95689ee"
|
||||
integrity sha512-ILc4mSLoAE6S24GquNAS5TNLqM+RBKx8zyfFVgKEUB2sF00341HDi+/l5MuCCds2VnhTvIuVcIsCrPlj9NVmFg==
|
||||
"@standardnotes/features@1.34.8", "@standardnotes/features@^1.34.8":
|
||||
version "1.34.8"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.34.8.tgz#a82b7648706bdf62355eff6df1f4ccadf0b83afa"
|
||||
integrity sha512-0ttEnBjod7zANER0sU6TzCz0RHSt+p1lQ9xzg9a3z6Azjz8WAJkQAMeIBJNgym8EPVI8QUjKFyz/Rdo0PNAyng==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.17.3"
|
||||
"@standardnotes/auth" "^3.17.4"
|
||||
"@standardnotes/common" "^1.15.3"
|
||||
|
||||
"@standardnotes/payloads@^1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/payloads/-/payloads-1.3.2.tgz#3255abf6e2a2385c73e7998705066b828e5a74e0"
|
||||
integrity sha512-SnDqdQXyEWett/Y33crvNnDGwv4BfCkoHId99fLd+UL5uTgXpuFgkd/AVHg+mOT5xC3+KAR8zdUCmQSXTRuysg==
|
||||
"@standardnotes/filepicker@1.8.0":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.8.0.tgz#f8d85350c4b4022235e3017b0b2c7841882eef4f"
|
||||
integrity sha512-xgFoD+aHFCKV5pAbhKNCyyhUL18G9l2Aep6eiQ5gxB55l8CcNHlLBi5qw5i1we07NdCwIJ3yP3aVKI+7qe22yQ==
|
||||
|
||||
"@standardnotes/payloads@^1.4.7":
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/payloads/-/payloads-1.4.7.tgz#e722a117a1bb02b76555cc6b164eefdb765bea9f"
|
||||
integrity sha512-0y7fDqu1OBMhvIrQDkJbsYEzpPqodSM9X8c5s6hVCs4GtriQcw1JAjsekRyV2/9iKd8GiOldNNWBDDYre+78gQ==
|
||||
dependencies:
|
||||
"@standardnotes/applications" "^1.1.3"
|
||||
"@standardnotes/common" "^1.15.3"
|
||||
"@standardnotes/features" "^1.34.1"
|
||||
"@standardnotes/features" "^1.34.8"
|
||||
"@standardnotes/utils" "^1.2.3"
|
||||
|
||||
"@standardnotes/responses@^1.1.7":
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.1.7.tgz#ab982f94693f2e1f967809bdb5e3863182770b14"
|
||||
integrity sha512-1q8+eGjvttleesciHAOTe6u478W3UpEy+0StT8ZL3miFWzIyCLXJmnxNbYfYCm6oZ+t2rwhW+AzmnMRhG2cOUA==
|
||||
"@standardnotes/responses@1.3.7", "@standardnotes/responses@^1.3.7":
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.3.7.tgz#6304d55613df77342c21336b8c4000a65d3bb42f"
|
||||
integrity sha512-RYIWNB6RinZmOSre6lfLmFAdpN0cSWb5PCjmYAThedkor21iK//9yhOG5xKChqIfB+Y+kaKFfnm8+BtONcMD0A==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.17.3"
|
||||
"@standardnotes/auth" "^3.17.4"
|
||||
"@standardnotes/common" "^1.15.3"
|
||||
"@standardnotes/features" "^1.34.1"
|
||||
"@standardnotes/payloads" "^1.3.2"
|
||||
"@standardnotes/features" "^1.34.8"
|
||||
"@standardnotes/payloads" "^1.4.7"
|
||||
|
||||
"@standardnotes/services@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.4.0.tgz#776ee5d022e4512844af1a284a2e90f599217218"
|
||||
integrity sha512-wO0LQ+qMG0bfH0HNPulsO8nZ2Z1Y84NLP0fZdMdtqiuaCi1GrM/PUlcL/fpXCJKNeTKoYa8Dh4PfF8DOjalKXg==
|
||||
"@standardnotes/services@1.5.9", "@standardnotes/services@^1.5.9":
|
||||
version "1.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.5.9.tgz#4356c739659145553fafe87d59f98db6c9dc4ad8"
|
||||
integrity sha512-EiiAbFGwsqwgb2cFLDKt1Jb9LpuKynrVwYtDUoxZlM9FiRvcmQdK9w+0upS/mthcreaiKHjdkSuQMwJkCaU3rw==
|
||||
dependencies:
|
||||
"@standardnotes/applications" "^1.1.3"
|
||||
"@standardnotes/common" "^1.15.3"
|
||||
"@standardnotes/responses" "^1.3.7"
|
||||
"@standardnotes/utils" "^1.2.3"
|
||||
|
||||
"@standardnotes/settings@^1.11.5":
|
||||
version "1.11.5"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.11.5.tgz#792bf3e0505065486f521b2f19c2bf1081b8fa5e"
|
||||
integrity sha512-n6StAS3nBgs7Lia5mOt3+H4Xd6hatCcHFx83paFq9kdI1cKbqn7oFF4g/rUbWPy4nsx+96zBehB6EhsJ2MGzyQ==
|
||||
"@standardnotes/settings@1.12.0", "@standardnotes/settings@^1.12.0":
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.12.0.tgz#43f3dd7f015f726b1ed88a48fcc3737899116cd5"
|
||||
integrity sha512-w6S5TT7KRpvUb+JsXZ7ucWPjlWRtpKQdsyT7cLs66ynKRXxUn40hf4kA8T9FhuLAKbG+wIYDrAZl3FRk+HvDWQ==
|
||||
|
||||
"@standardnotes/sncrypto-common@^1.7.3":
|
||||
version "1.7.3"
|
||||
@@ -2395,27 +2401,27 @@
|
||||
buffer "^6.0.3"
|
||||
libsodium-wrappers "^0.7.9"
|
||||
|
||||
"@standardnotes/snjs@2.73.2":
|
||||
version "2.73.2"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.73.2.tgz#5361d73c861b990d06821d5a822dcb0893830a43"
|
||||
integrity sha512-oGPZmzX+tNWQFxmvrQRtsqlofbW25YUOapyEPN8oVgfwV98r8EVzpUGXHWkog3ZW6oO3aO22Rhtk0D92d0CqtQ==
|
||||
"@standardnotes/snjs@2.79.5":
|
||||
version "2.79.5"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.79.5.tgz#2df9ef88d0c0ffead2e0044249c5a833921d94dc"
|
||||
integrity sha512-IEZtHg6Ga4/R2QUMbJhD4MEOvkNuBtiNYrokpqrf5kzGGOvL3b/RwcSWUW8zjBS1WxDSGmIo3WBKze3FXhfdEA==
|
||||
dependencies:
|
||||
"@standardnotes/applications" "^1.1.3"
|
||||
"@standardnotes/auth" "^3.17.3"
|
||||
"@standardnotes/auth" "^3.17.4"
|
||||
"@standardnotes/common" "^1.15.3"
|
||||
"@standardnotes/domain-events" "^2.23.21"
|
||||
"@standardnotes/features" "^1.34.1"
|
||||
"@standardnotes/payloads" "^1.3.2"
|
||||
"@standardnotes/responses" "^1.1.7"
|
||||
"@standardnotes/services" "^1.4.0"
|
||||
"@standardnotes/settings" "^1.11.5"
|
||||
"@standardnotes/domain-events" "^2.24.5"
|
||||
"@standardnotes/features" "^1.34.8"
|
||||
"@standardnotes/payloads" "^1.4.7"
|
||||
"@standardnotes/responses" "^1.3.7"
|
||||
"@standardnotes/services" "^1.5.9"
|
||||
"@standardnotes/settings" "^1.12.0"
|
||||
"@standardnotes/sncrypto-common" "^1.7.3"
|
||||
"@standardnotes/utils" "^1.2.3"
|
||||
|
||||
"@standardnotes/stylekit@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.14.0.tgz#806b7d896fb94de8be72e762dd205beefff05fd7"
|
||||
integrity sha512-zCB8QFPUTe+063RsLNCudkP6FY6ujKC3iE2pD6ak4htpXtW6DndwYbhEffuiWIjbRzDGAy0YyQ7y8V19Jw7ElQ==
|
||||
"@standardnotes/stylekit@5.15.0":
|
||||
version "5.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.15.0.tgz#14c0ff5c4e40d4afa9ea6fec0431934e1184a18f"
|
||||
integrity sha512-BR78DIdXo8fxzNPruiugiQJfgT7usUFWxJ0CPnjhk3hshYM2/kleHdcrahYdi2Rs5MR7chhbsvUFUWHxykOaRg==
|
||||
dependencies:
|
||||
"@nanostores/preact" "^0.1.3"
|
||||
"@reach/listbox" "^0.16.2"
|
||||
@@ -2767,10 +2773,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
|
||||
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
|
||||
|
||||
"@types/react@^17.0.39":
|
||||
version "17.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce"
|
||||
integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==
|
||||
"@types/react@^17.0.40":
|
||||
version "17.0.40"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.40.tgz#dc010cee6254d5239a138083f3799a16638e6bad"
|
||||
integrity sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
@@ -2813,6 +2819,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
||||
|
||||
"@types/wicg-file-system-access@^2020.9.5":
|
||||
version "2020.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.5.tgz#4a0c8f3d1ed101525f329e86c978f7735404474f"
|
||||
integrity sha512-UYK244awtmcUYQfs7FR8710MJcefL2WvkyHMjA8yJzxd1mo0Gfn88sRZ1Bls7hiUhA2w7ne1gpJ9T5g3G0wOyA==
|
||||
|
||||
"@types/ws@^8.2.2":
|
||||
version "8.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21"
|
||||
@@ -2832,14 +2843,14 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^5.13.0":
|
||||
version "5.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.13.0.tgz#2809052b85911ced9c54a60dac10e515e9114497"
|
||||
integrity sha512-vLktb2Uec81fxm/cfz2Hd6QaWOs8qdmVAZXLdOBX6JFJDhf6oDZpMzZ4/LZ6SFM/5DgDcxIMIvy3F+O9yZBuiQ==
|
||||
"@typescript-eslint/eslint-plugin@^5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.14.0.tgz#5119b67152356231a0e24b998035288a9cd21335"
|
||||
integrity sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.13.0"
|
||||
"@typescript-eslint/type-utils" "5.13.0"
|
||||
"@typescript-eslint/utils" "5.13.0"
|
||||
"@typescript-eslint/scope-manager" "5.14.0"
|
||||
"@typescript-eslint/type-utils" "5.14.0"
|
||||
"@typescript-eslint/utils" "5.14.0"
|
||||
debug "^4.3.2"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
ignore "^5.1.8"
|
||||
@@ -2847,69 +2858,69 @@
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/parser@^5.13.0":
|
||||
version "5.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.13.0.tgz#0394ed8f2f849273c0bf4b811994d177112ced5c"
|
||||
integrity sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg==
|
||||
"@typescript-eslint/parser@^5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.14.0.tgz#7c79f898aa3cff0ceee6f1d34eeed0f034fb9ef3"
|
||||
integrity sha512-aHJN8/FuIy1Zvqk4U/gcO/fxeMKyoSv/rS46UXMXOJKVsLQ+iYPuXNbpbH7cBLcpSbmyyFbwrniLx5+kutu1pw==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.13.0"
|
||||
"@typescript-eslint/types" "5.13.0"
|
||||
"@typescript-eslint/typescript-estree" "5.13.0"
|
||||
"@typescript-eslint/scope-manager" "5.14.0"
|
||||
"@typescript-eslint/types" "5.14.0"
|
||||
"@typescript-eslint/typescript-estree" "5.14.0"
|
||||
debug "^4.3.2"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.13.0":
|
||||
version "5.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz#cf6aff61ca497cb19f0397eea8444a58f46156b6"
|
||||
integrity sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA==
|
||||
"@typescript-eslint/scope-manager@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz#ea518962b42db8ed0a55152ea959c218cb53ca7b"
|
||||
integrity sha512-LazdcMlGnv+xUc5R4qIlqH0OWARyl2kaP8pVCS39qSL3Pd1F7mI10DbdXeARcE62sVQE4fHNvEqMWsypWO+yEw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.13.0"
|
||||
"@typescript-eslint/visitor-keys" "5.13.0"
|
||||
"@typescript-eslint/types" "5.14.0"
|
||||
"@typescript-eslint/visitor-keys" "5.14.0"
|
||||
|
||||
"@typescript-eslint/type-utils@5.13.0":
|
||||
version "5.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.13.0.tgz#b0efd45c85b7bab1125c97b752cab3a86c7b615d"
|
||||
integrity sha512-/nz7qFizaBM1SuqAKb7GLkcNn2buRdDgZraXlkhz+vUGiN1NZ9LzkA595tHHeduAiS2MsHqMNhE2zNzGdw43Yg==
|
||||
"@typescript-eslint/type-utils@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz#711f08105860b12988454e91df433567205a8f0b"
|
||||
integrity sha512-d4PTJxsqaUpv8iERTDSQBKUCV7Q5yyXjqXUl3XF7Sd9ogNLuKLkxz82qxokqQ4jXdTPZudWpmNtr/JjbbvUixw==
|
||||
dependencies:
|
||||
"@typescript-eslint/utils" "5.13.0"
|
||||
"@typescript-eslint/utils" "5.14.0"
|
||||
debug "^4.3.2"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/types@5.13.0":
|
||||
version "5.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.13.0.tgz#da1de4ae905b1b9ff682cab0bed6b2e3be9c04e5"
|
||||
integrity sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg==
|
||||
"@typescript-eslint/types@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.14.0.tgz#96317cf116cea4befabc0defef371a1013f8ab11"
|
||||
integrity sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.13.0":
|
||||
version "5.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz#b37c07b748ff030a3e93d87c842714e020b78141"
|
||||
integrity sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA==
|
||||
"@typescript-eslint/typescript-estree@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz#78b7f7385d5b6f2748aacea5c9b7f6ae62058314"
|
||||
integrity sha512-QGnxvROrCVtLQ1724GLTHBTR0lZVu13izOp9njRvMkCBgWX26PKvmMP8k82nmXBRD3DQcFFq2oj3cKDwr0FaUA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.13.0"
|
||||
"@typescript-eslint/visitor-keys" "5.13.0"
|
||||
"@typescript-eslint/types" "5.14.0"
|
||||
"@typescript-eslint/visitor-keys" "5.14.0"
|
||||
debug "^4.3.2"
|
||||
globby "^11.0.4"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/utils@5.13.0":
|
||||
version "5.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.13.0.tgz#2328feca700eb02837298339a2e49c46b41bd0af"
|
||||
integrity sha512-+9oHlPWYNl6AwwoEt5TQryEHwiKRVjz7Vk6kaBeD3/kwHE5YqTGHtm/JZY8Bo9ITOeKutFaXnBlMgSATMJALUQ==
|
||||
"@typescript-eslint/utils@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.14.0.tgz#6c8bc4f384298cbbb32b3629ba7415f9f80dc8c4"
|
||||
integrity sha512-EHwlII5mvUA0UsKYnVzySb/5EE/t03duUTweVy8Zqt3UQXBrpEVY144OTceFKaOe4xQXZJrkptCf7PjEBeGK4w==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.9"
|
||||
"@typescript-eslint/scope-manager" "5.13.0"
|
||||
"@typescript-eslint/types" "5.13.0"
|
||||
"@typescript-eslint/typescript-estree" "5.13.0"
|
||||
"@typescript-eslint/scope-manager" "5.14.0"
|
||||
"@typescript-eslint/types" "5.14.0"
|
||||
"@typescript-eslint/typescript-estree" "5.14.0"
|
||||
eslint-scope "^5.1.1"
|
||||
eslint-utils "^3.0.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.13.0":
|
||||
version "5.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz#f45ff55bcce16403b221ac9240fbeeae4764f0fd"
|
||||
integrity sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g==
|
||||
"@typescript-eslint/visitor-keys@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.14.0.tgz#1927005b3434ccd0d3ae1b2ecf60e65943c36986"
|
||||
integrity sha512-yL0XxfzR94UEkjBqyymMLgCBdojzEuy/eim7N9/RIcTNxpJudAcqsU8eRyfzBbcEzGoPWfdM3AGak3cN08WOIw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.13.0"
|
||||
"@typescript-eslint/types" "5.14.0"
|
||||
eslint-visitor-keys "^3.0.0"
|
||||
|
||||
"@webassemblyjs/ast@1.11.1":
|
||||
@@ -4115,10 +4126,10 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
css-loader@^6.7.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.0.tgz#c1200da1dfffe6643b18bda20fdd84cad3e36d39"
|
||||
integrity sha512-S7HCfCiDHLA+VXKqdZwyRZgoO0R9BnKDnVIoHMq5grl3N86zAu7MB+FBWHr5xOJC8SmvpTLha/2NpfFkFEN/ig==
|
||||
css-loader@^6.7.1:
|
||||
version "6.7.1"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e"
|
||||
integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==
|
||||
dependencies:
|
||||
icss-utils "^5.1.0"
|
||||
postcss "^8.4.7"
|
||||
@@ -8002,10 +8013,10 @@ qr.js@0.0.0:
|
||||
resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f"
|
||||
integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=
|
||||
|
||||
qrcode.react@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-1.0.1.tgz#2834bb50e5e275ffe5af6906eff15391fe9e38a5"
|
||||
integrity sha512-8d3Tackk8IRLXTo67Y+c1rpaiXjoz/Dd2HpcMdW//62/x8J1Nbho14Kh8x974t9prsLHN6XqVgcnRiBGFptQmg==
|
||||
qrcode.react@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-2.0.0.tgz#20b738eddcfc6958673bbbd1a0c5568ee5c399f5"
|
||||
integrity sha512-1CCzwC4KHYCzOLb7M+lKYqHzDMVsppKJBhrU1ZDCuJRHKiD99LeDHOkuvUKCRvnJgDzp9SnB6/WNA7qp6I7ozA==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
Reference in New Issue
Block a user