Merge branch 'release/3.8.14' into main
@@ -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
|
||||
|
||||
3
app/assets/icons/ic-accessibility.svg
Normal 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 |
3
app/assets/icons/ic-help.svg
Normal 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 |
3
app/assets/icons/ic-keyboard.svg
Normal 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 |
3
app/assets/icons/ic-listed.svg
Normal 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 |
3
app/assets/icons/ic-security.svg
Normal 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 |
3
app/assets/icons/ic-settings-filled.svg
Normal 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 |
3
app/assets/icons/ic-star.svg
Normal 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 |
3
app/assets/icons/ic-themes.svg
Normal 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 |
3
app/assets/icons/ic-user.svg
Normal 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 |
@@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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: '@',
|
||||
});
|
||||
|
||||
53
app/assets/javascripts/components/IconButton.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@@ -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"
|
||||
|
||||
@@ -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} />
|
||||
|
||||
23
app/assets/javascripts/components/PreferencesMenuItem.tsx
Normal 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>
|
||||
);
|
||||
@@ -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);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
13
app/assets/javascripts/components/TitleBar.tsx
Normal 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>;
|
||||
};
|
||||
22
app/assets/javascripts/components/preferences/index.tsx
Normal 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
|
||||
);
|
||||
50
app/assets/javascripts/components/preferences/mock-state.ts
Normal 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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
33
app/assets/javascripts/components/preferences/pane.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
28
app/assets/javascripts/components/preferences/view.tsx
Normal 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>
|
||||
);
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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: '=',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
46
app/assets/stylesheets/_preferences.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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? %>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||