diff --git a/app/assets/javascripts/components/ApplicationView.tsx b/app/assets/javascripts/components/ApplicationView.tsx index 821be8977..9f5191c1c 100644 --- a/app/assets/javascripts/components/ApplicationView.tsx +++ b/app/assets/javascripts/components/ApplicationView.tsx @@ -1,5 +1,5 @@ import { ApplicationGroup } from '@/ui_models/application_group'; -import { getPlatformString } from '@/utils'; +import { getPlatformString, getWindowUrlParams } from '@/utils'; import { AppStateEvent, PanelResizedData } from '@/ui_models/app_state'; import { ApplicationEvent, @@ -144,15 +144,12 @@ export class ApplicationView extends PureComponent { } async handleDemoSignInFromParams() { - if ( - window.location.href.includes('demo') && - !this.application.hasAccount() - ) { - await this.application.setCustomHost( - 'https://syncing-server-demo.standardnotes.com' - ); - this.application.signIn('demo@standardnotes.org', 'password'); + const token = getWindowUrlParams().get('demo-token'); + if (!token || this.application.hasAccount()) { + return; } + + await this.application.sessions.populateSessionFromDemoShareToken(token); } presentPermissionsDialog = (dialog: PermissionDialog) => { diff --git a/app/assets/javascripts/components/PurchaseFlow/PurchaseFlowWrapper.tsx b/app/assets/javascripts/components/PurchaseFlow/PurchaseFlowWrapper.tsx index 3e77e77e5..aedf3b039 100644 --- a/app/assets/javascripts/components/PurchaseFlow/PurchaseFlowWrapper.tsx +++ b/app/assets/javascripts/components/PurchaseFlow/PurchaseFlowWrapper.tsx @@ -1,6 +1,6 @@ import { WebApplication } from '@/ui_models/application'; import { AppState } from '@/ui_models/app_state'; -import { isDesktopApplication } from '@/utils'; +import { getWindowUrlParams, isDesktopApplication } from '@/utils'; import { observer } from 'mobx-react-lite'; import { FunctionComponent } from 'preact'; import { PurchaseFlowView } from './PurchaseFlowView'; @@ -29,7 +29,7 @@ export const loadPurchaseFlowUrl = async ( application: WebApplication ): Promise => { const url = await getPurchaseFlowUrl(application); - const params = new URLSearchParams(window.location.search); + const params = getWindowUrlParams(); const period = params.get('period') ? `&period=${params.get('period')}` : ''; const plan = params.get('plan') ? `&plan=${params.get('plan')}` : ''; if (url) { diff --git a/app/assets/javascripts/utils/index.ts b/app/assets/javascripts/utils/index.ts index f16780ca7..a572f9209 100644 --- a/app/assets/javascripts/utils/index.ts +++ b/app/assets/javascripts/utils/index.ts @@ -1,170 +1,5 @@ -import { Platform, platformFromString } from '@standardnotes/snjs'; -import { IsDesktopPlatform, IsWebPlatform } from '@/version'; -import { - BYTES_IN_ONE_KILOBYTE, - BYTES_IN_ONE_MEGABYTE, - EMAIL_REGEX, -} from '../constants'; -export { isMobile } from './isMobile'; - -declare const process: { - env: { - NODE_ENV: string | null | undefined; - }; -}; - -export const isDev = process.env.NODE_ENV === 'development'; - -export function getPlatformString() { - try { - const platform = navigator.platform.toLowerCase(); - let trimmed = ''; - if (platform.includes('mac')) { - trimmed = 'mac'; - } else if (platform.includes('win')) { - trimmed = 'windows'; - } else if (platform.includes('linux')) { - trimmed = 'linux'; - } else { - /** Treat other platforms as linux */ - trimmed = 'linux'; - } - return trimmed + (isDesktopApplication() ? '-desktop' : '-web'); - } catch (e) { - return 'linux-web'; - } -} - -export function getPlatform(): Platform { - return platformFromString(getPlatformString()); -} - -export function isSameDay(dateA: Date, dateB: Date): boolean { - return ( - dateA.getFullYear() === dateB.getFullYear() && - dateA.getMonth() === dateB.getMonth() && - dateA.getDate() === dateB.getDate() - ); -} - -/** Via https://davidwalsh.name/javascript-debounce-function */ -export function debounce( - this: any, - func: any, - wait: number, - immediate = false -) { - let timeout: any; - return () => { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const context = this; - // eslint-disable-next-line prefer-rest-params - const args = arguments; - const later = function () { - timeout = null; - if (!immediate) func.apply(context, args); - }; - const callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; -} - -// https://tc39.github.io/ecma262/#sec-array.prototype.includes -if (!Array.prototype.includes) { - // eslint-disable-next-line no-extend-native - Object.defineProperty(Array.prototype, 'includes', { - value: function (searchElement: any, fromIndex: number) { - if (this == null) { - throw new TypeError('"this" is null or not defined'); - } - - // 1. Let O be ? ToObject(this value). - const o = Object(this); - - // 2. Let len be ? ToLength(? Get(O, "length")). - const len = o.length >>> 0; - - // 3. If len is 0, return false. - if (len === 0) { - return false; - } - - // 4. Let n be ? ToInteger(fromIndex). - // (If fromIndex is undefined, this step produces the value 0.) - const n = fromIndex | 0; - - // 5. If n ≥ 0, then - // a. Let k be n. - // 6. Else n < 0, - // a. Let k be len + n. - // b. If k < 0, let k be 0. - let k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); - - function sameValueZero(x: number, y: number) { - return ( - x === y || - (typeof x === 'number' && - typeof y === 'number' && - isNaN(x) && - isNaN(y)) - ); - } - - // 7. Repeat, while k < len - while (k < len) { - // a. Let elementK be the result of ? Get(O, ! ToString(k)). - // b. If SameValueZero(searchElement, elementK) is true, return true. - if (sameValueZero(o[k], searchElement)) { - return true; - } - // c. Increase k by 1. - k++; - } - - // 8. Return false - return false; - }, - }); -} - -export async function preventRefreshing( - message: string, - operation: () => Promise | void -) { - const onBeforeUnload = window.onbeforeunload; - try { - window.onbeforeunload = () => message; - await operation(); - } finally { - window.onbeforeunload = onBeforeUnload; - } -} - -if (!IsWebPlatform && !IsDesktopPlatform) { - throw Error( - 'Neither __WEB__ nor __DESKTOP__ is true. Check your configuration files.' - ); -} - -export function isDesktopApplication() { - return IsDesktopPlatform; -} - -export function getDesktopVersion() { - return (window as any).electronAppVersion; -} - -export const isEmailValid = (email: string): boolean => { - return EMAIL_REGEX.test(email); -}; - -export const openInNewTab = (url: string) => { - const newWindow = window.open(url, '_blank', 'noopener,noreferrer'); - if (newWindow) newWindow.opener = null; -}; - -export const convertStringifiedBooleanToBoolean = (value: string) => { - return value !== 'false'; -}; +export * from './calculateSubmenuStyle'; +export * from './concatenateUint8Arrays'; +export * from './isMobile'; +export * from './stringUtils'; +export * from './utils'; diff --git a/app/assets/javascripts/utils/utils.ts b/app/assets/javascripts/utils/utils.ts new file mode 100644 index 000000000..920baa69c --- /dev/null +++ b/app/assets/javascripts/utils/utils.ts @@ -0,0 +1,170 @@ +import { Platform, platformFromString } from '@standardnotes/snjs'; +import { IsDesktopPlatform, IsWebPlatform } from '@/version'; +import { EMAIL_REGEX } from '../constants'; +export { isMobile } from './isMobile'; + +declare const process: { + env: { + NODE_ENV: string | null | undefined; + }; +}; + +export const isDev = process.env.NODE_ENV === 'development'; + +export function getPlatformString() { + try { + const platform = navigator.platform.toLowerCase(); + let trimmed = ''; + if (platform.includes('mac')) { + trimmed = 'mac'; + } else if (platform.includes('win')) { + trimmed = 'windows'; + } else if (platform.includes('linux')) { + trimmed = 'linux'; + } else { + /** Treat other platforms as linux */ + trimmed = 'linux'; + } + return trimmed + (isDesktopApplication() ? '-desktop' : '-web'); + } catch (e) { + return 'linux-web'; + } +} + +export function getPlatform(): Platform { + return platformFromString(getPlatformString()); +} + +export function isSameDay(dateA: Date, dateB: Date): boolean { + return ( + dateA.getFullYear() === dateB.getFullYear() && + dateA.getMonth() === dateB.getMonth() && + dateA.getDate() === dateB.getDate() + ); +} + +/** Via https://davidwalsh.name/javascript-debounce-function */ +export function debounce( + this: any, + func: any, + wait: number, + immediate = false +) { + let timeout: any; + return () => { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const context = this; + // eslint-disable-next-line prefer-rest-params + const args = arguments; + const later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + const callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +} + +// https://tc39.github.io/ecma262/#sec-array.prototype.includes +if (!Array.prototype.includes) { + // eslint-disable-next-line no-extend-native + Object.defineProperty(Array.prototype, 'includes', { + value: function (searchElement: any, fromIndex: number) { + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + // 1. Let O be ? ToObject(this value). + const o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + const len = o.length >>> 0; + + // 3. If len is 0, return false. + if (len === 0) { + return false; + } + + // 4. Let n be ? ToInteger(fromIndex). + // (If fromIndex is undefined, this step produces the value 0.) + const n = fromIndex | 0; + + // 5. If n ≥ 0, then + // a. Let k be n. + // 6. Else n < 0, + // a. Let k be len + n. + // b. If k < 0, let k be 0. + let k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); + + function sameValueZero(x: number, y: number) { + return ( + x === y || + (typeof x === 'number' && + typeof y === 'number' && + isNaN(x) && + isNaN(y)) + ); + } + + // 7. Repeat, while k < len + while (k < len) { + // a. Let elementK be the result of ? Get(O, ! ToString(k)). + // b. If SameValueZero(searchElement, elementK) is true, return true. + if (sameValueZero(o[k], searchElement)) { + return true; + } + // c. Increase k by 1. + k++; + } + + // 8. Return false + return false; + }, + }); +} + +export async function preventRefreshing( + message: string, + operation: () => Promise | void +) { + const onBeforeUnload = window.onbeforeunload; + try { + window.onbeforeunload = () => message; + await operation(); + } finally { + window.onbeforeunload = onBeforeUnload; + } +} + +if (!IsWebPlatform && !IsDesktopPlatform) { + throw Error( + 'Neither __WEB__ nor __DESKTOP__ is true. Check your configuration files.' + ); +} + +export function isDesktopApplication() { + return IsDesktopPlatform; +} + +export function getDesktopVersion() { + return (window as any).electronAppVersion; +} + +export const isEmailValid = (email: string): boolean => { + return EMAIL_REGEX.test(email); +}; + +export const getWindowUrlParams = (): URLSearchParams => { + return new URLSearchParams(window.location.search); +}; + +export const openInNewTab = (url: string) => { + const newWindow = window.open(url, '_blank', 'noopener,noreferrer'); + if (newWindow) newWindow.opener = null; +}; + +export const convertStringifiedBooleanToBoolean = (value: string) => { + return value !== 'false'; +}; diff --git a/package.json b/package.json index 2f4f33d18..f14a9bc09 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,8 @@ "@babel/preset-typescript": "^7.16.7", "@reach/disclosure": "^0.16.2", "@reach/visually-hidden": "^0.16.0", - "@standardnotes/responses": "1.3.21", - "@standardnotes/services": "1.6.10", + "@standardnotes/responses": "1.4.0", + "@standardnotes/services": "1.6.11", "@standardnotes/stylekit": "5.17.0", "@svgr/webpack": "^6.2.1", "@types/jest": "^27.4.1", @@ -76,7 +76,7 @@ "@standardnotes/filepicker": "1.10.2", "@standardnotes/settings": "1.13.1", "@standardnotes/sncrypto-web": "1.8.0", - "@standardnotes/snjs": "2.91.2", + "@standardnotes/snjs": "2.92.0", "@zip.js/zip.js": "^2.4.7", "mobx": "^6.5.0", "mobx-react-lite": "^3.3.0", diff --git a/yarn.lock b/yarn.lock index ac5e88e92..02dbdbcb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2404,14 +2404,6 @@ "@standardnotes/common" "^1.17.0" jsonwebtoken "^8.5.1" -"@standardnotes/auth@^3.18.0": - version "3.18.0" - resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.18.0.tgz#682301abe8b15d297cd3d140682e8c85bc849953" - integrity sha512-7X11Uy9DtmTRgplvk9TkUjVqCxlv2gsgigM9rxtGPZQ++cb/iypD34RfhDVQ87tPtVL8LOlqtkZ/iC8eItS/JQ== - dependencies: - "@standardnotes/common" "^1.17.0" - jsonwebtoken "^8.5.1" - "@standardnotes/common@^1.17.0": version "1.17.0" resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.17.0.tgz#4f862dae7599bc6541bc259f98f93cd399581175" @@ -2430,7 +2422,7 @@ "@standardnotes/auth" "^3.18.0" "@standardnotes/features" "^1.35.6" -"@standardnotes/features@1.35.6": +"@standardnotes/features@1.35.6", "@standardnotes/features@^1.35.6": version "1.35.6" resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.35.6.tgz#177b34cdb5dc71e13aba0a6b57e5d2ab0849d740" integrity sha512-fiYZvlTpYyBcjnPSs1bN7pvIYWvQevZTEBTjNvOTo5ZqCtLoXu0p0hzU+17J2pYrjkh4vHZhYVxrRLXnyft3ZQ== @@ -2438,14 +2430,6 @@ "@standardnotes/auth" "^3.18.0" "@standardnotes/common" "^1.17.0" -"@standardnotes/features@^1.35.4": - version "1.35.4" - resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.35.4.tgz#0f82a13a5bf283bea832e2984d3a7a321419fdad" - integrity sha512-ir1XeJ7Q32OcqCH+iPmuFtT2curP1XdSMV+NkndvIthfsigZvE/e6IeUKX3eEG6gJVsNlUe06iyrFOjlxhQb5w== - dependencies: - "@standardnotes/auth" "^3.18.0" - "@standardnotes/common" "^1.17.0" - "@standardnotes/filepicker@1.10.2": version "1.10.2" resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.10.2.tgz#4e2d76a327e61c5d864ce6e0597786caa83f2e20" @@ -2461,24 +2445,24 @@ "@standardnotes/features" "^1.35.6" "@standardnotes/utils" "^1.4.3" -"@standardnotes/responses@1.3.21", "@standardnotes/responses@^1.3.21": - version "1.3.21" - resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.3.21.tgz#41898e1b155d3b01f26e98b10f12d6b74d8f3f4b" - integrity sha512-ARnLty1NRhxbDNNcAAYpQKLXTCmF/gZlecY36SADpSzZdxz4SQk37Vltx69IfzszWr4q50HLOhkwwmOpXQ+BcA== +"@standardnotes/responses@1.4.0", "@standardnotes/responses@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.4.0.tgz#6ce8c0e94bdf2bdf613904e330cba8708ab39a4b" + integrity sha512-eHjaJcYlZ/U49X2E3xH3etVmCVTc1BDV3XdUkSiDt5SsxavSHKKGiHu/h7QNHNYAmV/+yOU3N5YZAweBp2ZCMA== dependencies: "@standardnotes/auth" "^3.18.0" "@standardnotes/common" "^1.17.0" "@standardnotes/features" "^1.35.6" "@standardnotes/payloads" "^1.4.20" -"@standardnotes/services@1.6.10", "@standardnotes/services@^1.6.10": - version "1.6.10" - resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.6.10.tgz#171a07cdf6afb6b46c8142d0845bd07e0e2a8718" - integrity sha512-DqfaZ5PITimoo4SZdURaUAK2bJTNKNz5vIVlvtumLmtPAjBJYLue0nEIWaWPYRlkB4nFW1XImNEbsHjaHgfRfA== +"@standardnotes/services@1.6.11", "@standardnotes/services@^1.6.11": + version "1.6.11" + resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.6.11.tgz#a1ac0e28128a720e1c4f85220b0e4ed122ea4573" + integrity sha512-iGVobfAavRALJOvnhF24HHcule7jaUGx4xVH9Kr8g+9a2vVWRM3Fyp3EqE4LZjlS5u8Qz1i8BADzQtzajb1VmQ== dependencies: "@standardnotes/applications" "^1.2.6" "@standardnotes/common" "^1.17.0" - "@standardnotes/responses" "^1.3.21" + "@standardnotes/responses" "^1.4.0" "@standardnotes/utils" "^1.4.3" "@standardnotes/settings@1.13.1", "@standardnotes/settings@^1.13.1": @@ -2500,10 +2484,10 @@ buffer "^6.0.3" libsodium-wrappers "^0.7.9" -"@standardnotes/snjs@2.91.2": - version "2.91.2" - resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.91.2.tgz#782f70b6181813b9593143a93b5b1837811524eb" - integrity sha512-NupgizPiabyaA9xYlHShCqrLU2086khH2440FKfFXPVC/oVHSykzlq4HqwFX4/kj27SU+nKCluSEBBVQl0m73Q== +"@standardnotes/snjs@2.92.0": + version "2.92.0" + resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.92.0.tgz#92b926982a014f2584264bfcdb428347f7498c51" + integrity sha512-AlQiF2ToDAOdUdVhc8IGg6F9VCGqUKdWXdOPUzM9gB+vmSNz51cGz3CE0vaO0HfZ7bq7vav9wJ4EFIx8wSD04g== dependencies: "@standardnotes/applications" "^1.2.6" "@standardnotes/auth" "^3.18.0" @@ -2511,8 +2495,8 @@ "@standardnotes/domain-events" "^2.25.6" "@standardnotes/features" "^1.35.6" "@standardnotes/payloads" "^1.4.20" - "@standardnotes/responses" "^1.3.21" - "@standardnotes/services" "^1.6.10" + "@standardnotes/responses" "^1.4.0" + "@standardnotes/services" "^1.6.11" "@standardnotes/settings" "^1.13.1" "@standardnotes/sncrypto-common" "^1.7.3" "@standardnotes/utils" "^1.4.3"