Merge branch 'release/3.5.12'
This commit is contained in:
9
.babelrc
9
.babelrc
@@ -1,10 +1,13 @@
|
|||||||
{
|
{
|
||||||
"presets": [
|
"presets": [
|
||||||
"@babel/typescript",
|
"@babel/preset-typescript",
|
||||||
"@babel/preset-env"
|
"@babel/preset-env"
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@babel/plugin-proposal-class-properties",
|
"angularjs-annotate",
|
||||||
"angularjs-annotate"
|
["@babel/plugin-transform-react-jsx", {
|
||||||
|
"pragma": "h",
|
||||||
|
"pragmaFrag": "Fragment"
|
||||||
|
}]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
4
.github/workflows/beta.yml
vendored
4
.github/workflows/beta.yml
vendored
@@ -17,10 +17,10 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: yarn install --pure-lockfile
|
||||||
|
|
||||||
- name: Typescript
|
- name: Typescript
|
||||||
run: npm run tsc
|
run: yarn tsc
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/dev.yml
vendored
4
.github/workflows/dev.yml
vendored
@@ -17,10 +17,10 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: yarn install --pure-lockfile
|
||||||
|
|
||||||
- name: Typescript
|
- name: Typescript
|
||||||
run: npm run tsc
|
run: yarn tsc
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/pr.yml
vendored
4
.github/workflows/pr.yml
vendored
@@ -13,6 +13,6 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: yarn install --pure-lockfile
|
||||||
- name: Typescript
|
- name: Typescript
|
||||||
run: npm run tsc
|
run: yarn tsc
|
||||||
|
|||||||
4
.github/workflows/prod.yml
vendored
4
.github/workflows/prod.yml
vendored
@@ -18,10 +18,10 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: yarn install --pure-lockfile
|
||||||
|
|
||||||
- name: Typescript
|
- name: Typescript
|
||||||
run: npm run tsc
|
run: yarn tsc
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -42,3 +42,7 @@ dump.rdb
|
|||||||
/dist/stylesheets
|
/dist/stylesheets
|
||||||
/dist/fonts
|
/dist/fonts
|
||||||
/dist/@types
|
/dist/@types
|
||||||
|
|
||||||
|
# Yarn
|
||||||
|
yarn-error.log
|
||||||
|
package-lock.json
|
||||||
|
|||||||
11
Dockerfile
11
Dockerfile
@@ -1,4 +1,4 @@
|
|||||||
FROM ruby:2.7.1-alpine
|
FROM ruby:2.7.1-alpine3.12
|
||||||
|
|
||||||
ARG UID=1000
|
ARG UID=1000
|
||||||
ARG GID=1000
|
ARG GID=1000
|
||||||
@@ -7,10 +7,11 @@ RUN addgroup -S webapp -g $GID && adduser -D -S webapp -G webapp -u $UID
|
|||||||
|
|
||||||
RUN apk add --update --no-cache \
|
RUN apk add --update --no-cache \
|
||||||
alpine-sdk \
|
alpine-sdk \
|
||||||
nodejs \
|
nodejs-current \
|
||||||
python2 \
|
python2 \
|
||||||
git \
|
git \
|
||||||
nodejs-npm \
|
nodejs-npm \
|
||||||
|
yarn \
|
||||||
tzdata
|
tzdata
|
||||||
|
|
||||||
WORKDIR /app/
|
WORKDIR /app/
|
||||||
@@ -19,17 +20,17 @@ RUN chown -R $UID:$GID .
|
|||||||
|
|
||||||
USER webapp
|
USER webapp
|
||||||
|
|
||||||
COPY --chown=$UID:$GID package.json package-lock.json Gemfile Gemfile.lock /app/
|
COPY --chown=$UID:$GID package.json yarn.lock Gemfile Gemfile.lock /app/
|
||||||
|
|
||||||
COPY --chown=$UID:$GID vendor /app/vendor
|
COPY --chown=$UID:$GID vendor /app/vendor
|
||||||
|
|
||||||
RUN npm ci
|
RUN yarn install --pure-lockfile
|
||||||
|
|
||||||
RUN gem install bundler && bundle install
|
RUN gem install bundler && bundle install
|
||||||
|
|
||||||
COPY --chown=$UID:$GID . /app/
|
COPY --chown=$UID:$GID . /app/
|
||||||
|
|
||||||
RUN npm run bundle
|
RUN yarn bundle
|
||||||
|
|
||||||
RUN bundle exec rails assets:precompile
|
RUN bundle exec rails assets:precompile
|
||||||
|
|
||||||
|
|||||||
@@ -88,9 +88,10 @@ This repo contains the core code used in the web app, as well as the Electron-ba
|
|||||||
|
|
||||||
**Instructions:**
|
**Instructions:**
|
||||||
|
|
||||||
|
1. Ensure you have [Yarn](https://classic.yarnpkg.com) installed
|
||||||
1. Clone the repo
|
1. Clone the repo
|
||||||
1. `npm run setup`
|
1. `yarn setup`
|
||||||
1. `npm start`
|
1. `yarn start`
|
||||||
|
|
||||||
Then open your browser to `http://localhost:3001`.
|
Then open your browser to `http://localhost:3001`.
|
||||||
|
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ import { trusted } from './filters';
|
|||||||
import { isDev } from './utils';
|
import { isDev } from './utils';
|
||||||
import { BrowserBridge } from './services/browserBridge';
|
import { BrowserBridge } from './services/browserBridge';
|
||||||
import { startErrorReporting } from './services/errorReporting';
|
import { startErrorReporting } from './services/errorReporting';
|
||||||
import { alertDialog } from './services/alertService';
|
|
||||||
import { StartApplication } from './startApplication';
|
import { StartApplication } from './startApplication';
|
||||||
import { Bridge } from './services/bridge';
|
import { Bridge } from './services/bridge';
|
||||||
|
import { SessionsModalDirective } from './directives/views/sessionsModal';
|
||||||
|
|
||||||
const startApplication: StartApplication = async function startApplication(
|
const startApplication: StartApplication = async function startApplication(
|
||||||
defaultSyncServerHost: string,
|
defaultSyncServerHost: string,
|
||||||
@@ -122,7 +122,8 @@ const startApplication: StartApplication = async function startApplication(
|
|||||||
)
|
)
|
||||||
.directive('revisionPreviewModal', () => new RevisionPreviewModal())
|
.directive('revisionPreviewModal', () => new RevisionPreviewModal())
|
||||||
.directive('historyMenu', () => new HistoryMenu())
|
.directive('historyMenu', () => new HistoryMenu())
|
||||||
.directive('syncResolutionMenu', () => new SyncResolutionMenu());
|
.directive('syncResolutionMenu', () => new SyncResolutionMenu())
|
||||||
|
.directive('sessionsModal', SessionsModalDirective);
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
angular.module('app').filter('trusted', ['$sce', trusted]);
|
angular.module('app').filter('trusted', ['$sce', trusted]);
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ type AccountMenuState = {
|
|||||||
syncInProgress: boolean;
|
syncInProgress: boolean;
|
||||||
syncError: string;
|
syncError: string;
|
||||||
syncPercentage: string;
|
syncPercentage: string;
|
||||||
|
showSessions: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> {
|
class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> {
|
||||||
@@ -101,6 +102,7 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> {
|
|||||||
mutable: {},
|
mutable: {},
|
||||||
showBetaWarning: false,
|
showBetaWarning: false,
|
||||||
errorReportingEnabled: !storage.get(StorageKey.DisableErrorReporting),
|
errorReportingEnabled: !storage.get(StorageKey.DisableErrorReporting),
|
||||||
|
showSessions: false,
|
||||||
} as AccountMenuState;
|
} as AccountMenuState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,8 +132,12 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
async $onInit() {
|
||||||
super.$onInit();
|
super.$onInit();
|
||||||
|
this.setState({
|
||||||
|
showSessions: this.appState.enableUnfinishedFeatures && await this.application.userCanManageSessions()
|
||||||
|
});
|
||||||
|
|
||||||
const sync = this.appState.sync;
|
const sync = this.appState.sync;
|
||||||
this.removeSyncObserver = autorun(() => {
|
this.removeSyncObserver = autorun(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -320,6 +326,11 @@ class AccountMenuCtrl extends PureViewCtrl<{}, AccountMenuState> {
|
|||||||
this.application!.presentPasswordWizard(PasswordWizardType.ChangePassword);
|
this.application!.presentPasswordWizard(PasswordWizardType.ChangePassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openSessionsModal() {
|
||||||
|
this.close();
|
||||||
|
this.appState.openSessionsModal();
|
||||||
|
}
|
||||||
|
|
||||||
async openPrivilegesModal() {
|
async openPrivilegesModal() {
|
||||||
const run = () => {
|
const run = () => {
|
||||||
this.application!.presentPrivilegesManagementModal();
|
this.application!.presentPrivilegesManagementModal();
|
||||||
|
|||||||
252
app/assets/javascripts/directives/views/sessionsModal.tsx
Normal file
252
app/assets/javascripts/directives/views/sessionsModal.tsx
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
import { AppState } from '@/ui_models/app_state';
|
||||||
|
import { PureViewCtrl } from '@/views';
|
||||||
|
import { SNApplication, RemoteSession, UuidString } from '@standardnotes/snjs';
|
||||||
|
import { autorun, IAutorunOptions, IReactionPublic } from 'mobx';
|
||||||
|
import { render, FunctionComponent } from 'preact';
|
||||||
|
import { useState, useEffect, useRef } from 'preact/hooks';
|
||||||
|
import { Dialog } from '@reach/dialog';
|
||||||
|
import { Alert } from '@reach/alert';
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogLabel,
|
||||||
|
} from '@reach/alert-dialog';
|
||||||
|
|
||||||
|
function useAutorun(view: (r: IReactionPublic) => any, opts?: IAutorunOptions) {
|
||||||
|
useEffect(() => autorun(view, opts), []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useSessions(
|
||||||
|
application: SNApplication
|
||||||
|
): [
|
||||||
|
RemoteSession[],
|
||||||
|
() => void,
|
||||||
|
boolean,
|
||||||
|
(uuid: UuidString) => Promise<void>,
|
||||||
|
string
|
||||||
|
] {
|
||||||
|
const [sessions, setSessions] = useState<RemoteSession[]>([]);
|
||||||
|
const [lastRefreshDate, setLastRefreshDate] = useState(Date.now());
|
||||||
|
const [refreshing, setRefreshing] = useState(true);
|
||||||
|
const [errorMessage, setErrorMessage] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
setRefreshing(true);
|
||||||
|
const response = await application.getSessions();
|
||||||
|
if ('error' in response) {
|
||||||
|
if (response.error?.message) {
|
||||||
|
setErrorMessage(response.error.message);
|
||||||
|
} else {
|
||||||
|
setErrorMessage('An unknown error occured while loading sessions.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const sessions = response as RemoteSession[];
|
||||||
|
setSessions(sessions);
|
||||||
|
setErrorMessage('');
|
||||||
|
}
|
||||||
|
setRefreshing(false);
|
||||||
|
})();
|
||||||
|
}, [lastRefreshDate]);
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
setLastRefreshDate(Date.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function revokeSession(uuid: UuidString) {
|
||||||
|
let sessionsBeforeRevoke = sessions;
|
||||||
|
setSessions(sessions.filter((session) => session.uuid !== uuid));
|
||||||
|
const response = await application.revokeSession(uuid);
|
||||||
|
if ('error' in response) {
|
||||||
|
if (response.error?.message) {
|
||||||
|
setErrorMessage(response.error?.message);
|
||||||
|
} else {
|
||||||
|
setErrorMessage('An unknown error occured while revoking the session.');
|
||||||
|
}
|
||||||
|
setSessions(sessionsBeforeRevoke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [sessions, refresh, refreshing, revokeSession, errorMessage];
|
||||||
|
}
|
||||||
|
|
||||||
|
const SessionsModal: FunctionComponent<{
|
||||||
|
appState: AppState;
|
||||||
|
application: SNApplication;
|
||||||
|
}> = ({ appState, application }) => {
|
||||||
|
const close = () => appState.closeSessionsModal();
|
||||||
|
|
||||||
|
const [
|
||||||
|
sessions,
|
||||||
|
refresh,
|
||||||
|
refreshing,
|
||||||
|
revokeSession,
|
||||||
|
errorMessage,
|
||||||
|
] = useSessions(application);
|
||||||
|
|
||||||
|
const [revokingSessionUuid, setRevokingSessionUuid] = useState('');
|
||||||
|
const closeRevokeSessionAlert = () => setRevokingSessionUuid('');
|
||||||
|
const cancelRevokeRef = useRef<HTMLButtonElement>();
|
||||||
|
|
||||||
|
const formatter = new Intl.DateTimeFormat(undefined, {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
weekday: 'long',
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog onDismiss={close}>
|
||||||
|
<div className="sk-modal-content sessions-modal">
|
||||||
|
<div class="sn-component">
|
||||||
|
<div class="sk-panel">
|
||||||
|
<div class="sk-panel-header">
|
||||||
|
<div class="sk-panel-header-title">Active Sessions</div>
|
||||||
|
<div className="buttons">
|
||||||
|
<button
|
||||||
|
class="sk-a close-button info"
|
||||||
|
disabled={refreshing}
|
||||||
|
onClick={refresh}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
<button class="sk-a close-button info" onClick={close}>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sk-panel-content">
|
||||||
|
{refreshing ? (
|
||||||
|
<>
|
||||||
|
<div class="sk-spinner small info"></div>
|
||||||
|
<h2 className="sk-p sessions-modal-refreshing">
|
||||||
|
Loading sessions
|
||||||
|
</h2>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{errorMessage && (
|
||||||
|
<Alert className="sk-p bold">{errorMessage}</Alert>
|
||||||
|
)}
|
||||||
|
{sessions.length > 0 && (
|
||||||
|
<ul>
|
||||||
|
{sessions.map((session) => (
|
||||||
|
<li>
|
||||||
|
<h2>{session.device_info}</h2>
|
||||||
|
{session.current ? (
|
||||||
|
<span className="info bold">Current session</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
Signed in on{' '}
|
||||||
|
{formatter.format(session.updated_at)}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
className="sk-button danger sk-label"
|
||||||
|
onClick={() =>
|
||||||
|
setRevokingSessionUuid(session.uuid)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>Revoke</span>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
{revokingSessionUuid && (
|
||||||
|
<AlertDialog leastDestructiveRef={cancelRevokeRef}>
|
||||||
|
<div className="sk-modal-content">
|
||||||
|
<div className="sn-component">
|
||||||
|
<div className="sk-panel">
|
||||||
|
<div className="sk-panel-content">
|
||||||
|
<div className="sk-panel-section">
|
||||||
|
<AlertDialogLabel className="sk-h3 sk-panel-section-title">
|
||||||
|
Revoke this session?
|
||||||
|
</AlertDialogLabel>
|
||||||
|
<AlertDialogDescription className="sk-panel-row">
|
||||||
|
<p>
|
||||||
|
The associated app will be signed out and all data
|
||||||
|
removed from the device when it is next launched. You
|
||||||
|
can sign back in on that device at any time.
|
||||||
|
</p>
|
||||||
|
</AlertDialogDescription>
|
||||||
|
<div className="sk-panel-row">
|
||||||
|
<div className="sk-button-group">
|
||||||
|
<button
|
||||||
|
className="sk-button neutral sk-label"
|
||||||
|
ref={cancelRevokeRef}
|
||||||
|
onClick={closeRevokeSessionAlert}
|
||||||
|
>
|
||||||
|
<span>Cancel</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="sk-button danger sk-label"
|
||||||
|
onClick={() => {
|
||||||
|
closeRevokeSessionAlert();
|
||||||
|
revokeSession(revokingSessionUuid);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>Revoke</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AlertDialog>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Sessions: FunctionComponent<{
|
||||||
|
appState: AppState;
|
||||||
|
application: SNApplication;
|
||||||
|
}> = ({ appState, application }) => {
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
useAutorun(() => setShowModal(appState.isSessionsModalVisible));
|
||||||
|
|
||||||
|
if (showModal) {
|
||||||
|
return <SessionsModal application={application} appState={appState} />;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SessionsModalCtrl extends PureViewCtrl<{}, {}> {
|
||||||
|
/* @ngInject */
|
||||||
|
constructor(private $element: JQLite, $timeout: ng.ITimeoutService) {
|
||||||
|
super($timeout);
|
||||||
|
this.$element = $element;
|
||||||
|
}
|
||||||
|
$onChanges() {
|
||||||
|
render(
|
||||||
|
<Sessions appState={this.appState} application={this.application} />,
|
||||||
|
this.$element[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SessionsModalDirective() {
|
||||||
|
return {
|
||||||
|
controller: SessionsModalCtrl,
|
||||||
|
bindToController: true,
|
||||||
|
scope: {
|
||||||
|
application: '=',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
//= require_tree ./app
|
//= require_tree ./app
|
||||||
|
|
||||||
// css
|
// css
|
||||||
|
import '@reach/dialog/styles.css';
|
||||||
import 'sn-stylekit/dist/stylekit.css';
|
import 'sn-stylekit/dist/stylekit.css';
|
||||||
import '../stylesheets/index.css.scss';
|
import '../stylesheets/index.css.scss';
|
||||||
|
// import '../stylesheets/_reach-sub.scss';
|
||||||
|
|
||||||
// Vendor
|
// Vendor
|
||||||
import 'angular';
|
import 'angular';
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export class ArchiveManager {
|
|||||||
scriptTag.async = false;
|
scriptTag.async = false;
|
||||||
const headTag = document.getElementsByTagName('head')[0];
|
const headTag = document.getElementsByTagName('head')[0];
|
||||||
headTag.appendChild(scriptTag);
|
headTag.appendChild(scriptTag);
|
||||||
return new Promise((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
scriptTag.onload = () => {
|
scriptTag.onload = () => {
|
||||||
this.zip.workerScriptsPath = 'assets/zip/';
|
this.zip.workerScriptsPath = 'assets/zip/';
|
||||||
resolve();
|
resolve();
|
||||||
|
|||||||
@@ -4,6 +4,5 @@ export { DesktopManager } from './desktopManager';
|
|||||||
export { KeyboardManager } from './keyboardManager';
|
export { KeyboardManager } from './keyboardManager';
|
||||||
export { AutolockService } from './autolock_service';
|
export { AutolockService } from './autolock_service';
|
||||||
export { NativeExtManager } from './nativeExtManager';
|
export { NativeExtManager } from './nativeExtManager';
|
||||||
export { PreferencesManager } from './preferencesManager';
|
|
||||||
export { StatusManager } from './statusManager';
|
export { StatusManager } from './statusManager';
|
||||||
export { ThemeManager } from './themeManager';
|
export { ThemeManager } from './themeManager';
|
||||||
|
|||||||
@@ -2,12 +2,16 @@ export enum StorageKey {
|
|||||||
DisableErrorReporting = 'DisableErrorReporting',
|
DisableErrorReporting = 'DisableErrorReporting',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StorageValue = {
|
||||||
|
[StorageKey.DisableErrorReporting]: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const storage = {
|
export const storage = {
|
||||||
get(key: StorageKey) {
|
get<K extends StorageKey>(key: K): StorageValue[K] | null {
|
||||||
const value = localStorage.getItem(key);
|
const value = localStorage.getItem(key);
|
||||||
return value ? JSON.parse(value) : null;
|
return value ? JSON.parse(value) : null;
|
||||||
},
|
},
|
||||||
set(key: StorageKey, value: unknown) {
|
set<K extends StorageKey>(key: K, value: StorageValue[K]) {
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
localStorage.setItem(key, JSON.stringify(value));
|
||||||
},
|
},
|
||||||
remove(key: StorageKey) {
|
remove(key: StorageKey) {
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
import { WebApplication } from '@/ui_models/application';
|
|
||||||
import {
|
|
||||||
SNPredicate,
|
|
||||||
ContentType,
|
|
||||||
ApplicationService,
|
|
||||||
SNUserPrefs,
|
|
||||||
WebPrefKey,
|
|
||||||
UserPrefsMutator,
|
|
||||||
FillItemContent,
|
|
||||||
ApplicationEvent,
|
|
||||||
} from '@standardnotes/snjs';
|
|
||||||
|
|
||||||
export class PreferencesManager extends ApplicationService {
|
|
||||||
private userPreferences!: SNUserPrefs;
|
|
||||||
private loadingPrefs = false;
|
|
||||||
private unubscribeStreamItems?: () => void;
|
|
||||||
private needsSingletonReload = true;
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
async onAppLaunch() {
|
|
||||||
super.onAppLaunch();
|
|
||||||
this.reloadSingleton();
|
|
||||||
this.streamPreferences();
|
|
||||||
}
|
|
||||||
|
|
||||||
async onAppEvent(event: ApplicationEvent) {
|
|
||||||
super.onAppEvent(event);
|
|
||||||
if (event === ApplicationEvent.CompletedFullSync) {
|
|
||||||
this.reloadSingleton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit() {
|
|
||||||
this.unubscribeStreamItems?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
get webApplication() {
|
|
||||||
return this.application as WebApplication;
|
|
||||||
}
|
|
||||||
|
|
||||||
streamPreferences() {
|
|
||||||
this.unubscribeStreamItems = this.application!.streamItems(
|
|
||||||
ContentType.UserPrefs,
|
|
||||||
() => {
|
|
||||||
this.needsSingletonReload = true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async reloadSingleton() {
|
|
||||||
if (this.loadingPrefs || !this.needsSingletonReload) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.loadingPrefs = true;
|
|
||||||
const contentType = ContentType.UserPrefs;
|
|
||||||
const predicate = new SNPredicate('content_type', '=', contentType);
|
|
||||||
const previousRef = this.userPreferences;
|
|
||||||
this.userPreferences = (await this.application!.singletonManager!.findOrCreateSingleton(
|
|
||||||
predicate,
|
|
||||||
contentType,
|
|
||||||
FillItemContent({})
|
|
||||||
)) as SNUserPrefs;
|
|
||||||
this.loadingPrefs = false;
|
|
||||||
this.needsSingletonReload = false;
|
|
||||||
if (
|
|
||||||
previousRef?.uuid !== this.userPreferences.uuid ||
|
|
||||||
this.userPreferences.lastSyncBegan?.getTime() !==
|
|
||||||
previousRef?.lastSyncBegan?.getTime()
|
|
||||||
) {
|
|
||||||
this.webApplication
|
|
||||||
.getAppState()
|
|
||||||
.setUserPreferences(this.userPreferences);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
syncUserPreferences() {
|
|
||||||
if (this.userPreferences) {
|
|
||||||
this.application!.saveItem(this.userPreferences.uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getValue(key: WebPrefKey, defaultValue?: any) {
|
|
||||||
if (!this.userPreferences) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
const value = this.userPreferences.getPref(key);
|
|
||||||
return value !== undefined && value !== null ? value : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setUserPrefValue(key: WebPrefKey, value: any, sync = false) {
|
|
||||||
await this.application!.changeItem(this.userPreferences.uuid, (m) => {
|
|
||||||
const mutator = m as UserPrefsMutator;
|
|
||||||
mutator.setWebPref(key, value);
|
|
||||||
});
|
|
||||||
if (sync) {
|
|
||||||
this.syncUserPreferences();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"target": "ES2019",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
@@ -12,6 +12,8 @@
|
|||||||
"newLine": "lf",
|
"newLine": "lf",
|
||||||
"declarationDir": "../../../dist/@types",
|
"declarationDir": "../../../dist/@types",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "preact",
|
||||||
"paths": {
|
"paths": {
|
||||||
"%/*": ["../templates/*"],
|
"%/*": ["../templates/*"],
|
||||||
"@/*": ["./*"],
|
"@/*": ["./*"],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isDesktopApplication } from '@/utils';
|
import { isDesktopApplication, isDev } from '@/utils';
|
||||||
import pull from 'lodash/pull';
|
import pull from 'lodash/pull';
|
||||||
import {
|
import {
|
||||||
ProtectedAction,
|
ProtectedAction,
|
||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
PayloadSource,
|
PayloadSource,
|
||||||
DeinitSource,
|
DeinitSource,
|
||||||
UuidString,
|
UuidString,
|
||||||
SyncOpStatus
|
SyncOpStatus,
|
||||||
} from '@standardnotes/snjs';
|
} from '@standardnotes/snjs';
|
||||||
import { WebApplication } from '@/ui_models/application';
|
import { WebApplication } from '@/ui_models/application';
|
||||||
import { Editor } from '@/ui_models/editor';
|
import { Editor } from '@/ui_models/editor';
|
||||||
@@ -19,23 +19,22 @@ import { action, makeObservable, observable } from 'mobx';
|
|||||||
import { Bridge } from '@/services/bridge';
|
import { Bridge } from '@/services/bridge';
|
||||||
|
|
||||||
export enum AppStateEvent {
|
export enum AppStateEvent {
|
||||||
TagChanged = 1,
|
TagChanged,
|
||||||
ActiveEditorChanged = 2,
|
ActiveEditorChanged,
|
||||||
PreferencesChanged = 3,
|
PanelResized,
|
||||||
PanelResized = 4,
|
EditorFocused,
|
||||||
EditorFocused = 5,
|
BeganBackupDownload,
|
||||||
BeganBackupDownload = 6,
|
EndedBackupDownload,
|
||||||
EndedBackupDownload = 7,
|
WindowDidFocus,
|
||||||
WindowDidFocus = 9,
|
WindowDidBlur,
|
||||||
WindowDidBlur = 10,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export enum EventSource {
|
export enum EventSource {
|
||||||
UserInteraction = 1,
|
UserInteraction,
|
||||||
Script = 2
|
Script,
|
||||||
};
|
}
|
||||||
|
|
||||||
type ObserverCallback = (event: AppStateEvent, data?: any) => Promise<void>
|
type ObserverCallback = (event: AppStateEvent, data?: any) => Promise<void>;
|
||||||
|
|
||||||
const SHOW_BETA_WARNING_KEY = 'show_beta_warning';
|
const SHOW_BETA_WARNING_KEY = 'show_beta_warning';
|
||||||
|
|
||||||
@@ -61,8 +60,8 @@ class ActionsMenuState {
|
|||||||
|
|
||||||
export class SyncState {
|
export class SyncState {
|
||||||
inProgress = false;
|
inProgress = false;
|
||||||
errorMessage?: string;
|
errorMessage?: string = undefined;
|
||||||
humanReadablePercentage?: string;
|
humanReadablePercentage?: string = undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
@@ -77,7 +76,8 @@ export class SyncState {
|
|||||||
this.errorMessage = status.error?.message;
|
this.errorMessage = status.error?.message;
|
||||||
this.inProgress = status.syncInProgress;
|
this.inProgress = status.syncInProgress;
|
||||||
const stats = status.getStats();
|
const stats = status.getStats();
|
||||||
const completionPercentage = stats.uploadCompletionCount === 0
|
const completionPercentage =
|
||||||
|
stats.uploadCompletionCount === 0
|
||||||
? 0
|
? 0
|
||||||
: stats.uploadCompletionCount / stats.uploadTotalCount;
|
: stats.uploadCompletionCount / stats.uploadTotalCount;
|
||||||
|
|
||||||
@@ -93,6 +93,9 @@ export class SyncState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AppState {
|
export class AppState {
|
||||||
|
readonly enableUnfinishedFeatures =
|
||||||
|
isDev || location.host.includes('app-dev.standardnotes.org');
|
||||||
|
|
||||||
$rootScope: ng.IRootScopeService;
|
$rootScope: ng.IRootScopeService;
|
||||||
$timeout: ng.ITimeoutService;
|
$timeout: ng.ITimeoutService;
|
||||||
application: WebApplication;
|
application: WebApplication;
|
||||||
@@ -103,36 +106,40 @@ export class AppState {
|
|||||||
rootScopeCleanup2: any;
|
rootScopeCleanup2: any;
|
||||||
onVisibilityChange: any;
|
onVisibilityChange: any;
|
||||||
selectedTag?: SNTag;
|
selectedTag?: SNTag;
|
||||||
userPreferences?: SNUserPrefs;
|
|
||||||
multiEditorEnabled = false;
|
multiEditorEnabled = false;
|
||||||
showBetaWarning = false;
|
showBetaWarning = false;
|
||||||
readonly actionsMenu = new ActionsMenuState();
|
readonly actionsMenu = new ActionsMenuState();
|
||||||
readonly sync = new SyncState();
|
readonly sync = new SyncState();
|
||||||
|
isSessionsModalVisible = false;
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
$rootScope: ng.IRootScopeService,
|
$rootScope: ng.IRootScopeService,
|
||||||
$timeout: ng.ITimeoutService,
|
$timeout: ng.ITimeoutService,
|
||||||
application: WebApplication,
|
application: WebApplication,
|
||||||
private bridge: Bridge,
|
private bridge: Bridge
|
||||||
) {
|
) {
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
this.application = application;
|
this.application = application;
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
showBetaWarning: observable,
|
showBetaWarning: observable,
|
||||||
|
isSessionsModalVisible: observable,
|
||||||
|
|
||||||
enableBetaWarning: action,
|
enableBetaWarning: action,
|
||||||
disableBetaWarning: action,
|
disableBetaWarning: action,
|
||||||
|
openSessionsModal: action,
|
||||||
|
closeSessionsModal: action,
|
||||||
});
|
});
|
||||||
this.addAppEventObserver();
|
this.addAppEventObserver();
|
||||||
this.streamNotesAndTags();
|
this.streamNotesAndTags();
|
||||||
this.onVisibilityChange = () => {
|
this.onVisibilityChange = () => {
|
||||||
const visible = document.visibilityState === "visible";
|
const visible = document.visibilityState === 'visible';
|
||||||
const event = visible
|
const event = visible
|
||||||
? AppStateEvent.WindowDidFocus
|
? AppStateEvent.WindowDidFocus
|
||||||
: AppStateEvent.WindowDidBlur;
|
: AppStateEvent.WindowDidBlur;
|
||||||
this.notifyEvent(event);
|
this.notifyEvent(event);
|
||||||
}
|
};
|
||||||
this.registerVisibilityObservers();
|
this.registerVisibilityObservers();
|
||||||
this.determineBetaWarningValue();
|
this.determineBetaWarningValue();
|
||||||
}
|
}
|
||||||
@@ -155,6 +162,14 @@ export class AppState {
|
|||||||
this.onVisibilityChange = undefined;
|
this.onVisibilityChange = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openSessionsModal() {
|
||||||
|
this.isSessionsModalVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSessionsModal() {
|
||||||
|
this.isSessionsModalVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
disableBetaWarning() {
|
disableBetaWarning() {
|
||||||
this.showBetaWarning = false;
|
this.showBetaWarning = false;
|
||||||
localStorage.setItem(SHOW_BETA_WARNING_KEY, 'false');
|
localStorage.setItem(SHOW_BETA_WARNING_KEY, 'false');
|
||||||
@@ -190,10 +205,10 @@ export class AppState {
|
|||||||
async createEditor(title?: string) {
|
async createEditor(title?: string) {
|
||||||
const activeEditor = this.getActiveEditor();
|
const activeEditor = this.getActiveEditor();
|
||||||
const activeTagUuid = this.selectedTag
|
const activeTagUuid = this.selectedTag
|
||||||
? this.selectedTag.isSmartTag()
|
? this.selectedTag.isSmartTag()
|
||||||
? undefined
|
? undefined
|
||||||
: this.selectedTag.uuid
|
: this.selectedTag.uuid
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (!activeEditor || this.multiEditorEnabled) {
|
if (!activeEditor || this.multiEditorEnabled) {
|
||||||
this.application.editorGroup.createEditor(
|
this.application.editorGroup.createEditor(
|
||||||
@@ -218,10 +233,13 @@ export class AppState {
|
|||||||
}
|
}
|
||||||
await this.notifyEvent(AppStateEvent.ActiveEditorChanged);
|
await this.notifyEvent(AppStateEvent.ActiveEditorChanged);
|
||||||
};
|
};
|
||||||
if (note && note.safeContent.protected &&
|
if (
|
||||||
await this.application.privilegesService!.actionRequiresPrivilege(
|
note &&
|
||||||
|
note.safeContent.protected &&
|
||||||
|
(await this.application.privilegesService!.actionRequiresPrivilege(
|
||||||
ProtectedAction.ViewProtectedNotes
|
ProtectedAction.ViewProtectedNotes
|
||||||
)) {
|
))
|
||||||
|
) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
this.application.presentPrivilegesModal(
|
this.application.presentPrivilegesModal(
|
||||||
ProtectedAction.ViewProtectedNotes,
|
ProtectedAction.ViewProtectedNotes,
|
||||||
@@ -269,8 +287,8 @@ export class AppState {
|
|||||||
async (items, source) => {
|
async (items, source) => {
|
||||||
/** Close any editors for deleted/trashed/archived notes */
|
/** Close any editors for deleted/trashed/archived notes */
|
||||||
if (source === PayloadSource.PreSyncSave) {
|
if (source === PayloadSource.PreSyncSave) {
|
||||||
const notes = items.filter((candidate) =>
|
const notes = items.filter(
|
||||||
candidate.content_type === ContentType.Note
|
(candidate) => candidate.content_type === ContentType.Note
|
||||||
) as SNNote[];
|
) as SNNote[];
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
const editor = this.editorForNote(note);
|
const editor = this.editorForNote(note);
|
||||||
@@ -287,7 +305,9 @@ export class AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.selectedTag) {
|
if (this.selectedTag) {
|
||||||
const matchingTag = items.find((candidate) => candidate.uuid === this.selectedTag!.uuid);
|
const matchingTag = items.find(
|
||||||
|
(candidate) => candidate.uuid === this.selectedTag!.uuid
|
||||||
|
);
|
||||||
if (matchingTag) {
|
if (matchingTag) {
|
||||||
this.selectedTag = matchingTag as SNTag;
|
this.selectedTag = matchingTag as SNTag;
|
||||||
}
|
}
|
||||||
@@ -321,9 +341,12 @@ export class AppState {
|
|||||||
this.rootScopeCleanup1 = this.$rootScope.$on('window-lost-focus', () => {
|
this.rootScopeCleanup1 = this.$rootScope.$on('window-lost-focus', () => {
|
||||||
this.notifyEvent(AppStateEvent.WindowDidBlur);
|
this.notifyEvent(AppStateEvent.WindowDidBlur);
|
||||||
});
|
});
|
||||||
this.rootScopeCleanup2 = this.$rootScope.$on('window-gained-focus', () => {
|
this.rootScopeCleanup2 = this.$rootScope.$on(
|
||||||
this.notifyEvent(AppStateEvent.WindowDidFocus);
|
'window-gained-focus',
|
||||||
});
|
() => {
|
||||||
|
this.notifyEvent(AppStateEvent.WindowDidFocus);
|
||||||
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
/* Tab visibility listener, web only */
|
/* Tab visibility listener, web only */
|
||||||
document.addEventListener('visibilitychange', this.onVisibilityChange);
|
document.addEventListener('visibilitychange', this.onVisibilityChange);
|
||||||
@@ -343,7 +366,7 @@ export class AppState {
|
|||||||
* Timeout is particullary important so we can give all initial
|
* Timeout is particullary important so we can give all initial
|
||||||
* controllers a chance to construct before propogting any events *
|
* controllers a chance to construct before propogting any events *
|
||||||
*/
|
*/
|
||||||
return new Promise((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
this.$timeout(async () => {
|
this.$timeout(async () => {
|
||||||
for (const callback of this.observers) {
|
for (const callback of this.observers) {
|
||||||
await callback(eventName, data);
|
await callback(eventName, data);
|
||||||
@@ -359,71 +382,39 @@ export class AppState {
|
|||||||
}
|
}
|
||||||
const previousTag = this.selectedTag;
|
const previousTag = this.selectedTag;
|
||||||
this.selectedTag = tag;
|
this.selectedTag = tag;
|
||||||
this.notifyEvent(
|
this.notifyEvent(AppStateEvent.TagChanged, {
|
||||||
AppStateEvent.TagChanged,
|
tag: tag,
|
||||||
{
|
previousTag: previousTag,
|
||||||
tag: tag,
|
});
|
||||||
previousTag: previousTag
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the tags that are referncing this note */
|
/** Returns the tags that are referncing this note */
|
||||||
public getNoteTags(note: SNNote) {
|
public getNoteTags(note: SNNote) {
|
||||||
return this.application.referencingForItem(note).filter((ref) => {
|
return this.application.referencingForItem(note).filter((ref) => {
|
||||||
return ref.content_type === ContentType.Tag;
|
return ref.content_type === ContentType.Tag;
|
||||||
}) as SNTag[]
|
}) as SNTag[];
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the notes this tag references */
|
|
||||||
public getTagNotes(tag: SNTag) {
|
|
||||||
if (tag.isSmartTag()) {
|
|
||||||
return this.application.notesMatchingSmartTag(tag as SNSmartTag);
|
|
||||||
} else {
|
|
||||||
return this.application.referencesForItem(tag).filter((ref) => {
|
|
||||||
return ref.content_type === ContentType.Note;
|
|
||||||
}) as SNNote[]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSelectedTag() {
|
public getSelectedTag() {
|
||||||
return this.selectedTag;
|
return this.selectedTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserPreferences(preferences: SNUserPrefs) {
|
|
||||||
this.userPreferences = preferences;
|
|
||||||
this.notifyEvent(
|
|
||||||
AppStateEvent.PreferencesChanged
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
panelDidResize(name: string, collapsed: boolean) {
|
panelDidResize(name: string, collapsed: boolean) {
|
||||||
this.notifyEvent(
|
this.notifyEvent(AppStateEvent.PanelResized, {
|
||||||
AppStateEvent.PanelResized,
|
panel: name,
|
||||||
{
|
collapsed: collapsed,
|
||||||
panel: name,
|
});
|
||||||
collapsed: collapsed
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
editorDidFocus(eventSource: EventSource) {
|
editorDidFocus(eventSource: EventSource) {
|
||||||
this.notifyEvent(
|
this.notifyEvent(AppStateEvent.EditorFocused, { eventSource: eventSource });
|
||||||
AppStateEvent.EditorFocused,
|
|
||||||
{ eventSource: eventSource }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
beganBackupDownload() {
|
beganBackupDownload() {
|
||||||
this.notifyEvent(
|
this.notifyEvent(AppStateEvent.BeganBackupDownload);
|
||||||
AppStateEvent.BeganBackupDownload
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
endedBackupDownload(success: boolean) {
|
endedBackupDownload(success: boolean) {
|
||||||
this.notifyEvent(
|
this.notifyEvent(AppStateEvent.EndedBackupDownload, { success: success });
|
||||||
AppStateEvent.EndedBackupDownload,
|
|
||||||
{ success: success }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
NativeExtManager,
|
NativeExtManager,
|
||||||
StatusManager,
|
StatusManager,
|
||||||
ThemeManager,
|
ThemeManager,
|
||||||
PreferencesManager,
|
|
||||||
KeyboardManager
|
KeyboardManager
|
||||||
} from '@/services';
|
} from '@/services';
|
||||||
import { AppState } from '@/ui_models/app_state';
|
import { AppState } from '@/ui_models/app_state';
|
||||||
@@ -38,7 +37,6 @@ type WebServices = {
|
|||||||
nativeExtService: NativeExtManager
|
nativeExtService: NativeExtManager
|
||||||
statusManager: StatusManager
|
statusManager: StatusManager
|
||||||
themeService: ThemeManager
|
themeService: ThemeManager
|
||||||
prefsService: PreferencesManager
|
|
||||||
keyboardService: KeyboardManager
|
keyboardService: KeyboardManager
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,10 +139,6 @@ export class WebApplication extends SNApplication {
|
|||||||
return this.webServices.themeService;
|
return this.webServices.themeService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPrefsService() {
|
|
||||||
return this.webServices.prefsService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getKeyboardService() {
|
public getKeyboardService() {
|
||||||
return this.webServices.keyboardService;
|
return this.webServices.keyboardService;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
KeyboardManager,
|
KeyboardManager,
|
||||||
AutolockService,
|
AutolockService,
|
||||||
NativeExtManager,
|
NativeExtManager,
|
||||||
PreferencesManager,
|
|
||||||
StatusManager,
|
StatusManager,
|
||||||
ThemeManager
|
ThemeManager
|
||||||
} from '@/services';
|
} from '@/services';
|
||||||
@@ -82,9 +81,6 @@ export class ApplicationGroup extends SNApplicationGroup {
|
|||||||
const nativeExtService = new NativeExtManager(
|
const nativeExtService = new NativeExtManager(
|
||||||
application
|
application
|
||||||
);
|
);
|
||||||
const prefsService = new PreferencesManager(
|
|
||||||
application
|
|
||||||
);
|
|
||||||
const statusService = new StatusManager();
|
const statusService = new StatusManager();
|
||||||
const themeService = new ThemeManager(
|
const themeService = new ThemeManager(
|
||||||
application,
|
application,
|
||||||
@@ -96,7 +92,6 @@ export class ApplicationGroup extends SNApplicationGroup {
|
|||||||
keyboardService,
|
keyboardService,
|
||||||
autolockService,
|
autolockService,
|
||||||
nativeExtService,
|
nativeExtService,
|
||||||
prefsService,
|
|
||||||
statusManager: statusService,
|
statusManager: statusService,
|
||||||
themeService
|
themeService
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
|
|||||||
if (!this.$timeout) {
|
if (!this.$timeout) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return new Promise((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
this.stateTimeout = this.$timeout(() => {
|
this.stateTimeout = this.$timeout(() => {
|
||||||
/**
|
/**
|
||||||
* State changes must be *inside* the timeout block for them to be affected in the UI
|
* State changes must be *inside* the timeout block for them to be affected in the UI
|
||||||
|
|||||||
@@ -24,3 +24,6 @@
|
|||||||
symbol#layers-sharp.ionicon(viewbox="0 0 512 512")
|
symbol#layers-sharp.ionicon(viewbox="0 0 512 512")
|
||||||
path(d="M480 150L256 48 32 150l224 104 224-104zM255.71 392.95l-144.81-66.2L32 362l224 102 224-102-78.69-35.3-145.6 66.25z")
|
path(d="M480 150L256 48 32 150l224 104 224-104zM255.71 392.95l-144.81-66.2L32 362l224 102 224-102-78.69-35.3-145.6 66.25z")
|
||||||
path(d="M480 256l-75.53-33.53L256.1 290.6l-148.77-68.17L32 256l224 102 224-102z")
|
path(d="M480 256l-75.53-33.53L256.1 290.6l-148.77-68.17L32 256l224 102 224-102z")
|
||||||
|
sessions-modal(
|
||||||
|
application='self.application'
|
||||||
|
)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
Uuids,
|
Uuids,
|
||||||
ComponentArea,
|
ComponentArea,
|
||||||
ComponentAction,
|
ComponentAction,
|
||||||
WebPrefKey,
|
PrefKey,
|
||||||
ComponentMutator,
|
ComponentMutator,
|
||||||
} from '@standardnotes/snjs';
|
} from '@standardnotes/snjs';
|
||||||
import find from 'lodash/find';
|
import find from 'lodash/find';
|
||||||
@@ -135,9 +135,9 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
|
|||||||
onReady: () => this.reloadPreferences()
|
onReady: () => this.reloadPreferences()
|
||||||
};
|
};
|
||||||
/** Used by .pug template */
|
/** Used by .pug template */
|
||||||
this.prefKeyMonospace = WebPrefKey.EditorMonospaceEnabled;
|
this.prefKeyMonospace = PrefKey.EditorMonospaceEnabled;
|
||||||
this.prefKeySpellcheck = WebPrefKey.EditorSpellcheck;
|
this.prefKeySpellcheck = PrefKey.EditorSpellcheck;
|
||||||
this.prefKeyMarginResizers = WebPrefKey.EditorResizersEnabled;
|
this.prefKeyMarginResizers = PrefKey.EditorResizersEnabled;
|
||||||
|
|
||||||
this.editorMenuOnSelect = this.editorMenuOnSelect.bind(this);
|
this.editorMenuOnSelect = this.editorMenuOnSelect.bind(this);
|
||||||
this.onPanelResizeFinish = this.onPanelResizeFinish.bind(this);
|
this.onPanelResizeFinish = this.onPanelResizeFinish.bind(this);
|
||||||
@@ -239,38 +239,39 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
|
|||||||
this.registerComponentHandler();
|
this.registerComponentHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
|
||||||
onAppStateEvent(eventName: AppStateEvent, data: any) {
|
|
||||||
if (eventName === AppStateEvent.PreferencesChanged) {
|
|
||||||
this.reloadPreferences();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
onAppEvent(eventName: ApplicationEvent) {
|
onAppEvent(eventName: ApplicationEvent) {
|
||||||
if (eventName === ApplicationEvent.HighLatencySync) {
|
switch (eventName) {
|
||||||
this.setState({ syncTakingTooLong: true });
|
case ApplicationEvent.PreferencesChanged:
|
||||||
} else if (eventName === ApplicationEvent.CompletedFullSync) {
|
this.reloadPreferences();
|
||||||
this.setState({ syncTakingTooLong: false });
|
break;
|
||||||
const isInErrorState = this.state.saveError;
|
case ApplicationEvent.HighLatencySync:
|
||||||
/** if we're still dirty, don't change status, a sync is likely upcoming. */
|
this.setState({ syncTakingTooLong: true });
|
||||||
if (!this.note.dirty && isInErrorState) {
|
break;
|
||||||
this.showAllChangesSavedStatus();
|
case ApplicationEvent.CompletedFullSync:
|
||||||
}
|
this.setState({ syncTakingTooLong: false });
|
||||||
} else if (eventName === ApplicationEvent.FailedSync) {
|
const isInErrorState = this.state.saveError;
|
||||||
/**
|
/** if we're still dirty, don't change status, a sync is likely upcoming. */
|
||||||
* Only show error status in editor if the note is dirty.
|
if (!this.note.dirty && isInErrorState) {
|
||||||
* Otherwise, it means the originating sync came from somewhere else
|
this.showAllChangesSavedStatus();
|
||||||
* and we don't want to display an error here.
|
}
|
||||||
*/
|
break;
|
||||||
if (this.note.dirty) {
|
case ApplicationEvent.FailedSync:
|
||||||
this.showErrorStatus();
|
/**
|
||||||
}
|
* Only show error status in editor if the note is dirty.
|
||||||
} else if (eventName === ApplicationEvent.LocalDatabaseWriteError) {
|
* Otherwise, it means the originating sync came from somewhere else
|
||||||
this.showErrorStatus({
|
* and we don't want to display an error here.
|
||||||
message: "Offline Saving Issue",
|
*/
|
||||||
desc: "Changes not saved"
|
if (this.note.dirty) {
|
||||||
});
|
this.showErrorStatus();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ApplicationEvent.LocalDatabaseWriteError:
|
||||||
|
this.showErrorStatus({
|
||||||
|
message: "Offline Saving Issue",
|
||||||
|
desc: "Changes not saved"
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,40 +892,40 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
|
|||||||
|
|
||||||
async onPanelResizeFinish(width: number, left: number, isMaxWidth: boolean) {
|
async onPanelResizeFinish(width: number, left: number, isMaxWidth: boolean) {
|
||||||
if (isMaxWidth) {
|
if (isMaxWidth) {
|
||||||
await this.application.getPrefsService().setUserPrefValue(
|
await this.application.setPreference(
|
||||||
WebPrefKey.EditorWidth,
|
PrefKey.EditorWidth,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (width !== undefined && width !== null) {
|
if (width !== undefined && width !== null) {
|
||||||
await this.application.getPrefsService().setUserPrefValue(
|
await this.application.setPreference(
|
||||||
WebPrefKey.EditorWidth,
|
PrefKey.EditorWidth,
|
||||||
width
|
width
|
||||||
);
|
);
|
||||||
this.leftPanelPuppet!.setWidth!(width);
|
this.leftPanelPuppet!.setWidth!(width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (left !== undefined && left !== null) {
|
if (left !== undefined && left !== null) {
|
||||||
await this.application.getPrefsService().setUserPrefValue(
|
await this.application.setPreference(
|
||||||
WebPrefKey.EditorLeft,
|
PrefKey.EditorLeft,
|
||||||
left
|
left
|
||||||
);
|
);
|
||||||
this.rightPanelPuppet!.setLeft!(left);
|
this.rightPanelPuppet!.setLeft!(left);
|
||||||
}
|
}
|
||||||
this.application.getPrefsService().syncUserPreferences();
|
this.application.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
async reloadPreferences() {
|
async reloadPreferences() {
|
||||||
const monospaceFont = this.application.getPrefsService().getValue(
|
const monospaceFont = this.application.getPreference(
|
||||||
WebPrefKey.EditorMonospaceEnabled,
|
PrefKey.EditorMonospaceEnabled,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
const spellcheck = this.application.getPrefsService().getValue(
|
const spellcheck = this.application.getPreference(
|
||||||
WebPrefKey.EditorSpellcheck,
|
PrefKey.EditorSpellcheck,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
const marginResizersEnabled = this.application.getPrefsService().getValue(
|
const marginResizersEnabled = this.application.getPreference(
|
||||||
WebPrefKey.EditorResizersEnabled,
|
PrefKey.EditorResizersEnabled,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
await this.setState({
|
await this.setState({
|
||||||
@@ -945,16 +946,16 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
|
|||||||
this.leftPanelPuppet?.ready &&
|
this.leftPanelPuppet?.ready &&
|
||||||
this.rightPanelPuppet?.ready
|
this.rightPanelPuppet?.ready
|
||||||
) {
|
) {
|
||||||
const width = this.application.getPrefsService().getValue(
|
const width = this.application.getPreference(
|
||||||
WebPrefKey.EditorWidth,
|
PrefKey.EditorWidth,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
if (width != null) {
|
if (width != null) {
|
||||||
this.leftPanelPuppet!.setWidth!(width);
|
this.leftPanelPuppet!.setWidth!(width);
|
||||||
this.rightPanelPuppet!.setWidth!(width);
|
this.rightPanelPuppet!.setWidth!(width);
|
||||||
}
|
}
|
||||||
const left = this.application.getPrefsService().getValue(
|
const left = this.application.getPreference(
|
||||||
WebPrefKey.EditorLeft,
|
PrefKey.EditorLeft,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
if (left != null) {
|
if (left != null) {
|
||||||
@@ -982,24 +983,23 @@ class EditorViewCtrl extends PureViewCtrl<{}, EditorState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleWebPrefKey(key: WebPrefKey) {
|
async toggleWebPrefKey(key: PrefKey) {
|
||||||
const currentValue = (this.state as any)[key];
|
const currentValue = (this.state as any)[key];
|
||||||
await this.application.getPrefsService().setUserPrefValue(
|
await this.application.setPreference(
|
||||||
key,
|
key,
|
||||||
!currentValue,
|
!currentValue,
|
||||||
true
|
|
||||||
);
|
);
|
||||||
await this.setState({
|
await this.setState({
|
||||||
[key]: !currentValue
|
[key]: !currentValue
|
||||||
})
|
})
|
||||||
this.reloadFont();
|
this.reloadFont();
|
||||||
|
|
||||||
if (key === WebPrefKey.EditorSpellcheck) {
|
if (key === PrefKey.EditorSpellcheck) {
|
||||||
/** Allows textarea to reload */
|
/** Allows textarea to reload */
|
||||||
await this.setState({ textareaUnloading: true });
|
await this.setState({ textareaUnloading: true });
|
||||||
await this.setState({ textareaUnloading: false });
|
await this.setState({ textareaUnloading: false });
|
||||||
this.reloadFont();
|
this.reloadFont();
|
||||||
} else if (key === WebPrefKey.EditorResizersEnabled && this.state[key] === true) {
|
} else if (key === PrefKey.EditorResizersEnabled && this.state[key] === true) {
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
this.leftPanelPuppet!.flash!();
|
this.leftPanelPuppet!.flash!();
|
||||||
this.rightPanelPuppet!.flash!();
|
this.rightPanelPuppet!.flash!();
|
||||||
|
|||||||
@@ -1,16 +1,5 @@
|
|||||||
import { SNNote } from '@standardnotes/snjs';
|
import { SNNote } from '@standardnotes/snjs';
|
||||||
|
|
||||||
export enum NoteSortKey {
|
|
||||||
CreatedAt = 'created_at',
|
|
||||||
UserUpdatedAt = 'userModifiedDate',
|
|
||||||
Title = 'title',
|
|
||||||
|
|
||||||
/** @legacy Use UserUpdatedAt instead */
|
|
||||||
UpdatedAt = 'updated_at',
|
|
||||||
/** @legacy Use UserUpdatedAt instead */
|
|
||||||
ClientUpdatedAt = 'client_updated_at',
|
|
||||||
}
|
|
||||||
|
|
||||||
export function notePassesFilter(
|
export function notePassesFilter(
|
||||||
note: SNNote,
|
note: SNNote,
|
||||||
showArchived: boolean,
|
showArchived: boolean,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
removeFromArray,
|
removeFromArray,
|
||||||
SNNote,
|
SNNote,
|
||||||
SNTag,
|
SNTag,
|
||||||
WebPrefKey,
|
PrefKey,
|
||||||
findInArray,
|
findInArray,
|
||||||
CollectionSort,
|
CollectionSort,
|
||||||
} from '@standardnotes/snjs';
|
} from '@standardnotes/snjs';
|
||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
PANEL_NAME_NOTES
|
PANEL_NAME_NOTES
|
||||||
} from '@/views/constants';
|
} from '@/views/constants';
|
||||||
import {
|
import {
|
||||||
NoteSortKey,
|
|
||||||
notePassesFilter
|
notePassesFilter
|
||||||
} from './note_utils';
|
} from './note_utils';
|
||||||
import { UuidString } from '@standardnotes/snjs';
|
import { UuidString } from '@standardnotes/snjs';
|
||||||
@@ -37,13 +36,13 @@ type NotesState = {
|
|||||||
noteFilter: { text: string }
|
noteFilter: { text: string }
|
||||||
mutable: { showMenu: boolean }
|
mutable: { showMenu: boolean }
|
||||||
completedFullSync: boolean
|
completedFullSync: boolean
|
||||||
[WebPrefKey.TagsPanelWidth]?: number
|
[PrefKey.TagsPanelWidth]?: number
|
||||||
[WebPrefKey.NotesPanelWidth]?: number
|
[PrefKey.NotesPanelWidth]?: number
|
||||||
[WebPrefKey.EditorWidth]?: number
|
[PrefKey.EditorWidth]?: number
|
||||||
[WebPrefKey.EditorLeft]?: number
|
[PrefKey.EditorLeft]?: number
|
||||||
[WebPrefKey.EditorMonospaceEnabled]?: boolean
|
[PrefKey.EditorMonospaceEnabled]?: boolean
|
||||||
[WebPrefKey.EditorSpellcheck]?: boolean
|
[PrefKey.EditorSpellcheck]?: boolean
|
||||||
[WebPrefKey.EditorResizersEnabled]?: boolean
|
[PrefKey.EditorResizersEnabled]?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type NoteFlag = {
|
type NoteFlag = {
|
||||||
@@ -147,8 +146,6 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
|
|||||||
this.handleTagChange(this.selectedTag!);
|
this.handleTagChange(this.selectedTag!);
|
||||||
} else if (eventName === AppStateEvent.ActiveEditorChanged) {
|
} else if (eventName === AppStateEvent.ActiveEditorChanged) {
|
||||||
this.handleEditorChange();
|
this.handleEditorChange();
|
||||||
} else if (eventName === AppStateEvent.PreferencesChanged) {
|
|
||||||
this.reloadPreferences();
|
|
||||||
} else if (eventName === AppStateEvent.EditorFocused) {
|
} else if (eventName === AppStateEvent.EditorFocused) {
|
||||||
this.setShowMenuFalse();
|
this.setShowMenuFalse();
|
||||||
}
|
}
|
||||||
@@ -166,6 +163,9 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
|
|||||||
/** @override */
|
/** @override */
|
||||||
async onAppEvent(eventName: ApplicationEvent) {
|
async onAppEvent(eventName: ApplicationEvent) {
|
||||||
switch (eventName) {
|
switch (eventName) {
|
||||||
|
case ApplicationEvent.PreferencesChanged:
|
||||||
|
this.reloadPreferences();
|
||||||
|
break;
|
||||||
case ApplicationEvent.SignedIn:
|
case ApplicationEvent.SignedIn:
|
||||||
this.appState.closeAllEditors();
|
this.appState.closeAllEditors();
|
||||||
this.selectFirstNote();
|
this.selectFirstNote();
|
||||||
@@ -420,37 +420,40 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
|
|||||||
async reloadPreferences() {
|
async reloadPreferences() {
|
||||||
const viewOptions = {} as NotesState;
|
const viewOptions = {} as NotesState;
|
||||||
const prevSortValue = this.getState().sortBy;
|
const prevSortValue = this.getState().sortBy;
|
||||||
let sortBy = this.application!.getPrefsService().getValue(
|
let sortBy = this.application!.getPreference(
|
||||||
WebPrefKey.SortNotesBy,
|
PrefKey.SortNotesBy,
|
||||||
NoteSortKey.CreatedAt
|
CollectionSort.CreatedAt
|
||||||
);
|
);
|
||||||
if (sortBy === NoteSortKey.UpdatedAt || sortBy === NoteSortKey.ClientUpdatedAt) {
|
if (
|
||||||
|
sortBy === CollectionSort.UpdatedAt ||
|
||||||
|
(sortBy as string) === "client_updated_at"
|
||||||
|
) {
|
||||||
/** Use UserUpdatedAt instead */
|
/** Use UserUpdatedAt instead */
|
||||||
sortBy = NoteSortKey.UserUpdatedAt;
|
sortBy = CollectionSort.UpdatedAt;
|
||||||
}
|
}
|
||||||
viewOptions.sortBy = sortBy;
|
viewOptions.sortBy = sortBy;
|
||||||
viewOptions.sortReverse = this.application!.getPrefsService().getValue(
|
viewOptions.sortReverse = this.application!.getPreference(
|
||||||
WebPrefKey.SortNotesReverse,
|
PrefKey.SortNotesReverse,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
viewOptions.showArchived = this.application!.getPrefsService().getValue(
|
viewOptions.showArchived = this.application!.getPreference(
|
||||||
WebPrefKey.NotesShowArchived,
|
PrefKey.NotesShowArchived,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
viewOptions.hidePinned = this.application!.getPrefsService().getValue(
|
viewOptions.hidePinned = this.application!.getPreference(
|
||||||
WebPrefKey.NotesHidePinned,
|
PrefKey.NotesHidePinned,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
viewOptions.hideNotePreview = this.application!.getPrefsService().getValue(
|
viewOptions.hideNotePreview = this.application!.getPreference(
|
||||||
WebPrefKey.NotesHideNotePreview,
|
PrefKey.NotesHideNotePreview,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
viewOptions.hideDate = this.application!.getPrefsService().getValue(
|
viewOptions.hideDate = this.application!.getPreference(
|
||||||
WebPrefKey.NotesHideDate,
|
PrefKey.NotesHideDate,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
viewOptions.hideTags = this.application.getPrefsService().getValue(
|
viewOptions.hideTags = this.application.getPreference(
|
||||||
WebPrefKey.NotesHideTags,
|
PrefKey.NotesHideTags,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
const state = this.getState();
|
const state = this.getState();
|
||||||
@@ -475,8 +478,8 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reloadPanelWidth() {
|
reloadPanelWidth() {
|
||||||
const width = this.application!.getPrefsService().getValue(
|
const width = this.application!.getPreference(
|
||||||
WebPrefKey.NotesPanelWidth
|
PrefKey.NotesPanelWidth
|
||||||
);
|
);
|
||||||
if (width && this.panelPuppet!.ready) {
|
if (width && this.panelPuppet!.ready) {
|
||||||
this.panelPuppet!.setWidth!(width);
|
this.panelPuppet!.setWidth!(width);
|
||||||
@@ -495,10 +498,9 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
|
|||||||
__: boolean,
|
__: boolean,
|
||||||
isCollapsed: boolean
|
isCollapsed: boolean
|
||||||
) {
|
) {
|
||||||
this.application!.getPrefsService().setUserPrefValue(
|
this.application!.setPreference(
|
||||||
WebPrefKey.NotesPanelWidth,
|
PrefKey.NotesPanelWidth,
|
||||||
newWidth,
|
newWidth
|
||||||
true
|
|
||||||
);
|
);
|
||||||
this.application!.getAppState().panelDidResize(
|
this.application!.getAppState().panelDidResize(
|
||||||
PANEL_NAME_NOTES,
|
PANEL_NAME_NOTES,
|
||||||
@@ -541,11 +543,11 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
|
|||||||
|
|
||||||
optionsSubtitle() {
|
optionsSubtitle() {
|
||||||
let base = "";
|
let base = "";
|
||||||
if (this.getState().sortBy === NoteSortKey.CreatedAt) {
|
if (this.getState().sortBy === CollectionSort.CreatedAt) {
|
||||||
base += " Date Added";
|
base += " Date Added";
|
||||||
} else if (this.getState().sortBy === NoteSortKey.UserUpdatedAt) {
|
} else if (this.getState().sortBy === CollectionSort.UpdatedAt) {
|
||||||
base += " Date Modified";
|
base += " Date Modified";
|
||||||
} else if (this.getState().sortBy === NoteSortKey.Title) {
|
} else if (this.getState().sortBy === CollectionSort.Title) {
|
||||||
base += " Title";
|
base += " Title";
|
||||||
}
|
}
|
||||||
if (this.getState().showArchived) {
|
if (this.getState().showArchived) {
|
||||||
@@ -709,40 +711,37 @@ class NotesViewCtrl extends PureViewCtrl<{}, NotesState> {
|
|||||||
this.setShowMenuFalse();
|
this.setShowMenuFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleWebPrefKey(key: WebPrefKey) {
|
togglePrefKey(key: PrefKey) {
|
||||||
this.application!.getPrefsService().setUserPrefValue(
|
this.application!.setPreference(
|
||||||
key,
|
key,
|
||||||
!this.state[key],
|
!this.state[key]
|
||||||
true
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedSortByCreated() {
|
selectedSortByCreated() {
|
||||||
this.setSortBy(NoteSortKey.CreatedAt);
|
this.setSortBy(CollectionSort.CreatedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedSortByUpdated() {
|
selectedSortByUpdated() {
|
||||||
this.setSortBy(NoteSortKey.ClientUpdatedAt);
|
this.setSortBy(CollectionSort.UpdatedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedSortByTitle() {
|
selectedSortByTitle() {
|
||||||
this.setSortBy(NoteSortKey.Title);
|
this.setSortBy(CollectionSort.Title);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleReverseSort() {
|
toggleReverseSort() {
|
||||||
this.selectedMenuItem();
|
this.selectedMenuItem();
|
||||||
this.application!.getPrefsService().setUserPrefValue(
|
this.application!.setPreference(
|
||||||
WebPrefKey.SortNotesReverse,
|
PrefKey.SortNotesReverse,
|
||||||
!this.getState().sortReverse,
|
!this.getState().sortReverse
|
||||||
true
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSortBy(type: NoteSortKey) {
|
setSortBy(type: CollectionSort) {
|
||||||
this.application!.getPrefsService().setUserPrefValue(
|
this.application!.setPreference(
|
||||||
WebPrefKey.SortNotesBy,
|
PrefKey.SortNotesBy,
|
||||||
type,
|
type
|
||||||
true
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
SNSmartTag,
|
SNSmartTag,
|
||||||
ComponentArea,
|
ComponentArea,
|
||||||
SNComponent,
|
SNComponent,
|
||||||
WebPrefKey,
|
PrefKey,
|
||||||
UuidString,
|
UuidString,
|
||||||
TagMutator
|
TagMutator
|
||||||
} from '@standardnotes/snjs';
|
} from '@standardnotes/snjs';
|
||||||
@@ -145,10 +145,8 @@ class TagsViewCtrl extends PureViewCtrl<{}, TagState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
onAppStateEvent(eventName: AppStateEvent, data?: any) {
|
onAppStateEvent(eventName: AppStateEvent) {
|
||||||
if (eventName === AppStateEvent.PreferencesChanged) {
|
if (eventName === AppStateEvent.TagChanged) {
|
||||||
this.loadPreferences();
|
|
||||||
} else if (eventName === AppStateEvent.TagChanged) {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedTag: this.application.getAppState().getSelectedTag()
|
selectedTag: this.application.getAppState().getSelectedTag()
|
||||||
});
|
});
|
||||||
@@ -159,8 +157,13 @@ class TagsViewCtrl extends PureViewCtrl<{}, TagState> {
|
|||||||
/** @override */
|
/** @override */
|
||||||
async onAppEvent(eventName: ApplicationEvent) {
|
async onAppEvent(eventName: ApplicationEvent) {
|
||||||
super.onAppEvent(eventName);
|
super.onAppEvent(eventName);
|
||||||
if (eventName === ApplicationEvent.LocalDataIncrementalLoad) {
|
switch (eventName) {
|
||||||
this.reloadNoteCounts();
|
case ApplicationEvent.LocalDataIncrementalLoad:
|
||||||
|
this.reloadNoteCounts();
|
||||||
|
break;
|
||||||
|
case ApplicationEvent.PreferencesChanged:
|
||||||
|
this.loadPreferences();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +206,7 @@ class TagsViewCtrl extends PureViewCtrl<{}, TagState> {
|
|||||||
if (!this.panelPuppet.ready) {
|
if (!this.panelPuppet.ready) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const width = this.application.getPrefsService().getValue(WebPrefKey.TagsPanelWidth);
|
const width = this.application.getPreference(PrefKey.TagsPanelWidth);
|
||||||
if (width) {
|
if (width) {
|
||||||
this.panelPuppet.setWidth!(width);
|
this.panelPuppet.setWidth!(width);
|
||||||
if (this.panelPuppet.isCollapsed!()) {
|
if (this.panelPuppet.isCollapsed!()) {
|
||||||
@@ -221,11 +224,10 @@ class TagsViewCtrl extends PureViewCtrl<{}, TagState> {
|
|||||||
_isAtMaxWidth: boolean,
|
_isAtMaxWidth: boolean,
|
||||||
isCollapsed: boolean
|
isCollapsed: boolean
|
||||||
) => {
|
) => {
|
||||||
this.application.getPrefsService().setUserPrefValue(
|
this.application.setPreference(
|
||||||
WebPrefKey.TagsPanelWidth,
|
PrefKey.TagsPanelWidth,
|
||||||
newWidth,
|
newWidth
|
||||||
true
|
).then(() => this.application.sync());
|
||||||
);
|
|
||||||
this.application.getAppState().panelDidResize(
|
this.application.getAppState().panelDidResize(
|
||||||
PANEL_NAME_TAGS,
|
PANEL_NAME_TAGS,
|
||||||
isCollapsed
|
isCollapsed
|
||||||
|
|||||||
@@ -48,10 +48,16 @@ body {
|
|||||||
color: var(--sn-stylekit-info-contrast-color);
|
color: var(--sn-stylekit-info-contrast-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
*:focus {outline:0;}
|
h1 {
|
||||||
|
font-size: var(--sn-stylekit-font-size-h1);
|
||||||
|
}
|
||||||
|
|
||||||
button:focus {
|
h2 {
|
||||||
outline:0;
|
font-size: var(--sn-stylekit-font-size-h2);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: var(--sn-stylekit-font-size-h3);
|
||||||
}
|
}
|
||||||
|
|
||||||
input, button, select, textarea {
|
input, button, select, textarea {
|
||||||
|
|||||||
@@ -63,9 +63,16 @@
|
|||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: var(--sn-stylekit-font-size-h3);
|
font-size: var(--sn-stylekit-font-size-h3);
|
||||||
|
|
||||||
border: none;
|
border-style: solid;
|
||||||
|
border-color: transparent;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 0;
|
||||||
|
border-color: var(--sn-stylekit-info-color);
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#search-clear-button {
|
#search-clear-button {
|
||||||
|
|||||||
38
app/assets/stylesheets/_reach-sub.scss
Normal file
38
app/assets/stylesheets/_reach-sub.scss
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
[data-reach-dialog-overlay] {
|
||||||
|
z-index: $z-index-modal;
|
||||||
|
background: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: unset;
|
||||||
|
}
|
||||||
|
[data-reach-dialog-overlay]::before {
|
||||||
|
background-color: var(--sn-stylekit-contrast-background-color);
|
||||||
|
content: "";
|
||||||
|
position: fixed;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
opacity: 0.75;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-reach-dialog-content] {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
overflow: unset;
|
||||||
|
flex-basis: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-reach-dialog-content] .sk-modal-content,
|
||||||
|
[data-reach-dialog-content] .sn-component,
|
||||||
|
[data-reach-dialog-content] .sk-panel {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-reach-alert-dialog-content] {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
48
app/assets/stylesheets/_sessions-modal.scss
Normal file
48
app/assets/stylesheets/_sessions-modal.scss
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
.sessions-modal {
|
||||||
|
h2, ul, p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: var(--sn-stylekit-font-size-h2);
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
grid-column: 1 / 3;
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
grid-gap: 16px;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr max-content;
|
||||||
|
border-bottom: 1px solid var(--sn-stylekit-border-color);
|
||||||
|
padding-bottom: 16px;
|
||||||
|
grid-column-gap: 12px;
|
||||||
|
column-gap: 12px;
|
||||||
|
}
|
||||||
|
li:last-of-type {
|
||||||
|
border: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
li > * {
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
li button {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1 / span 3;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
.sn-component .sk-panel-content {
|
||||||
|
padding-bottom: 1.6rem;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
align-items: center;
|
||||||
|
grid-gap: 8px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sessions-modal-refreshing {
|
||||||
|
grid-column: 2;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
@@ -77,6 +77,12 @@ button.sk-button {
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a, .sk-a {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
color: var(--sn-stylekit-info-color);
|
color: var(--sn-stylekit-info-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.sk-a {
|
||||||
|
min-height: 24px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,3 +9,5 @@
|
|||||||
@import "lock-screen";
|
@import "lock-screen";
|
||||||
@import "stylekit-sub";
|
@import "stylekit-sub";
|
||||||
@import "ionicons";
|
@import "ionicons";
|
||||||
|
@import "reach-sub";
|
||||||
|
@import "sessions-modal";
|
||||||
|
|||||||
@@ -163,8 +163,11 @@
|
|||||||
.sk-panel-row
|
.sk-panel-row
|
||||||
a.sk-a.info.sk-panel-row.condensed(
|
a.sk-a.info.sk-panel-row.condensed(
|
||||||
ng-click="self.openPasswordWizard()"
|
ng-click="self.openPasswordWizard()"
|
||||||
ng-if="!self.state.showBetaWarning"
|
|
||||||
) Change Password
|
) Change Password
|
||||||
|
a.sk-a.info.sk-panel-row.condensed(
|
||||||
|
ng-click="self.openSessionsModal()"
|
||||||
|
ng-if="self.state.showSessions"
|
||||||
|
) Manage Sessions
|
||||||
a.sk-a.info.sk-panel-row.condensed(
|
a.sk-a.info.sk-panel-row.condensed(
|
||||||
ng-click="self.openPrivilegesModal('')",
|
ng-click="self.openPrivilegesModal('')",
|
||||||
ng-show='self.state.user'
|
ng-show='self.state.user'
|
||||||
|
|||||||
13118
package-lock.json
generated
13118
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
36
package.json
36
package.json
@@ -7,35 +7,34 @@
|
|||||||
"url": "https://github.com/standardnotes/web"
|
"url": "https://github.com/standardnotes/web"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"setup": "npm run submodules && npm install",
|
"setup": "yarn submodules && yarn install",
|
||||||
"start": "webpack-dev-server --progress --config webpack.dev.js",
|
"start": "webpack-dev-server --progress --config webpack.dev.js",
|
||||||
"watch": "webpack -w --config webpack.dev.js",
|
"watch": "webpack -w --config webpack.dev.js",
|
||||||
"watch:desktop": "webpack -w --config webpack.dev.js --env.platform='desktop'",
|
"watch:desktop": "webpack -w --config webpack.dev.js --env.platform='desktop'",
|
||||||
"bundle": "webpack --config webpack.prod.js && npm run tsc",
|
"bundle": "webpack --config webpack.prod.js && yarn tsc",
|
||||||
"bundle:desktop": "webpack --config webpack.prod.js --env.platform='desktop'",
|
"bundle:desktop": "webpack --config webpack.prod.js --env.platform='desktop'",
|
||||||
"bundle:desktop:beta": "webpack --config webpack.prod.js --env.platform='desktop' --env.public_beta='true'",
|
"bundle:desktop:beta": "webpack --config webpack.prod.js --env.platform='desktop' --env.public_beta='true'",
|
||||||
"build": "bundle install && npm ci && bundle exec rails assets:precompile && npm run bundle",
|
"build": "bundle install && yarn install --pure-lockfile && bundle exec rails assets:precompile && yarn bundle",
|
||||||
"submodules": "git submodule update --init --force",
|
"submodules": "git submodule update --init --force",
|
||||||
"lint": "eslint --fix app/assets/javascripts/**/*.js",
|
"lint": "eslint --fix app/assets/javascripts/**/*.js",
|
||||||
"tsc": "tsc --project app/assets/javascripts/tsconfig.json"
|
"tsc": "tsc --project app/assets/javascripts/tsconfig.json"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.10.5",
|
"@babel/core": "^7.12.10",
|
||||||
"@babel/core": "^7.11.1",
|
"@babel/plugin-transform-react-jsx": "^7.12.10",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
"@babel/preset-env": "^7.12.10",
|
||||||
"@babel/preset-env": "^7.11.0",
|
"@babel/preset-typescript": "^7.12.7",
|
||||||
"@babel/preset-typescript": "^7.10.4",
|
"@types/angular": "^1.8.0",
|
||||||
"@types/angular": "^1.7.0",
|
|
||||||
"@types/chai": "^4.2.11",
|
"@types/chai": "^4.2.11",
|
||||||
"@types/lodash": "^4.14.149",
|
"@types/lodash": "^4.14.149",
|
||||||
"@types/mocha": "^7.0.2",
|
"@types/mocha": "^7.0.2",
|
||||||
"@types/pug": "^2.0.4",
|
"@types/pug": "^2.0.4",
|
||||||
|
"@types/react": "^17.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.23.0",
|
"@typescript-eslint/eslint-plugin": "^2.23.0",
|
||||||
"@typescript-eslint/parser": "^2.23.0",
|
"@typescript-eslint/parser": "^2.23.0",
|
||||||
"angular": "1.8.0",
|
"angular": "^1.8.2",
|
||||||
"apply-loader": "^2.0.0",
|
"apply-loader": "^2.0.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"babel-loader": "^8.1.0",
|
|
||||||
"babel-plugin-angularjs-annotate": "^0.10.0",
|
"babel-plugin-angularjs-annotate": "^0.10.0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"connect": "^3.7.0",
|
"connect": "^3.7.0",
|
||||||
@@ -43,8 +42,6 @@
|
|||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-config-prettier": "^6.10.0",
|
"eslint-config-prettier": "^6.10.0",
|
||||||
"eslint-config-semistandard": "^15.0.0",
|
|
||||||
"eslint-config-standard": "^14.1.0",
|
|
||||||
"eslint-plugin-import": "^2.20.1",
|
"eslint-plugin-import": "^2.20.1",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-standard": "^4.0.1",
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
@@ -60,8 +57,8 @@
|
|||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^1.14.1",
|
||||||
"sn-stylekit": "2.1.0",
|
"sn-stylekit": "2.1.0",
|
||||||
"ts-loader": "^8.0.2",
|
"ts-loader": "^8.0.12",
|
||||||
"typescript": "^3.9.7",
|
"typescript": "^4.1.3",
|
||||||
"typescript-eslint": "0.0.1-alpha.0",
|
"typescript-eslint": "0.0.1-alpha.0",
|
||||||
"webpack": "^4.44.1",
|
"webpack": "^4.44.1",
|
||||||
"webpack-cli": "^3.3.12",
|
"webpack-cli": "^3.3.12",
|
||||||
@@ -70,8 +67,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bugsnag/js": "^7.5.1",
|
"@bugsnag/js": "^7.5.1",
|
||||||
|
"@reach/alert": "^0.12.1",
|
||||||
|
"@reach/alert-dialog": "^0.12.1",
|
||||||
|
"@reach/dialog": "^0.12.1",
|
||||||
"@standardnotes/sncrypto-web": "^1.2.9",
|
"@standardnotes/sncrypto-web": "^1.2.9",
|
||||||
"@standardnotes/snjs": "^2.0.16",
|
"@standardnotes/snjs": "^2.0.30",
|
||||||
"mobx": "^6.0.1"
|
"babel-loader": "^8.2.2",
|
||||||
|
"mobx": "^6.0.4",
|
||||||
|
"preact": "^10.5.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ const webpack = require('webpack');
|
|||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
||||||
module.exports = (env = {
|
module.exports = (
|
||||||
platform: 'web'
|
env = {
|
||||||
}) => ({
|
platform: 'web',
|
||||||
|
}
|
||||||
|
) => ({
|
||||||
entry: './app/assets/javascripts/index.ts',
|
entry: './app/assets/javascripts/index.ts',
|
||||||
output: {
|
output: {
|
||||||
filename: './javascripts/app.js'
|
filename: './javascripts/app.js',
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
@@ -19,28 +21,40 @@ module.exports = (env = {
|
|||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
// Options similar to the same options in webpackOptions.output
|
// Options similar to the same options in webpackOptions.output
|
||||||
filename: './stylesheets/app.css',
|
filename: './stylesheets/app.css',
|
||||||
ignoreOrder: true // Enable to remove warnings about conflicting order
|
ignoreOrder: true, // Enable to remove warnings about conflicting order
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.js'],
|
extensions: ['.ts', '.tsx', '.js'],
|
||||||
alias: {
|
alias: {
|
||||||
'%': path.resolve(__dirname, 'app/assets/templates'),
|
'%': path.resolve(__dirname, 'app/assets/templates'),
|
||||||
'@': path.resolve(__dirname, 'app/assets/javascripts'),
|
'@': path.resolve(__dirname, 'app/assets/javascripts'),
|
||||||
'@Controllers': path.resolve(__dirname, 'app/assets/javascripts/controllers'),
|
'@Controllers': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'app/assets/javascripts/controllers'
|
||||||
|
),
|
||||||
'@Views': path.resolve(__dirname, 'app/assets/javascripts/views'),
|
'@Views': path.resolve(__dirname, 'app/assets/javascripts/views'),
|
||||||
'@Services': path.resolve(__dirname, 'app/assets/javascripts/services'),
|
'@Services': path.resolve(__dirname, 'app/assets/javascripts/services'),
|
||||||
'@node_modules': path.resolve(__dirname, 'node_modules'),
|
'@node_modules': path.resolve(__dirname, 'node_modules'),
|
||||||
}
|
react: 'preact/compat',
|
||||||
|
'react-dom/test-utils': 'preact/test-utils',
|
||||||
|
'react-dom': 'preact/compat',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.(js|ts)$/,
|
test: /\.(js|tsx?)$/,
|
||||||
exclude: /(node_modules|snjs)/,
|
exclude: /(node_modules)/,
|
||||||
use: {
|
use: [
|
||||||
loader: 'babel-loader'
|
'babel-loader',
|
||||||
}
|
{
|
||||||
|
loader: 'ts-loader',
|
||||||
|
options: {
|
||||||
|
transpileOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.s?css$/,
|
test: /\.s?css$/,
|
||||||
@@ -49,12 +63,12 @@ module.exports = (env = {
|
|||||||
loader: MiniCssExtractPlugin.loader,
|
loader: MiniCssExtractPlugin.loader,
|
||||||
options: {
|
options: {
|
||||||
publicPath: '../', // The base assets directory in relation to the stylesheets
|
publicPath: '../', // The base assets directory in relation to the stylesheets
|
||||||
hmr: process.env.NODE_ENV === 'development'
|
hmr: process.env.NODE_ENV === 'development',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
'css-loader',
|
'css-loader',
|
||||||
'sass-loader'
|
'sass-loader',
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
|
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
|
||||||
@@ -63,36 +77,34 @@ module.exports = (env = {
|
|||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
name: '[name].[ext]',
|
name: '[name].[ext]',
|
||||||
outputPath: 'fonts/'
|
outputPath: 'fonts/',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.html$/,
|
test: /\.html$/,
|
||||||
exclude: [
|
exclude: [path.resolve(__dirname, 'index.html')],
|
||||||
path.resolve(__dirname, 'index.html'),
|
|
||||||
],
|
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'ng-cache-loader',
|
loader: 'ng-cache-loader',
|
||||||
options: {
|
options: {
|
||||||
prefix: 'templates:**'
|
prefix: 'templates:**',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.pug$/,
|
test: /\.pug$/,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'apply-loader'
|
loader: 'apply-loader',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loader: 'pug-loader'
|
loader: 'pug-loader',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user