Merge branch 'release/3.8.14' into main

This commit is contained in:
Antonella Sgarlatta
2021-07-05 17:55:41 -03:00
48 changed files with 919 additions and 336 deletions

View File

@@ -16,6 +16,7 @@ SF_NEXT_VERSION_SERVER=http://localhost:3000
DEV_DEFAULT_SYNC_SERVER=https://sync.standardnotes.org
DEV_NEXT_VERSION_SYNC_SERVER=https://api.standardnotes.com
DEV_EXTENSIONS_MANAGER_LOCATION=public/extensions/extensions-manager/dist/index.html
ENABLE_UNFINISHED_FEATURES=false
# NewRelic (Optional)
NEW_RELIC_ENABLED=false

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.5 7.50008H12.5V18.3334H10.8333V13.3334H9.16667V18.3334H7.5V7.50008H2.5V5.83342H17.5V7.50008ZM10 1.66675C10.442 1.66675 10.866 1.84234 11.1785 2.1549C11.4911 2.46746 11.6667 2.89139 11.6667 3.33342C11.6667 3.77544 11.4911 4.19937 11.1785 4.51193C10.866 4.82449 10.442 5.00008 10 5.00008C9.075 5.00008 8.33333 4.25008 8.33333 3.33342C8.33333 2.40841 9.075 1.66675 10 1.66675Z" fill="#72767E"/>
</svg>

After

Width:  |  Height:  |  Size: 508 B

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.16675 15.0001H10.8334V13.3334H9.16675V15.0001ZM10.0001 1.66675C8.90573 1.66675 7.8221 1.8823 6.81105 2.30109C5.80001 2.71987 4.88135 3.3337 4.10753 4.10753C2.54472 5.67033 1.66675 7.78995 1.66675 10.0001C1.66675 12.2102 2.54472 14.3298 4.10753 15.8926C4.88135 16.6665 5.80001 17.2803 6.81105 17.6991C7.8221 18.1179 8.90573 18.3334 10.0001 18.3334C12.2102 18.3334 14.3298 17.4554 15.8926 15.8926C17.4554 14.3298 18.3334 12.2102 18.3334 10.0001C18.3334 8.90573 18.1179 7.8221 17.6991 6.81105C17.2803 5.80001 16.6665 4.88135 15.8926 4.10753C15.1188 3.3337 14.2002 2.71987 13.1891 2.30109C12.1781 1.8823 11.0944 1.66675 10.0001 1.66675ZM10.0001 16.6668C6.32508 16.6668 3.33342 13.6751 3.33342 10.0001C3.33342 6.32508 6.32508 3.33342 10.0001 3.33342C13.6751 3.33342 16.6668 6.32508 16.6668 10.0001C16.6668 13.6751 13.6751 16.6668 10.0001 16.6668ZM10.0001 5.00008C9.11603 5.00008 8.26818 5.35127 7.64306 5.97639C7.01794 6.60151 6.66675 7.44936 6.66675 8.33342H8.33342C8.33342 7.89139 8.50901 7.46747 8.82157 7.1549C9.13413 6.84234 9.55806 6.66675 10.0001 6.66675C10.4421 6.66675 10.866 6.84234 11.1786 7.1549C11.4912 7.46747 11.6667 7.89139 11.6667 8.33342C11.6667 10.0001 9.16675 9.79175 9.16675 12.5001H10.8334C10.8334 10.6251 13.3334 10.4167 13.3334 8.33342C13.3334 7.44936 12.9822 6.60151 12.3571 5.97639C11.732 5.35127 10.8841 5.00008 10.0001 5.00008Z" fill="#72767E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.33341 4.16675C2.89139 4.16675 2.46746 4.34234 2.1549 4.6549C1.84234 4.96746 1.66675 5.39139 1.66675 5.83341V14.1667C1.66675 14.6088 1.84234 15.0327 2.1549 15.3453C2.46746 15.6578 2.89139 15.8334 3.33341 15.8334H16.6667C17.1088 15.8334 17.5327 15.6578 17.8453 15.3453C18.1578 15.0327 18.3334 14.6088 18.3334 14.1667V5.83341C18.3334 5.39139 18.1578 4.96746 17.8453 4.6549C17.5327 4.34234 17.1088 4.16675 16.6667 4.16675H3.33341ZM3.33341 5.83341H16.6667V14.1667H3.33341V5.83341ZM4.16675 6.66675V8.33342H5.83341V6.66675H4.16675ZM6.66675 6.66675V8.33342H8.33341V6.66675H6.66675ZM9.16675 6.66675V8.33342H10.8334V6.66675H9.16675ZM11.6667 6.66675V8.33342H13.3334V6.66675H11.6667ZM14.1667 6.66675V8.33342H15.8334V6.66675H14.1667ZM4.16675 9.16675V10.8334H5.83341V9.16675H4.16675ZM6.66675 9.16675V10.8334H8.33341V9.16675H6.66675ZM9.16675 9.16675V10.8334H10.8334V9.16675H9.16675ZM11.6667 9.16675V10.8334H13.3334V9.16675H11.6667ZM14.1667 9.16675V10.8334H15.8334V9.16675H14.1667ZM6.66675 11.6667V13.3334H13.3334V11.6667H6.66675Z" fill="#72767E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.28571 3.6H15.7143C16.093 3.6 16.4 3.907 16.4 4.28571V15.7143C16.4 16.093 16.093 16.4 15.7143 16.4H4.28571C3.907 16.4 3.6 16.093 3.6 15.7143V4.28571C3.6 3.907 3.907 3.6 4.28571 3.6ZM2 4.28571C2 3.02335 3.02335 2 4.28571 2H15.7143C16.9767 2 18 3.02335 18 4.28571V15.7143C18 16.9767 16.9767 18 15.7143 18H4.28571C3.02335 18 2 16.9767 2 15.7143V4.28571ZM9.8045 6.31638C9.94745 6.27119 10.1703 6.24105 10.473 6.22599V6C10.0105 6.0226 9.35045 6.0339 8.49279 6.0339C7.59309 6.0339 6.92883 6.0226 6.5 6V6.22599C6.77748 6.24105 6.97928 6.27119 7.10541 6.31638C7.23994 6.36158 7.32823 6.44821 7.37027 6.57627C7.42072 6.70433 7.44595 6.91149 7.44595 7.19774V12.8023C7.44595 13.0885 7.42072 13.2957 7.37027 13.4237C7.32823 13.5518 7.23994 13.6384 7.10541 13.6836C6.97928 13.7288 6.77748 13.7589 6.5 13.774V14C7.40811 13.9774 8.77868 13.9661 10.6117 13.9661C11.9655 13.9661 12.9282 13.9774 13.5 14C13.4411 13.533 13.4117 12.9379 13.4117 12.2147C13.4117 11.8079 13.4243 11.4765 13.4495 11.2203H13.1595C13.0333 11.9812 12.7601 12.5913 12.3396 13.0508C11.9276 13.5104 11.4357 13.7401 10.864 13.7401H10.2459C10.0105 13.7401 9.83814 13.7213 9.72883 13.6836C9.62793 13.646 9.55646 13.5744 9.51441 13.4689C9.47237 13.3559 9.45135 13.1789 9.45135 12.9379V7.19774C9.45135 6.91149 9.47658 6.70433 9.52703 6.57627C9.57748 6.44821 9.66997 6.36158 9.8045 6.31638Z" fill="#72767E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 10H15.4444C15.0322 12.9891 12.8933 15.6582 10 16.4873V10H4.55556V5.85455L10 3.59273V10ZM10 2L3 4.90909V9.27273C3 13.3091 5.98667 17.0764 10 18C14.0133 17.0764 17 13.3091 17 9.27273V4.90909L10 2Z" fill="#72767E"/>
</svg>

After

Width:  |  Height:  |  Size: 329 B

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0001 12.9167C9.22656 12.9167 8.4847 12.6095 7.93772 12.0625C7.39073 11.5155 7.08344 10.7736 7.08344 10.0001C7.08344 9.22653 7.39073 8.48467 7.93772 7.93769C8.4847 7.39071 9.22656 7.08342 10.0001 7.08342C10.7737 7.08342 11.5155 7.39071 12.0625 7.93769C12.6095 8.48467 12.9168 9.22653 12.9168 10.0001C12.9168 10.7736 12.6095 11.5155 12.0625 12.0625C11.5155 12.6095 10.7737 12.9167 10.0001 12.9167ZM16.1918 10.8084C16.2251 10.5417 16.2501 10.2751 16.2501 10.0001C16.2501 9.72508 16.2251 9.45008 16.1918 9.16675L17.9501 7.80841C18.1084 7.68341 18.1501 7.45841 18.0501 7.27508L16.3834 4.39175C16.2834 4.20841 16.0584 4.13341 15.8751 4.20842L13.8001 5.04175C13.3668 4.71675 12.9168 4.43341 12.3918 4.22508L12.0834 2.01675C12.0501 1.81675 11.8751 1.66675 11.6668 1.66675H8.33344C8.12511 1.66675 7.95011 1.81675 7.91678 2.01675L7.60844 4.22508C7.08344 4.43341 6.63344 4.71675 6.20011 5.04175L4.12511 4.20842C3.94178 4.13341 3.71678 4.20841 3.61678 4.39175L1.95011 7.27508C1.84178 7.45841 1.89178 7.68341 2.05011 7.80841L3.80844 9.16675C3.77511 9.45008 3.75011 9.72508 3.75011 10.0001C3.75011 10.2751 3.77511 10.5417 3.80844 10.8084L2.05011 12.1917C1.89178 12.3167 1.84178 12.5417 1.95011 12.7251L3.61678 15.6084C3.71678 15.7917 3.94178 15.8584 4.12511 15.7917L6.20011 14.9501C6.63344 15.2834 7.08344 15.5667 7.60844 15.7751L7.91678 17.9834C7.95011 18.1834 8.12511 18.3334 8.33344 18.3334H11.6668C11.8751 18.3334 12.0501 18.1834 12.0834 17.9834L12.3918 15.7751C12.9168 15.5584 13.3668 15.2834 13.8001 14.9501L15.8751 15.7917C16.0584 15.8584 16.2834 15.7917 16.3834 15.6084L18.0501 12.7251C18.1501 12.5417 18.1084 12.3167 17.9501 12.1917L16.1918 10.8084Z" fill="#72767E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.1035 12.8875C10.0399 12.8491 9.96028 12.8491 9.89672 12.8875L7.27153 14.4724C7.12022 14.5637 6.93347 14.4283 6.9733 14.2561L7.66462 11.2674C7.68131 11.1952 7.65676 11.1197 7.60082 11.0712L5.283 9.06056C5.14938 8.94465 5.22096 8.72509 5.39722 8.7102L8.45479 8.45191C8.52877 8.44566 8.5932 8.39895 8.62214 8.33058L9.8159 5.51022C9.88478 5.3475 10.1154 5.3475 10.1843 5.51022L11.378 8.33058C11.407 8.39895 11.4714 8.44566 11.5454 8.45191L14.6029 8.7102C14.7792 8.72509 14.8508 8.94465 14.7172 9.06057L12.3993 11.0712C12.3434 11.1197 12.3189 11.1952 12.3355 11.2674L13.0269 14.2561C13.0667 14.4283 12.8799 14.5637 12.7286 14.4724L10.1035 12.8875ZM17.9751 8.01046C18.1089 7.89462 18.0374 7.67496 17.8611 7.66001L12.4619 7.20194C12.388 7.19567 12.3236 7.149 12.2947 7.08071L10.1842 2.10122C10.1153 1.93862 9.88486 1.93862 9.81594 2.10122L7.70548 7.08071C7.67653 7.149 7.61216 7.19567 7.53824 7.20194L2.13848 7.66006C1.96228 7.67501 1.89074 7.89448 2.02429 8.01039L6.11748 11.5628C6.17343 11.6114 6.19795 11.6869 6.18122 11.759L4.95694 17.0392C4.91701 17.2114 5.10377 17.347 5.25512 17.2556L9.89674 14.4541C9.96029 14.4158 10.0399 14.4158 10.1034 14.4541L14.7443 17.2552C14.8957 17.3466 15.0826 17.2108 15.0424 17.0385L13.8108 11.7593C13.794 11.687 13.8185 11.6113 13.8747 11.5627L17.9751 8.01046Z" fill="#72767E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 18C8.94943 18 7.90914 17.7931 6.93853 17.391C5.96793 16.989 5.08601 16.3997 4.34315 15.6569C2.84285 14.1566 2 12.1217 2 10C2 7.87827 2.84285 5.84344 4.34315 4.34315C5.84344 2.84285 7.87827 2 10 2C14.4 2 18 5.2 18 9.2C18 10.473 17.4943 11.6939 16.5941 12.5941C15.6939 13.4943 14.473 14 13.2 14H11.76C11.52 14 11.36 14.16 11.36 14.4C11.36 14.48 11.44 14.56 11.44 14.64C11.76 15.04 11.92 15.52 11.92 16C12 17.12 11.12 18 10 18ZM10 3.6C8.30261 3.6 6.67475 4.27428 5.47452 5.47452C4.27428 6.67475 3.6 8.30261 3.6 10C3.6 11.6974 4.27428 13.3253 5.47452 14.5255C6.67475 15.7257 8.30261 16.4 10 16.4C10.24 16.4 10.4 16.24 10.4 16C10.4 15.84 10.32 15.76 10.32 15.68C10 15.28 9.84 14.88 9.84 14.4C9.84 13.28 10.72 12.4 11.84 12.4H13.2C14.0487 12.4 14.8626 12.0629 15.4627 11.4627C16.0629 10.8626 16.4 10.0487 16.4 9.2C16.4 6.08 13.52 3.6 10 3.6ZM5.6 8.4C6.24 8.4 6.8 8.96 6.8 9.6C6.8 10.24 6.24 10.8 5.6 10.8C4.96 10.8 4.4 10.24 4.4 9.6C4.4 8.96 4.96 8.4 5.6 8.4ZM8 5.2C8.64 5.2 9.2 5.76 9.2 6.4C9.2 7.04 8.64 7.6 8 7.6C7.36 7.6 6.8 7.04 6.8 6.4C6.8 5.76 7.36 5.2 8 5.2ZM12 5.2C12.64 5.2 13.2 5.76 13.2 6.4C13.2 7.04 12.64 7.6 12 7.6C11.36 7.6 10.8 7.04 10.8 6.4C10.8 5.76 11.36 5.2 12 5.2ZM14.4 8.4C15.04 8.4 15.6 8.96 15.6 9.6C15.6 10.24 15.04 10.8 14.4 10.8C13.76 10.8 13.2 10.24 13.2 9.6C13.2 8.96 13.76 8.4 14.4 8.4Z" fill="#72767E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 3C10.9283 3 11.8185 3.36875 12.4749 4.02513C13.1313 4.6815 13.5 5.57174 13.5 6.5C13.5 7.42826 13.1313 8.3185 12.4749 8.97487C11.8185 9.63125 10.9283 10 10 10C9.07174 10 8.1815 9.63125 7.52513 8.97487C6.86875 8.3185 6.5 7.42826 6.5 6.5C6.5 5.57174 6.86875 4.6815 7.52513 4.02513C8.1815 3.36875 9.07174 3 10 3ZM10 4.75C9.53587 4.75 9.09075 4.93437 8.76256 5.26256C8.43437 5.59075 8.25 6.03587 8.25 6.5C8.25 6.96413 8.43437 7.40925 8.76256 7.73744C9.09075 8.06563 9.53587 8.25 10 8.25C10.4641 8.25 10.9092 8.06563 11.2374 7.73744C11.5656 7.40925 11.75 6.96413 11.75 6.5C11.75 6.03587 11.5656 5.59075 11.2374 5.26256C10.9092 4.93437 10.4641 4.75 10 4.75ZM10 10.875C12.3363 10.875 17 12.0387 17 14.375V17H3V14.375C3 12.0387 7.66375 10.875 10 10.875ZM10 12.5375C7.40125 12.5375 4.6625 13.815 4.6625 14.375V15.3375H15.3375V14.375C15.3375 13.815 12.5988 12.5375 10 12.5375Z" fill="#72767E"/>
</svg>

After

Width:  |  Height:  |  Size: 1000 B

View File

@@ -65,6 +65,7 @@ import { NotesContextMenuDirective } from './components/NotesContextMenu';
import { NotesOptionsPanelDirective } from './components/NotesOptionsPanel';
import { IconDirective } from './components/Icon';
import { NoteTagsContainerDirective } from './components/NoteTagsContainer';
import { PreferencesDirective } from './components/preferences';
function reloadHiddenFirefoxTab(): boolean {
/**
@@ -90,7 +91,7 @@ function reloadHiddenFirefoxTab(): boolean {
const startApplication: StartApplication = async function startApplication(
defaultSyncServerHost: string,
bridge: Bridge,
nextVersionSyncServerHost: string,
nextVersionSyncServerHost: string
) {
if (reloadHiddenFirefoxTab()) {
return;
@@ -161,7 +162,8 @@ const startApplication: StartApplication = async function startApplication(
.directive('notesContextMenu', NotesContextMenuDirective)
.directive('notesOptionsPanel', NotesOptionsPanelDirective)
.directive('icon', IconDirective)
.directive('noteTagsContainer', NoteTagsContainerDirective);
.directive('noteTagsContainer', NoteTagsContainerDirective)
.directive('preferences', PreferencesDirective);
// Filters
angular.module('app').filter('trusted', ['$sce', trusted]);
@@ -174,10 +176,12 @@ const startApplication: StartApplication = async function startApplication(
Object.defineProperties(window, {
application: {
get: () =>
(angular
.element(document)
.injector()
.get('mainApplicationGroup') as any).primaryApplication,
(
angular
.element(document)
.injector()
.get('mainApplicationGroup') as any
).primaryApplication,
},
});
}

View File

@@ -62,6 +62,7 @@ export const AutocompleteTagHint = observer(
onFocus={onFocus}
onBlur={onBlur}
onKeyDown={onKeyDown}
tabIndex={-1}
>
<span>Create new tag:</span>
<span className="bg-contrast rounded text-xs color-text py-1 pl-1 pr-2 flex items-center ml-2">

View File

@@ -25,10 +25,10 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
const [dropdownMaxHeight, setDropdownMaxHeight] =
useState<number | 'auto'>('auto');
const dropdownRef = useRef<HTMLDivElement>();
const containerRef = useRef<HTMLDivElement>();
const inputRef = useRef<HTMLInputElement>();
const [closeOnBlur] = useCloseOnBlur(dropdownRef, (visible: boolean) => {
const [closeOnBlur] = useCloseOnBlur(containerRef, (visible: boolean) => {
setDropdownVisible(visible);
appState.noteTags.clearAutocompleteSearch();
});
@@ -69,7 +69,9 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
case 'ArrowDown':
event.preventDefault();
if (autocompleteTagResults.length > 0) {
appState.noteTags.setFocusedTagResultUuid(autocompleteTagResults[0].uuid);
appState.noteTags.setFocusedTagResultUuid(
autocompleteTagResults[0].uuid
);
} else if (autocompleteTagHintVisible) {
appState.noteTags.setAutocompleteTagHintFocused(true);
}
@@ -92,54 +94,64 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
useEffect(() => {
if (autocompleteInputFocused) {
inputRef.current.focus();
appState.noteTags.setAutocompleteInputFocused(false);
}
}, [appState.noteTags, autocompleteInputFocused]);
return (
<form
onSubmit={onFormSubmit}
className={`${tags.length > 0 ? 'mt-2' : ''}`}
>
<Disclosure open={dropdownVisible} onChange={showDropdown}>
<input
ref={inputRef}
className={`${tags.length > 0 ? 'w-80' : 'w-70 mr-10'} bg-transparent text-xs
<div ref={containerRef}>
<form
onSubmit={onFormSubmit}
className={`${tags.length > 0 ? 'mt-2' : ''}`}
>
<Disclosure open={dropdownVisible} onChange={showDropdown}>
<input
ref={inputRef}
className={`${
tags.length > 0 ? 'w-80' : 'w-70 mr-10'
} bg-transparent text-xs
color-text no-border h-7 focus:outline-none focus:shadow-none focus:border-bottom`}
value={autocompleteSearchQuery}
onChange={onSearchQueryChange}
type="text"
placeholder="Add tag"
onBlur={onBlur}
onFocus={onFocus}
onKeyDown={onKeyDown}
/>
{dropdownVisible && (autocompleteTagResults.length > 0 || autocompleteTagHintVisible) && (
<DisclosurePanel
ref={dropdownRef}
className={`${tags.length > 0 ? 'w-80' : 'w-70 mr-10'} sn-dropdown flex flex-col py-2 absolute`}
style={{ maxHeight: dropdownMaxHeight, maxWidth: tagsContainerMaxWidth }}
onBlur={closeOnBlur}
>
<div className="overflow-y-auto">
{autocompleteTagResults.map((tagResult: SNTag) => (
<AutocompleteTagResult
key={tagResult.uuid}
appState={appState}
tagResult={tagResult}
closeOnBlur={closeOnBlur}
/>
))}
</div>
{autocompleteTagHintVisible && (
<AutocompleteTagHint
appState={appState}
closeOnBlur={closeOnBlur}
/>
value={autocompleteSearchQuery}
onChange={onSearchQueryChange}
type="text"
placeholder="Add tag"
onBlur={onBlur}
onFocus={onFocus}
onKeyDown={onKeyDown}
tabIndex={tags.length === 0 ? 0 : -1}
/>
{dropdownVisible &&
(autocompleteTagResults.length > 0 ||
autocompleteTagHintVisible) && (
<DisclosurePanel
className={`${
tags.length > 0 ? 'w-80' : 'w-70 mr-10'
} sn-dropdown flex flex-col py-2 absolute`}
style={{
maxHeight: dropdownMaxHeight,
maxWidth: tagsContainerMaxWidth,
}}
onBlur={closeOnBlur}
>
<div className="overflow-y-auto">
{autocompleteTagResults.map((tagResult: SNTag) => (
<AutocompleteTagResult
key={tagResult.uuid}
appState={appState}
tagResult={tagResult}
closeOnBlur={closeOnBlur}
/>
))}
</div>
{autocompleteTagHintVisible && (
<AutocompleteTagHint
appState={appState}
closeOnBlur={closeOnBlur}
/>
)}
</DisclosurePanel>
)}
</DisclosurePanel>
)}
</Disclosure>
</form>
</Disclosure>
</form>
</div>
);
});

View File

@@ -82,6 +82,7 @@ export const AutocompleteTagResult = observer(
onFocus={onFocus}
onBlur={onBlur}
onKeyDown={onKeyDown}
tabIndex={-1}
>
<Icon type="hashtag" className="color-neutral mr-2 min-h-5 min-w-5" />
<span className="whitespace-nowrap overflow-hidden overflow-ellipsis">

View File

@@ -13,40 +13,59 @@ import PasswordIcon from '../../icons/ic-textbox-password.svg';
import TrashSweepIcon from '../../icons/ic-trash-sweep.svg';
import MoreIcon from '../../icons/ic-more.svg';
import TuneIcon from '../../icons/ic-tune.svg';
import AccessibilityIcon from '../../icons/ic-accessibility.svg';
import HelpIcon from '../../icons/ic-help.svg';
import KeyboardIcon from '../../icons/ic-keyboard.svg';
import ListedIcon from '../../icons/ic-listed.svg';
import SecurityIcon from '../../icons/ic-security.svg';
import SettingsFilledIcon from '../../icons/ic-settings-filled.svg';
import StarIcon from '../../icons/ic-star.svg';
import ThemesIcon from '../../icons/ic-themes.svg';
import UserIcon from '../../icons/ic-user.svg';
import { toDirective } from './utils';
const ICONS = {
'pencil-off': PencilOffIcon,
'rich-text': RichTextIcon,
'trash': TrashIcon,
'pin': PinIcon,
'unpin': UnpinIcon,
'archive': ArchiveIcon,
'unarchive': UnarchiveIcon,
'hashtag': HashtagIcon,
trash: TrashIcon,
pin: PinIcon,
unpin: UnpinIcon,
archive: ArchiveIcon,
unarchive: UnarchiveIcon,
hashtag: HashtagIcon,
'chevron-right': ChevronRightIcon,
'restore': RestoreIcon,
'close': CloseIcon,
'password': PasswordIcon,
restore: RestoreIcon,
close: CloseIcon,
password: PasswordIcon,
'trash-sweep': TrashSweepIcon,
'more': MoreIcon,
'tune': TuneIcon,
more: MoreIcon,
tune: TuneIcon,
accessibility: AccessibilityIcon,
help: HelpIcon,
keyboard: KeyboardIcon,
listed: ListedIcon,
security: SecurityIcon,
'settings-filled': SettingsFilledIcon,
star: StarIcon,
themes: ThemesIcon,
user: UserIcon,
};
export type IconType = keyof typeof ICONS;
type Props = {
type: keyof (typeof ICONS);
className: string;
}
type: IconType;
className?: string;
};
export const Icon: React.FC<Props> = ({ type, className }) => {
const IconComponent = ICONS[type];
return <IconComponent className={`sn-icon ${className}`} />;
};
export const IconDirective = toDirective<Props>(
Icon,
{
type: '@',
className: '@',
}
);
export const IconDirective = toDirective<Props>(Icon, {
type: '@',
className: '@',
});

View File

@@ -0,0 +1,53 @@
import { FunctionComponent } from 'preact';
import { Icon, IconType } from './Icon';
const ICON_BUTTON_TYPES: {
[type: string]: { className: string };
} = {
normal: {
className: '',
},
primary: {
className: 'info',
},
};
export type IconButtonType = keyof typeof ICON_BUTTON_TYPES;
interface IconButtonProps {
/**
* onClick - preventDefault is handled within the component
*/
onClick: () => void;
type: IconButtonType;
className?: string;
iconType: IconType;
}
/**
* CircleButton component with an icon for SPA
* preventDefault is already handled within the component
*/
export const IconButton: FunctionComponent<IconButtonProps> = ({
onClick,
type,
className,
iconType,
}) => {
const click = (e: MouseEvent) => {
e.preventDefault();
onClick();
};
const typeProps = ICON_BUTTON_TYPES[type];
return (
<button
className={`sn-icon-button ${typeProps.className} ${className ?? ''}`}
onClick={click}
>
<Icon type={iconType} />
</button>
);
};

View File

@@ -10,7 +10,7 @@ type Props = {
};
export const NoteTag = observer(({ appState, tag }: Props) => {
const { focusedTagUuid, tags } = appState.noteTags;
const { autocompleteInputFocused, focusedTagUuid, tags } = appState.noteTags;
const [showDeleteButton, setShowDeleteButton] = useState(false);
const [tagClicked, setTagClicked] = useState(false);
@@ -51,6 +51,16 @@ export const NoteTag = observer(({ appState, tag }: Props) => {
}
};
const getTabIndex = () => {
if (focusedTagUuid) {
return focusedTagUuid === tag.uuid ? 0 : -1;
}
if (autocompleteInputFocused) {
return -1;
}
return tags[0].uuid === tag.uuid ? 0 : -1;
};
const onKeyDown = (event: KeyboardEvent) => {
const tagIndex = appState.noteTags.getTagIndex(tag, tags);
switch (event.key) {
@@ -75,7 +85,6 @@ export const NoteTag = observer(({ appState, tag }: Props) => {
useEffect(() => {
if (focusedTagUuid === tag.uuid) {
tagRef.current.focus();
appState.noteTags.setFocusedTagUuid(undefined);
}
}, [appState.noteTags, focusedTagUuid, tag]);
@@ -87,6 +96,7 @@ export const NoteTag = observer(({ appState, tag }: Props) => {
onKeyDown={onKeyDown}
onFocus={onFocus}
onBlur={onBlur}
tabIndex={getTabIndex()}
>
<Icon type="hashtag" className="sn-icon--small color-info mr-1" />
<span className="whitespace-nowrap overflow-hidden overflow-ellipsis max-w-290px">
@@ -97,9 +107,9 @@ export const NoteTag = observer(({ appState, tag }: Props) => {
ref={deleteTagRef}
type="button"
className="ml-2 -mr-1 border-0 p-0 bg-transparent cursor-pointer flex"
onFocus={onFocus}
onBlur={onBlur}
onClick={onDeleteTagClick}
tabIndex={-1}
>
<Icon
type="close"

View File

@@ -2,13 +2,19 @@ import { AppState } from '@/ui_models/app_state';
import { toDirective, useCloseOnBlur, useCloseOnClickOutside } from './utils';
import { observer } from 'mobx-react-lite';
import { NotesOptions } from './NotesOptions';
import { useRef } from 'preact/hooks';
import { useCallback, useEffect, useRef } from 'preact/hooks';
type Props = {
appState: AppState;
};
const NotesContextMenu = observer(({ appState }: Props) => {
const {
contextMenuOpen,
contextMenuPosition,
contextMenuMaxHeight,
} = appState.notes;
const contextMenuRef = useRef<HTMLDivElement>();
const [closeOnBlur] = useCloseOnBlur(
contextMenuRef,
@@ -20,13 +26,24 @@ const NotesContextMenu = observer(({ appState }: Props) => {
(open: boolean) => appState.notes.setContextMenuOpen(open)
);
return appState.notes.contextMenuOpen ? (
const reloadContextMenuLayout = useCallback(() => {
appState.notes.reloadContextMenuLayout();
}, [appState.notes]);
useEffect(() => {
window.addEventListener('resize', reloadContextMenuLayout);
return () => {
window.removeEventListener('resize', reloadContextMenuLayout);
};
}, [reloadContextMenuLayout]);
return contextMenuOpen ? (
<div
ref={contextMenuRef}
className="sn-dropdown min-w-80 max-h-120 max-w-xs flex flex-col py-2 overflow-y-auto fixed"
style={{
...appState.notes.contextMenuPosition,
maxHeight: appState.notes.contextMenuMaxHeight,
...contextMenuPosition,
maxHeight: contextMenuMaxHeight,
}}
>
<NotesOptions appState={appState} closeOnBlur={closeOnBlur} />

View File

@@ -0,0 +1,23 @@
import { Icon, IconType } from '@/components/Icon';
import { FunctionComponent } from 'preact';
interface PreferencesMenuItemProps {
iconType: IconType;
label: string;
selected: boolean;
onClick: () => void;
}
export const PreferencesMenuItem: FunctionComponent<PreferencesMenuItemProps> =
({ iconType, label, selected, onClick }) => (
<div
className={`preferences-menu-item ${selected ? 'selected' : ''}`}
onClick={(e) => {
e.preventDefault();
onClick();
}}
>
<Icon className="icon" type={iconType} />
{label}
</div>
);

View File

@@ -11,6 +11,7 @@ import {
} from '@reach/disclosure';
import { Switch } from './Switch';
import { observer } from 'mobx-react-lite';
import { useEffect } from 'react';
type Props = {
appState: AppState;
@@ -45,16 +46,27 @@ const SearchOptions = observer(({ appState }: Props) => {
}
}
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={() => {
const rect = buttonRef.current.getBoundingClientRect();
setMaxWidth(rect.right - 16);
setPosition({
top: rect.bottom,
right: document.body.clientWidth - rect.right,
});
updateWidthAndPosition();
setOpen(!open);
}}
>

View File

@@ -76,7 +76,6 @@ function useSessions(
setSessions(sessionsDuringRevoke);
const response = await responsePromise;
console.log(response);
if (isNullOrUndefined(response)) {
setSessions(sessionsBeforeRevoke);
} else if ('error' in response) {

View File

@@ -0,0 +1,13 @@
import { FunctionComponent } from 'preact';
export const TitleBar: FunctionComponent<{ className?: string }> = ({
children,
className,
}) => <div className={`sn-titlebar ${className ?? ''}`}>{children}</div>;
export const Title: FunctionComponent<{ className?: string }> = ({
children,
className,
}) => {
return <div className={`sn-title ${className ?? ''}`}>{children}</div>;
};

View File

@@ -0,0 +1,22 @@
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { toDirective } from '../utils';
import { PreferencesView } from './view';
interface WrapperProps {
appState: { preferences: { isOpen: boolean; closePreferences: () => void } };
}
const PreferencesViewWrapper: FunctionComponent<WrapperProps> = observer(
({ appState }) => {
if (!appState.preferences.isOpen) return null;
return (
<PreferencesView close={() => appState.preferences.closePreferences()} />
);
}
);
export const PreferencesDirective = toDirective<WrapperProps>(
PreferencesViewWrapper
);

View File

@@ -0,0 +1,50 @@
import { IconType } from '@/components/Icon';
import { action, computed, makeObservable, observable } from 'mobx';
interface PreferenceItem {
icon: IconType;
label: string;
}
interface PreferenceListItem extends PreferenceItem {
id: number;
}
const predefinedItems: PreferenceItem[] = [
{ label: 'General', icon: 'settings-filled' },
{ label: 'Account', icon: 'user' },
{ label: 'Appearance', icon: 'themes' },
{ label: 'Security', icon: 'security' },
{ label: 'Listed', icon: 'listed' },
{ label: 'Shortcuts', icon: 'keyboard' },
{ label: 'Accessibility', icon: 'accessibility' },
{ label: 'Get a free month', icon: 'star' },
{ label: 'Help & feedback', icon: 'help' },
];
export class MockState {
private readonly _items: PreferenceListItem[];
private _selectedId = 0;
constructor(items: PreferenceItem[] = predefinedItems) {
makeObservable<MockState, '_selectedId'>(this, {
_selectedId: observable,
items: computed,
select: action,
});
this._items = items.map((p, idx) => ({ ...p, id: idx }));
this._selectedId = this._items[0].id;
}
select(id: number) {
this._selectedId = id;
}
get items(): (PreferenceListItem & { selected: boolean })[] {
return this._items.map((p) => ({
...p,
selected: p.id === this._selectedId,
}));
}
}

View File

@@ -0,0 +1,33 @@
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { PreferencesMenuItem } from '../PreferencesMenuItem';
import { MockState } from './mock-state';
interface PreferencesMenuProps {
store: MockState;
}
const PreferencesMenu: FunctionComponent<PreferencesMenuProps> = observer(
({ store }) => (
<div className="h-full w-auto flex flex-col px-3 py-6">
{store.items.map((pref) => (
<PreferencesMenuItem
key={pref.id}
iconType={pref.icon}
label={pref.label}
selected={pref.selected}
onClick={() => store.select(pref.id)}
/>
))}
</div>
)
);
export const PreferencesPane: FunctionComponent = () => {
const store = new MockState();
return (
<div className="h-full w-full flex flex-row">
<PreferencesMenu store={store}></PreferencesMenu>
</div>
);
};

View File

@@ -0,0 +1,28 @@
import { IconButton } from '@/components/IconButton';
import { TitleBar, Title } from '@/components/TitleBar';
import { FunctionComponent } from 'preact';
import { PreferencesPane } from './pane';
interface PreferencesViewProps {
close: () => void;
}
export const PreferencesView: FunctionComponent<PreferencesViewProps> = ({
close,
}) => (
<div className="sn-full-screen flex flex-col bg-contrast z-index-preferences">
<TitleBar className="items-center justify-between">
{/* div is added so flex justify-between can center the title */}
<div className="h-8 w-8" />
<Title className="text-lg">Your preferences for Standard Notes</Title>
<IconButton
onClick={() => {
close();
}}
type="normal"
iconType="close"
/>
</TitleBar>
<PreferencesPane />
</div>
);

View File

@@ -18,6 +18,7 @@ import {
STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL,
STRING_UNSUPPORTED_BACKUP_FILE_VERSION,
StringUtils,
Strings,
} from '@/strings';
import { PasswordWizardType } from '@/types';
import {
@@ -509,8 +510,17 @@ class AccountMenuCtrl extends PureViewCtrl<unknown, AccountMenuState> {
}
async submitPasscodeForm() {
const passcode = this.getState().formData.passcode!;
if (passcode !== this.getState().formData.confirmPasscode!) {
const passcode = this.getState().formData.passcode;
if (!passcode || passcode.length === 0) {
await alertDialog({
text: Strings.enterPasscode,
});
this.passcodeInput[0].focus();
return;
}
if (passcode !== this.getState().formData.confirmPasscode) {
await alertDialog({
text: STRING_NON_MATCHING_PASSCODES,
});

View File

@@ -33,6 +33,9 @@ class ComponentViewCtrl implements ComponentViewScope {
private unregisterComponentHandler!: () => void
private unregisterDesktopObserver!: () => void
private issueLoading = false
private isDeprecated = false
private deprecationMessage = ''
private deprecationMessageDismissed = false
public reloading = false
private expired = false
private loading = false
@@ -175,6 +178,12 @@ class ComponentViewCtrl implements ComponentViewScope {
});
}
private dismissDeprecationMessage() {
this.$timeout(() => {
this.deprecationMessageDismissed = true;
});
}
private onVisibilityChange() {
if (document.visibilityState === 'hidden') {
return;
@@ -215,6 +224,8 @@ class ComponentViewCtrl implements ComponentViewScope {
if (this.expired && doManualReload) {
this.$rootScope.$broadcast(RootScopeMessages.ReloadExtendedData);
}
this.isDeprecated = component.isDeprecated;
this.deprecationMessage = component.package_info.deprecation_message;
}
private async handleIframeLoadTimeout() {

View File

@@ -28,6 +28,7 @@ type KeyboardObserver = {
elements?: HTMLElement[];
notElement?: HTMLElement;
notElementIds?: string[];
notTags?: string[];
};
export class IOService {
@@ -175,6 +176,10 @@ export class IOService {
continue;
}
if (observer.notTags && observer.notTags.includes(target.tagName)) {
continue;
}
if (
this.eventMatchesKeyAndModifiers(
event,

View File

@@ -5,7 +5,7 @@ import { getPlatform, isDesktopApplication } from './utils';
export const STRING_SESSION_EXPIRED =
'Your session has expired. New changes will not be pulled in. Please sign in to refresh your session.';
export const STRING_DEFAULT_FILE_ERROR =
'Please use FileSafe or the Bold Editor to attach images and files. Learn more at standardnotes.org/filesafe.';
'Please use FileSafe or the Bold Editor to attach images and files. Learn more at standardnotes.com/filesafe.';
export const STRING_GENERIC_SYNC_ERROR =
'There was an error syncing. Please try again. If all else fails, try signing out and signing back in.';
export function StringSyncException(data: any) {
@@ -105,7 +105,7 @@ export const STRING_UPGRADE_ACCOUNT_CONFIRM_TEXT =
'Encryption version 004 is available. ' +
'This version strengthens the encryption algorithms your account and ' +
'local storage use. To learn more about this upgrade, visit our ' +
'<a href="https://standardnotes.org/help/security" target="_blank">Security Upgrade page.</a>';
'<a href="https://standardnotes.com/help/security" target="_blank">Security Upgrade page.</a>';
export const STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON = 'Upgrade';
export const Strings = {
@@ -113,6 +113,7 @@ export const Strings = {
openAccountMenu: 'Open Account Menu',
trashNotesTitle: 'Move to Trash',
trashNotesText: 'Are you sure you want to move these notes to the trash?',
enterPasscode: 'Please enter a passcode.',
};
export const StringUtils = {

View File

@@ -1,4 +1,4 @@
import { isDesktopApplication, isDev } from '@/utils';
import { isDesktopApplication } from '@/utils';
import pull from 'lodash/pull';
import {
ApplicationEvent,
@@ -22,6 +22,7 @@ import { SyncState } from './sync_state';
import { SearchOptionsState } from './search_options_state';
import { NotesState } from './notes_state';
import { TagsState } from './tags_state';
import { PreferencesState } from './preferences_state';
export enum AppStateEvent {
TagChanged,
@@ -47,8 +48,8 @@ export enum EventSource {
type ObserverCallback = (event: AppStateEvent, data?: any) => Promise<void>;
export class AppState {
readonly enableUnfinishedFeatures =
isDev || location.host.includes('app-dev.standardnotes.org');
readonly enableUnfinishedFeatures: boolean = (window as any)
?._enable_unfinished_features;
$rootScope: ng.IRootScopeService;
$timeout: ng.ITimeoutService;
@@ -63,6 +64,7 @@ export class AppState {
showBetaWarning: boolean;
readonly accountMenu = new AccountMenuState();
readonly actionsMenu = new ActionsMenuState();
readonly preferences = new PreferencesState();
readonly noAccountWarning: NoAccountWarningState;
readonly noteTags: NoteTagsState;
readonly sync = new SyncState();
@@ -89,17 +91,14 @@ export class AppState {
async () => {
await this.notifyEvent(AppStateEvent.ActiveEditorChanged);
},
this.appEventObserverRemovers,
this.appEventObserverRemovers
);
this.noteTags = new NoteTagsState(
application,
this,
this.appEventObserverRemovers
);
this.tags = new TagsState(
application,
this.appEventObserverRemovers,
),
this.tags = new TagsState(application, this.appEventObserverRemovers);
this.noAccountWarning = new NoAccountWarningState(
application,
this.appEventObserverRemovers
@@ -128,6 +127,7 @@ export class AppState {
makeObservable(this, {
showBetaWarning: observable,
isSessionsModalVisible: observable,
preferences: observable,
enableBetaWarning: action,
disableBetaWarning: action,

View File

@@ -30,9 +30,6 @@ export class NoteTagsState {
autocompleteTagHintVisible: computed,
clearAutocompleteSearch: action,
focusNextTag: action,
focusPreviousTag: action,
setAutocompleteInputFocused: action,
setAutocompleteSearchQuery: action,
setAutocompleteTagHintFocused: action,
@@ -41,7 +38,6 @@ export class NoteTagsState {
setFocusedTagUuid: action,
setTags: action,
setTagsContainerMaxWidth: action,
reloadTags: action,
});
appEventListeners.push(

View File

@@ -28,6 +28,7 @@ export class NotesState {
top: 0,
left: 0,
};
contextMenuClickLocation: { x: number, y: number } = { x: 0, y: 0 };
contextMenuMaxHeight: number | 'auto' = 'auto';
showProtectedWarning = false;
@@ -47,6 +48,7 @@ export class NotesState {
trashedNotesCount: computed,
setContextMenuOpen: action,
setContextMenuClickLocation: action,
setContextMenuPosition: action,
setContextMenuMaxHeight: action,
setShowProtectedWarning: action,
@@ -183,6 +185,10 @@ export class NotesState {
this.contextMenuOpen = open;
}
setContextMenuClickLocation(location: { x: number, y: number }): void {
this.contextMenuClickLocation = location;
}
setContextMenuPosition(position: {
top?: number;
left: number;
@@ -195,6 +201,60 @@ export class NotesState {
this.contextMenuMaxHeight = maxHeight;
}
reloadContextMenuLayout(): void {
const { clientHeight } = document.documentElement;
const defaultFontSize = window.getComputedStyle(
document.documentElement
).fontSize;
const maxContextMenuHeight = parseFloat(defaultFontSize) * 30;
const footerHeight = 32;
// Open up-bottom is default behavior
let openUpBottom = true;
const bottomSpace = clientHeight - footerHeight - this.contextMenuClickLocation.y;
const upSpace = this.contextMenuClickLocation.y;
// If not enough space to open up-bottom
if (maxContextMenuHeight > bottomSpace) {
// If there's enough space, open bottom-up
if (upSpace > maxContextMenuHeight) {
openUpBottom = false;
this.setContextMenuMaxHeight(
'auto'
);
// Else, reduce max height (menu will be scrollable) and open in whichever direction there's more space
} else {
if (upSpace > bottomSpace) {
this.setContextMenuMaxHeight(
upSpace - 2
);
openUpBottom = false;
} else {
this.setContextMenuMaxHeight(
bottomSpace - 2
);
}
}
} else {
this.setContextMenuMaxHeight(
'auto'
);
}
if (openUpBottom) {
this.setContextMenuPosition({
top: this.contextMenuClickLocation.y,
left: this.contextMenuClickLocation.x,
});
} else {
this.setContextMenuPosition({
bottom: clientHeight - this.contextMenuClickLocation.y,
left: this.contextMenuClickLocation.x,
});
}
}
async changeSelectedNotes(
mutate: (mutator: NoteMutator) => void
): Promise<void> {

View File

@@ -0,0 +1,26 @@
import { action, computed, makeObservable, observable } from 'mobx';
export class PreferencesState {
private _open = false;
constructor() {
makeObservable<PreferencesState, '_open'>(this, {
_open: observable,
openPreferences: action,
closePreferences: action,
isOpen: computed,
});
}
openPreferences = (): void => {
this._open = true;
};
closePreferences = (): void => {
this._open = false;
};
get isOpen() {
return this._open;
}
}

View File

@@ -26,6 +26,9 @@
application='self.application'
app-state='self.appState'
)
preferences(
app-state='self.appState'
)
challenge-modal(
ng-repeat="challenge in self.challenges track by challenge.id"
class="sk-modal"
@@ -36,3 +39,4 @@
notes-context-menu(
app-state='self.appState'
)

View File

@@ -863,7 +863,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
.io
.addKeyObserver({
key: KeyboardKey.Backspace,
notElementIds: [ElementIds.NoteTextEditor, ElementIds.NoteTitleEditor],
notTags: ['INPUT', 'TEXTAREA'],
modifiers: [KeyboardModifier.Meta],
onKeyDown: () => {
this.deleteNote(false);

View File

@@ -18,6 +18,11 @@
ng-if='ctrl.showAccountMenu',
application='ctrl.application'
)
.sk-app-bar-item(
ng-click='ctrl.clickPreferences()'
ng-if='ctrl.appState.enableUnfinishedFeatures'
)
.sk-label.title Preferences
.sk-app-bar-item
a.no-decoration.sk-label.title(
href='https://standardnotes.com/help',

View File

@@ -33,44 +33,47 @@ const ACCOUNT_SWITCHER_ENABLED = false;
const ACCOUNT_SWITCHER_FEATURE_KEY = 'account_switcher';
type DockShortcut = {
name: string,
component: SNComponent,
name: string;
component: SNComponent;
icon: {
type: string
background_color: string
border_color: string
}
}
type: string;
background_color: string;
border_color: string;
};
};
class FooterViewCtrl extends PureViewCtrl<unknown, {
outOfSync: boolean;
hasPasscode: boolean;
dataUpgradeAvailable: boolean;
dockShortcuts: DockShortcut[];
hasAccountSwitcher: boolean;
showBetaWarning: boolean;
showDataUpgrade: boolean;
}> {
private $rootScope: ng.IRootScopeService
private rooms: SNComponent[] = []
private themesWithIcons: SNTheme[] = []
private showSyncResolution = false
private unregisterComponent: any
private rootScopeListener1: any
private rootScopeListener2: any
public arbitraryStatusMessage?: string
public user?: any
private offline = true
public showAccountMenu = false
private didCheckForOffline = false
private queueExtReload = false
private reloadInProgress = false
public hasError = false
public isRefreshing = false
public lastSyncDate?: string
public newUpdateAvailable = false
public dockShortcuts: DockShortcut[] = []
public roomShowState: Partial<Record<string, boolean>> = {}
class FooterViewCtrl extends PureViewCtrl<
unknown,
{
outOfSync: boolean;
hasPasscode: boolean;
dataUpgradeAvailable: boolean;
dockShortcuts: DockShortcut[];
hasAccountSwitcher: boolean;
showBetaWarning: boolean;
showDataUpgrade: boolean;
}
> {
private $rootScope: ng.IRootScopeService;
private rooms: SNComponent[] = [];
private themesWithIcons: SNTheme[] = [];
private showSyncResolution = false;
private unregisterComponent: any;
private rootScopeListener1: any;
private rootScopeListener2: any;
public arbitraryStatusMessage?: string;
public user?: any;
private offline = true;
public showAccountMenu = false;
private didCheckForOffline = false;
private queueExtReload = false;
private reloadInProgress = false;
public hasError = false;
public isRefreshing = false;
public lastSyncDate?: string;
public newUpdateAvailable = false;
public dockShortcuts: DockShortcut[] = [];
public roomShowState: Partial<Record<string, boolean>> = {};
private observerRemovers: Array<() => void> = [];
private completedInitialSync = false;
private showingDownloadStatus = false;
@@ -117,7 +120,7 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
this.showAccountMenu = this.appState.accountMenu.show;
this.setState({
showBetaWarning: showBetaWarning,
showDataUpgrade: !showBetaWarning
showDataUpgrade: !showBetaWarning,
});
});
}
@@ -128,7 +131,9 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
/** Enable permanently for this user so they don't lose the feature after its disabled */
localStorage.setItem(ACCOUNT_SWITCHER_FEATURE_KEY, JSON.stringify(true));
}
const hasAccountSwitcher = stringValue ? JSON.parse(stringValue) : ACCOUNT_SWITCHER_ENABLED;
const hasAccountSwitcher = stringValue
? JSON.parse(stringValue)
: ACCOUNT_SWITCHER_ENABLED;
this.setState({ hasAccountSwitcher });
}
@@ -148,7 +153,7 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
reloadUpgradeStatus() {
this.application.checkForSecurityUpdate().then((available) => {
this.setState({
dataUpgradeAvailable: available
dataUpgradeAvailable: available,
});
});
}
@@ -176,19 +181,25 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
async reloadPasscodeStatus() {
const hasPasscode = this.application.hasPasscode();
this.setState({
hasPasscode: hasPasscode
hasPasscode: hasPasscode,
});
}
addRootScopeListeners() {
this.rootScopeListener1 = this.$rootScope.$on(RootScopeMessages.ReloadExtendedData, () => {
this.reloadExtendedData();
});
this.rootScopeListener2 = this.$rootScope.$on(RootScopeMessages.NewUpdateAvailable, () => {
this.$timeout(() => {
this.onNewUpdateAvailable();
});
});
this.rootScopeListener1 = this.$rootScope.$on(
RootScopeMessages.ReloadExtendedData,
() => {
this.reloadExtendedData();
}
);
this.rootScopeListener2 = this.$rootScope.$on(
RootScopeMessages.NewUpdateAvailable,
() => {
this.$timeout(() => {
this.onNewUpdateAvailable();
});
}
);
}
/** @override */
@@ -202,11 +213,11 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
}
break;
case AppStateEvent.BeganBackupDownload:
statusService.setMessage("Saving local backup…");
statusService.setMessage('Saving local backup…');
break;
case AppStateEvent.EndedBackupDownload: {
const successMessage = "Successfully saved backup.";
const errorMessage = "Unable to save local backup.";
const successMessage = 'Successfully saved backup.';
const errorMessage = 'Unable to save local backup.';
statusService.setMessage(data.success ? successMessage : errorMessage);
const twoSeconds = 2000;
@@ -237,12 +248,12 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
break;
case ApplicationEvent.EnteredOutOfSync:
this.setState({
outOfSync: true
outOfSync: true,
});
break;
case ApplicationEvent.ExitedOutOfSync:
this.setState({
outOfSync: false
outOfSync: false,
});
break;
case ApplicationEvent.CompletedFullSync:
@@ -290,17 +301,15 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
CollectionSort.Title,
'asc',
(theme: SNTheme) => {
return (
theme.package_info &&
theme.package_info.dock_icon
);
return theme.package_info && theme.package_info.dock_icon;
}
);
this.observerRemovers.push(this.application.streamItems(
ContentType.Component,
async () => {
const components = this.application.getItems(ContentType.Component) as SNComponent[];
this.observerRemovers.push(
this.application.streamItems(ContentType.Component, async () => {
const components = this.application.getItems(
ContentType.Component
) as SNComponent[];
this.rooms = components.filter((candidate) => {
return candidate.area === ComponentArea.Rooms && !candidate.deleted;
});
@@ -308,33 +317,38 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
this.queueExtReload = false;
this.reloadExtendedData();
}
}
));
})
);
this.observerRemovers.push(this.application.streamItems(
ContentType.Theme,
async () => {
const themes = this.application.getDisplayableItems(ContentType.Theme) as SNTheme[];
this.observerRemovers.push(
this.application.streamItems(ContentType.Theme, async () => {
const themes = this.application.getDisplayableItems(
ContentType.Theme
) as SNTheme[];
this.themesWithIcons = themes;
this.reloadDockShortcuts();
}
));
})
);
}
registerComponentHandler() {
this.unregisterComponent = this.application.componentManager!.registerHandler({
identifier: 'room-bar',
areas: [ComponentArea.Rooms, ComponentArea.Modal],
focusHandler: (component, focused) => {
if (component.isEditor() && focused) {
if (component.package_info?.identifier === 'org.standardnotes.standard-sheets') {
return;
this.unregisterComponent =
this.application.componentManager!.registerHandler({
identifier: 'room-bar',
areas: [ComponentArea.Rooms, ComponentArea.Modal],
focusHandler: (component, focused) => {
if (component.isEditor() && focused) {
if (
component.package_info?.identifier ===
'org.standardnotes.standard-sheets'
) {
return;
}
this.closeAllRooms();
this.closeAccountMenu();
}
this.closeAllRooms();
this.closeAccountMenu();
}
}
});
},
});
}
updateSyncStatus() {
@@ -354,17 +368,17 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
statusManager.setMessage('');
}, 2000);
} else if (stats.uploadTotalCount > 20) {
const completionPercentage = stats.uploadCompletionCount === 0
? 0
: stats.uploadCompletionCount / stats.uploadTotalCount;
const completionPercentage =
stats.uploadCompletionCount === 0
? 0
: stats.uploadCompletionCount / stats.uploadTotalCount;
const stringPercentage = completionPercentage.toLocaleString(
undefined,
{ style: 'percent' }
);
const stringPercentage = completionPercentage.toLocaleString(undefined, {
style: 'percent',
});
statusManager.setMessage(
`Syncing ${stats.uploadTotalCount} items (${stringPercentage} complete)`,
`Syncing ${stats.uploadTotalCount} items (${stringPercentage} complete)`
);
} else {
statusManager.setMessage('');
@@ -398,8 +412,10 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
* then closing it after a short delay.
*/
const extWindow = this.rooms.find((room) => {
return room.package_info.identifier === this.application
.getNativeExtService().extManagerId;
return (
room.package_info.identifier ===
this.application.getNativeExtService().extManagerId
);
});
if (!extWindow) {
this.queueExtReload = true;
@@ -419,11 +435,13 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
}
async openSecurityUpdate() {
if (await confirmDialog({
title: STRING_UPGRADE_ACCOUNT_CONFIRM_TITLE,
text: STRING_UPGRADE_ACCOUNT_CONFIRM_TEXT,
confirmButtonText: STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON,
})) {
if (
await confirmDialog({
title: STRING_UPGRADE_ACCOUNT_CONFIRM_TITLE,
text: STRING_UPGRADE_ACCOUNT_CONFIRM_TEXT,
confirmButtonText: STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON,
})
) {
preventRefreshing(STRING_CONFIRM_APP_QUIT_DURING_UPGRADE, async () => {
await this.application.upgradeProtocolVersion();
});
@@ -453,25 +471,27 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
refreshData() {
this.isRefreshing = true;
this.application.sync({
queueStrategy: SyncQueueStrategy.ForceSpawnNew,
checkIntegrity: true
}).then((response) => {
this.$timeout(() => {
this.isRefreshing = false;
}, 200);
if (response && response.error) {
this.application.alertService!.alert(
STRING_GENERIC_SYNC_ERROR
);
} else {
this.syncUpdated();
}
});
this.application
.sync({
queueStrategy: SyncQueueStrategy.ForceSpawnNew,
checkIntegrity: true,
})
.then((response) => {
this.$timeout(() => {
this.isRefreshing = false;
}, 200);
if (response && response.error) {
this.application.alertService!.alert(STRING_GENERIC_SYNC_ERROR);
} else {
this.syncUpdated();
}
});
}
syncUpdated() {
this.lastSyncDate = dateToLocalizedString(this.application.getLastSyncDate()!);
this.lastSyncDate = dateToLocalizedString(
this.application.getLastSyncDate()!
);
}
onNewUpdateAvailable() {
@@ -480,9 +500,7 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
clickedNewUpdateAnnouncement() {
this.newUpdateAvailable = false;
this.application.alertService!.alert(
STRING_NEW_UPDATE_READY
);
this.application.alertService!.alert(STRING_NEW_UPDATE_READY);
}
reloadDockShortcuts() {
@@ -499,7 +517,7 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
shortcuts.push({
name: name,
component: theme,
icon: icon
icon: icon,
} as DockShortcut);
}
this.setState({
@@ -514,7 +532,7 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
} else {
return a.name.localeCompare(b.name);
}
})
}),
});
}
@@ -553,7 +571,7 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
text:
'If you wish to go back to a stable version, make sure to sign out ' +
'of this beta app first.<br>You can silence this warning from the ' +
'<em>Account</em> menu.'
'<em>Account</em> menu.',
});
}
@@ -563,6 +581,10 @@ class FooterViewCtrl extends PureViewCtrl<unknown, {
}
this.appState.accountMenu.setShow(false);
}
clickPreferences() {
this.appState.preferences.openPreferences();
}
}
export class FooterView extends WebDirective {
@@ -575,7 +597,7 @@ export class FooterView extends WebDirective {
this.controllerAs = 'ctrl';
this.bindToController = true;
this.scope = {
application: '='
application: '=',
};
}
}

View File

@@ -310,58 +310,11 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
await this.selectNote(note, true);
}
if (this.state.selectedNotes[note.uuid]) {
const { clientHeight } = document.documentElement;
const defaultFontSize = window.getComputedStyle(
document.documentElement
).fontSize;
const maxContextMenuHeight = parseFloat(defaultFontSize) * 30;
const footerHeight = 32;
// Open up-bottom is default behavior
let openUpBottom = true;
const bottomSpace = clientHeight - footerHeight - e.clientY;
const upSpace = e.clientY;
// If not enough space to open up-bottom
if (maxContextMenuHeight > bottomSpace) {
// If there's enough space, open bottom-up
if (upSpace > maxContextMenuHeight) {
openUpBottom = false;
this.appState.notes.setContextMenuMaxHeight(
'auto'
);
// Else, reduce max height (menu will be scrollable) and open in whichever direction there's more space
} else {
if (upSpace > bottomSpace) {
this.appState.notes.setContextMenuMaxHeight(
upSpace - 2
);
openUpBottom = false;
} else {
this.appState.notes.setContextMenuMaxHeight(
bottomSpace - 2
);
}
}
} else {
this.appState.notes.setContextMenuMaxHeight(
'auto'
);
}
if (openUpBottom) {
this.appState.notes.setContextMenuPosition({
top: e.clientY,
left: e.clientX,
});
} else {
this.appState.notes.setContextMenuPosition({
bottom: clientHeight - e.clientY,
left: e.clientX,
});
}
this.appState.notes.setContextMenuClickLocation({
x: e.clientX,
y: e.clientY,
});
this.appState.notes.reloadContextMenuLayout();
this.appState.notes.setContextMenuOpen(true);
}
}

View File

@@ -12,6 +12,8 @@ $z-index-footer-bar: 2000;
$z-index-footer-bar-item: 2000;
$z-index-footer-bar-item-panel: 2000;
$z-index-preferences: 3000;
$z-index-lock-screen: 10000;
$z-index-modal: 10000;
@@ -238,3 +240,7 @@ $footer-height: 32px;
.icon {
margin-right: 4px;
}
.z-index-preferences {
z-index: $z-index-preferences;
}

View File

@@ -0,0 +1,46 @@
.preferences-menu-item {
@extend .border-box;
@extend .w-auto;
@extend .h-auto;
@extend .rounded;
@extend .min-w-42;
@extend .py-2;
@extend .px-4;
@extend .flex;
@extend .flex-row;
@extend .items-center;
@extend .justify-start;
@extend .gap-1;
@extend .text-sm;
@extend .cursor-pointer;
@extend .border-transparent;
@extend .border-solid;
@extend .border-1;
.icon {
color: var(--sn-stylekit-grey-1);
@extend .text-base;
}
&:hover {
@extend .border-gray-300;
@extend .border-solid;
@extend .border-1;
}
}
.preferences-menu-item.selected {
@extend .border-info;
@extend .color-info;
@extend .font-bold;
background-color: var(--sn-stylekit-info-backdrop-color);
.icon {
@extend .color-info;
}
}

View File

@@ -4,42 +4,14 @@
height: 90vh;
}
/**
* A button that is just an icon. Separated from .sn-button because there
* is almost no style overlap.
*/
.sn-icon-button {
@extend .w-8;
@extend .h-8;
@extend .flex;
@extend .justify-center;
@extend .items-center;
@extend .border-solid;
@extend .border-1;
@extend .border-neutral;
@extend .bg-clip-padding;
@extend .m-0;
@extend .p-0;
@extend .bg-transparent;
@extend .cursor-pointer;
@extend .rounded-full;
@extend .color-neutral;
@extend .hover\:color-text;
@extend .focus\:color-text;
@extend .hover\:bg-contrast;
@extend .focus\:bg-contrast;
@extend .focus\:outline-none;
@extend .focus\:ring-info;
}
.sn-icon {
@extend .h-5;
@extend .w-5;
@extend .fill-current;
&.sn-icon--small {
@extend .h-3\.5 ;
@extend .w-3\.5 ;
@extend .h-3\.5;
@extend .w-3\.5;
}
}
@@ -57,7 +29,7 @@
&[data-state='collapsed'] {
display: none;
}
&.sn-dropdown--animated {
@extend .transition-transform;
@extend .duration-150;
@@ -105,10 +77,7 @@
transform: translate(0px, -50%);
&.sn-switch-handle--right {
transform: translate(
calc(2rem - 1.125rem),
-50%
);
transform: translate(calc(2rem - 1.125rem), -50%);
}
}
@@ -152,3 +121,23 @@
@extend .hover\:bg-secondary-contrast;
@extend .focus\:bg-secondary-contrast;
}
.sn-titlebar {
@extend .w-full;
@extend .bg-default;
@extend .h-14;
@extend .border-bottom-solid;
@extend .border-b-1;
@extend .border-gray-300;
@extend .py-3;
@extend .px-3;
@extend .flex;
@extend .flex-row;
}
.sn-title {
@extend .font-bold;
}

View File

@@ -1,15 +1,16 @@
@import "sn-stylekit/dist/stylekit";
@import "main";
@import "ui";
@import "footer";
@import "tags";
@import "notes";
@import "editor";
@import "menus";
@import "modals";
@import "lock-screen";
@import "stylekit-sub";
@import "ionicons";
@import "reach-sub";
@import "sessions-modal";
@import "sn";
@import 'sn-stylekit/dist/stylekit';
@import 'main';
@import 'ui';
@import 'footer';
@import 'tags';
@import 'notes';
@import 'editor';
@import 'menus';
@import 'modals';
@import 'lock-screen';
@import 'stylekit-sub';
@import 'ionicons';
@import 'reach-sub';
@import 'sessions-modal';
@import 'preferences';
@import 'sn';

View File

@@ -33,6 +33,14 @@
rel='noopener',
target='_blank'
) Help
.sn-component(ng-if='ctrl.isDeprecated && !ctrl.deprecationMessageDismissed')
.sk-app-bar.no-edges.no-top-edge.dynamic-height
.left
.sk-app-bar-item
.sk-label.warning {{ctrl.deprecationMessage || 'This extension is deprecated.'}}
.right
.sk-app-bar-item(ng-click='ctrl.dismissDeprecationMessage()')
button.sn-button.small.info Dismiss
.sn-component(ng-if="ctrl.error == 'offline-restricted'")
.sk-panel.static

View File

@@ -34,6 +34,7 @@
window._extensions_manager_location = "<%= ENV['EXTENSIONS_MANAGER_LOCATION'] %>";
window._batch_manager_location = "<%= ENV['BATCH_MANAGER_LOCATION'] %>";
window._bugsnag_api_key = "<%= ENV['BUGSNAG_API_KEY'] %>";
window._enable_unfinished_features = "<%= ENV['ENABLE_UNFINISHED_FEATURES'] %>"
</script>
<% if Rails.env.development? %>

View File

@@ -33,12 +33,14 @@
data-next-version-sync-server="<%= env.DEV_NEXT_VERSION_SYNC_SERVER || env.DEV_DEFAULT_SYNC_SERVER %>"
data-extensions-manager-location="<%= env.DEV_EXTENSIONS_MANAGER_LOCATION %>"
data-bugsnag-api-key="<%= env.DEV_BUGSNAG_API_KEY %>"
data-enable-unfinished-features="<%= env.ENABLE_UNFINISHED_FEATURES %>"
>
<script>
window._default_sync_server = document.body.dataset.defaultSyncServer || "https://sync.standardnotes.org";
window._next_version_sync_server = document.body.dataset.nextVersionSyncServer || "https://api.standardnotes.com";
window._extensions_manager_location = document.body.dataset.extensionsManagerLocation || "public/extensions/extensions-manager/dist/index.html";
window._bugsnag_api_key = document.body.dataset.bugsnagApiKey;
window._enable_unfinished_features = document.body.dataset.enableUnfinishedFeatures === 'true';
</script>
<application-group-view />
</body>

View File

@@ -1,6 +1,6 @@
{
"name": "standard-notes-web",
"version": "3.8.13",
"version": "3.8.14",
"license": "AGPL-3.0-or-later",
"repository": {
"type": "git",
@@ -55,7 +55,7 @@
"pug-loader": "^2.4.0",
"sass-loader": "^8.0.2",
"serve-static": "^1.14.1",
"sn-stylekit": "5.2.1",
"sn-stylekit": "5.2.3",
"ts-loader": "^8.0.17",
"typescript": "4.2.3",
"typescript-eslint": "0.0.1-alpha.0",
@@ -71,7 +71,7 @@
"@reach/checkbox": "^0.13.2",
"@reach/dialog": "^0.13.0",
"@standardnotes/sncrypto-web": "1.2.10",
"@standardnotes/snjs": "2.7.9",
"@standardnotes/snjs": "2.7.14",
"mobx": "^6.1.6",
"mobx-react-lite": "^3.2.0",
"preact": "^10.5.12"

128
yarn.lock
View File

@@ -1826,6 +1826,14 @@
"@reach/utils" "0.14.0"
tslib "^2.1.0"
"@reach/auto-id@0.15.2":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@reach/auto-id/-/auto-id-0.15.2.tgz#289640b47545a32f3621469ba497ba5c040b349c"
integrity sha512-K7d5qhYBlBHIjy+IpSEAyMeB5VTZ9m0tZdr7xyNd5Fr6oeefHEvJiJGuQpubP5bDoe7ShC3y0VQGFmT0g7KcZg==
dependencies:
"@reach/utils" "0.15.2"
tslib "^2.3.0"
"@reach/checkbox@^0.13.2":
version "0.13.2"
resolved "https://registry.yarnpkg.com/@reach/checkbox/-/checkbox-0.13.2.tgz#b972b922cf6cea0c2bbabca0129c58307b566a4e"
@@ -1837,6 +1845,14 @@
prop-types "^15.7.2"
tslib "^2.1.0"
"@reach/descendants@0.15.2":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@reach/descendants/-/descendants-0.15.2.tgz#b0641f0bc864d91271364678dea51cf86b2e5c66"
integrity sha512-GdQXWVpscss89MOhWh+sL4TnIn0qX1y+Te3wE72aKQrz/QCR69JFEW4wftovmF7rFm4/kDZcp14lMy512U6VlA==
dependencies:
"@reach/utils" "0.15.2"
tslib "^2.3.0"
"@reach/dialog@0.13.0", "@reach/dialog@^0.13.0":
version "0.13.0"
resolved "https://registry.yarnpkg.com/@reach/dialog/-/dialog-0.13.0.tgz#2110725c3b8a3c64685834cdc9f3ce5c15617809"
@@ -1859,6 +1875,18 @@
prop-types "^15.7.2"
tslib "^2.1.0"
"@reach/listbox@^0.15.0":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@reach/listbox/-/listbox-0.15.2.tgz#21aacc5340deb1b819d522f7f7fd4caac093c8a4"
integrity sha512-bXXelcm+PvAWAmhHQEaEVU6IQvTieIdoYcIrs5JsKzH98M+pp940Gdm0d2uDcmFatx/I9OMTAsOTJ129RPECrA==
dependencies:
"@reach/auto-id" "0.15.2"
"@reach/descendants" "0.15.2"
"@reach/machine" "0.15.2"
"@reach/popover" "0.15.2"
"@reach/utils" "0.15.2"
prop-types "^15.7.2"
"@reach/machine@0.13.2":
version "0.13.2"
resolved "https://registry.yarnpkg.com/@reach/machine/-/machine-0.13.2.tgz#744302f5ce2d4e5fd0527ae0baa60d325b2325d8"
@@ -1868,6 +1896,44 @@
"@xstate/fsm" "1.4.0"
tslib "^2.1.0"
"@reach/machine@0.15.2":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@reach/machine/-/machine-0.15.2.tgz#f1558e63a6b304e6cc12feefb0d0dd315b32c962"
integrity sha512-op8Duhp6rNCReZgdVScbIzoXMPFYP4nje6dvg4g2GKo8hUkyQpebsUFnUNNNv6NWdbmhIRiKHFlTgSl2gIlh5Q==
dependencies:
"@reach/utils" "0.15.2"
"@xstate/fsm" "1.4.0"
tslib "^2.3.0"
"@reach/menu-button@^0.15.1":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@reach/menu-button/-/menu-button-0.15.2.tgz#00f91402be3ff23d3b4cfe377529f4f9e82a78fe"
integrity sha512-slEji1AnfnH134YDZSTwst4nZHYNrfyg9GF+A8p1+kGc8N3JsPysHc7uIUZ/IsK+PdaOCB+r9kuL1QWBgNO+jw==
dependencies:
"@reach/auto-id" "0.15.2"
"@reach/descendants" "0.15.2"
"@reach/popover" "0.15.2"
"@reach/utils" "0.15.2"
prop-types "^15.7.2"
tiny-warning "^1.0.3"
tslib "^2.3.0"
"@reach/observe-rect@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@reach/observe-rect/-/observe-rect-1.2.0.tgz#d7a6013b8aafcc64c778a0ccb83355a11204d3b2"
integrity sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==
"@reach/popover@0.15.2":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@reach/popover/-/popover-0.15.2.tgz#e310e2afe82afa35929354f1fd1a747cdebb7654"
integrity sha512-92Ov7VPXjn4ciOVupeekki03lSMz9NAmw2BjWYE9mVvYWKyDx5jx2srtbkIqUaSCkAVV3KEsFUia8aMm60FDZg==
dependencies:
"@reach/portal" "0.15.2"
"@reach/rect" "0.15.2"
"@reach/utils" "0.15.2"
tabbable "^4.0.0"
tslib "^2.3.0"
"@reach/portal@0.13.0":
version "0.13.0"
resolved "https://registry.yarnpkg.com/@reach/portal/-/portal-0.13.0.tgz#bed220d41097deb1454a7928b22529ba10d3ea2b"
@@ -1876,6 +1942,25 @@
"@reach/utils" "0.13.0"
tslib "^2.0.0"
"@reach/portal@0.15.2":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@reach/portal/-/portal-0.15.2.tgz#fe7aa3d9a475ec07686c39bab3a0d85093fa91fb"
integrity sha512-5x+dchGr4btRnLazwmyCYbSPVJAIrw0eXwhz7Vj9uT/EIp43WzOtTcODdLOoH6Ol2QLjX1Yt/fBjdK9+UAKxSA==
dependencies:
"@reach/utils" "0.15.2"
tslib "^2.3.0"
"@reach/rect@0.15.2":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@reach/rect/-/rect-0.15.2.tgz#734e3f17a499d6e22bd2ea95856c801c41ed66fd"
integrity sha512-S2lzvvfclUHdvgcfW/eoz0i729HJvG5f6ayVaXcKz+X6LKF9i9Jdhfwsz7b3UmnSCihKNs0cX5tyWfWr1E1JFw==
dependencies:
"@reach/observe-rect" "1.2.0"
"@reach/utils" "0.15.2"
prop-types "^15.7.2"
tiny-warning "^1.0.3"
tslib "^2.3.0"
"@reach/utils@0.13.0":
version "0.13.0"
resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.13.0.tgz#2da775a910d8894bb34e1e94fe95842674f71844"
@@ -1903,6 +1988,14 @@
tslib "^2.1.0"
warning "^4.0.3"
"@reach/utils@0.15.2":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.15.2.tgz#a63a761eb36266bf33d65cb91520dd85a04ae116"
integrity sha512-Lr1SJ5X4hEjD/M0TAonURM8wytM/JuPSuIP7t+e5cil34pThyLsBvTGeNfmpSgaLJ5vlsv0x9u6g4SRAEr84Og==
dependencies:
tiny-warning "^1.0.3"
tslib "^2.3.0"
"@reach/visually-hidden@0.13.0":
version "0.13.0"
resolved "https://registry.yarnpkg.com/@reach/visually-hidden/-/visually-hidden-0.13.0.tgz#cace36d9bb80ffb797374fcaea989391b881038f"
@@ -1936,10 +2029,10 @@
"@standardnotes/sncrypto-common" "^1.2.7"
libsodium-wrappers "^0.7.8"
"@standardnotes/snjs@2.7.9":
version "2.7.9"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.7.9.tgz#8b2a5d0f6248e1872b2fe68a73307f754528b4c3"
integrity sha512-FNiLXdaUb2+WpCteTbfg/iiv4k0UXFzz57RnY1O8X40vEVFVY8VjrJuJxxwDuZqidUZeLIdTMlmfdKWoD+NRDA==
"@standardnotes/snjs@2.7.14":
version "2.7.14"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.7.14.tgz#3c1e57a53cfe61957bbcb253281c5c43611c9acf"
integrity sha512-xh71KLOCjodmVdrnNT0zfChB4fy3FaWMvWoh/5iso+myI6tQ8X59+gUM+VuNk9N9rwyItiBvq88BD82RUia8uQ==
dependencies:
"@standardnotes/auth" "^2.0.0"
"@standardnotes/sncrypto-common" "^1.2.9"
@@ -7815,10 +7908,14 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
sn-stylekit@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/sn-stylekit/-/sn-stylekit-5.2.1.tgz#d56a38017d6b45f5c2ebcf7b2c2db94c50a26e27"
integrity sha512-kupuH9XlPy5TuO2a2E+sLQnF41VQ0a4cwHRltNhQSI6O135n4HkkDYgyEAvy5DKcc0aun4KHy5mz9kYefQvciw==
sn-stylekit@5.2.3:
version "5.2.3"
resolved "https://registry.yarnpkg.com/sn-stylekit/-/sn-stylekit-5.2.3.tgz#24246471c03cde5129bda51a08fabef4d3c4880c"
integrity sha512-hzziH89IY2UjmGh8OYgapb+/QVD6P6NNjnoyzSyveOh671MM9Z4IaPLZTJckgxJVjV0q7G495Pxfta5r4CSRDQ==
dependencies:
"@reach/listbox" "^0.15.0"
"@reach/menu-button" "^0.15.1"
prop-types "^15.7.2"
snapdragon-node@^2.0.1:
version "2.1.1"
@@ -8266,6 +8363,11 @@ svgo@^1.2.2:
unquote "~1.1.1"
util.promisify "~1.0.0"
tabbable@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-4.0.0.tgz#5bff1d1135df1482cf0f0206434f15eadbeb9261"
integrity sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==
table@^6.0.4:
version "6.0.7"
resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34"
@@ -8339,6 +8441,11 @@ timers-browserify@^2.0.4:
dependencies:
setimmediate "^1.0.4"
tiny-warning@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
to-arraybuffer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
@@ -8437,6 +8544,11 @@ tslib@^2.1.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
tslib@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"