client: use next-pwa, fix import different version yjs

This commit is contained in:
fantasticit 2022-05-20 17:57:05 +08:00
parent 2ee25bcb50
commit d823775e3e
7 changed files with 210 additions and 29 deletions

View File

@ -39,6 +39,7 @@
"node": ">=16.5.0"
},
"devDependencies": {
"@types/node": "^17.0.35",
"husky": "^7.0.4",
"lint-staged": "^12.4.1",
"prettier": "^2.3.2",

View File

@ -1,6 +1,6 @@
const semi = require('@douyinfe/semi-next').default({});
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const withOffline = require('next-offline');
const withPWA = require('next-pwa');
const { getConfig } = require('@think/config');
const config = getConfig();
@ -27,26 +27,11 @@ const nextConfig = semi({
compiler: {
removeConsole: true,
},
workboxOpts: {
runtimeCaching: [
{
urlPattern: /.(png|jpg|jpeg|svg|webp)$/,
handler: 'CacheFirst',
},
{
urlPattern: /api/,
handler: 'NetworkFirst',
options: {
cacheableResponse: {
statuses: [0, 200],
headers: {
'x-sw': 'true',
},
},
},
},
],
pwa: {
disable: process.env.NODE_ENV !== 'production',
dest: '.next',
sw: 'service-worker.js',
},
});
module.exports = withOffline(nextConfig);
module.exports = withPWA(nextConfig);

View File

@ -71,7 +71,7 @@
"markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0",
"next": "12.0.10",
"next-offline": "^5.0.5",
"next-pwa": "^5.5.2",
"prosemirror-markdown": "^1.7.0",
"prosemirror-model": "^1.16.1",
"prosemirror-schema-list": "^1.1.6",
@ -93,8 +93,6 @@
"tippy.js": "^6.3.7",
"toggle-selection": "^1.0.6",
"viewerjs": "^1.10.4",
"y-indexeddb": "^9.0.7",
"y-prosemirror": "^1.0.14",
"yjs": "^13.5.24"
},
"devDependencies": {
@ -102,6 +100,7 @@
"@types/react": "17.0.38",
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.21.0",
"copy-webpack-plugin": "11.0.0",
"eslint": "^8.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
@ -109,7 +108,9 @@
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.5.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"fs-extra": "^10.0.0",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "4.5.5"
"typescript": "4.5.5",
"workbox-webpack-plugin": "^6.5.3"
}
}

View File

@ -1,8 +1,13 @@
import * as math from 'lib0/math';
import { Plugin } from 'prosemirror-state'; // eslint-disable-line
import { Decoration, DecorationSet } from 'prosemirror-view'; // eslint-disable-line
import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, setMeta } from 'y-prosemirror';
import { yCursorPluginKey, ySyncPluginKey } from 'y-prosemirror';
import {
absolutePositionToRelativePosition,
relativePositionToAbsolutePosition,
setMeta,
yCursorPluginKey,
ySyncPluginKey,
} from 'tiptap/core/y-prosemirror/y-prosemirror';
import * as Y from 'yjs';
/**

View File

@ -1,5 +1,5 @@
import { Extension } from '@tiptap/core';
import { redo, undo, ySyncPlugin, yUndoPlugin, yUndoPluginKey } from 'y-prosemirror';
import { redo, undo, ySyncPlugin, yUndoPlugin, yUndoPluginKey } from 'tiptap/core/y-prosemirror/y-prosemirror';
import { UndoManager } from 'yjs';
declare module '@tiptap/core' {

View File

@ -1,5 +1,5 @@
import { Transaction } from 'prosemirror-state';
import { ySyncPluginKey } from 'y-prosemirror';
import { ySyncPluginKey } from 'tiptap/core/y-prosemirror/y-prosemirror';
export function isChangeOrigin(transaction: Transaction): boolean {
return !!transaction.getMeta(ySyncPluginKey);

View File

@ -0,0 +1,189 @@
import * as idb from 'lib0/indexeddb.js';
import * as mutex from 'lib0/mutex.js';
import { Observable } from 'lib0/observable.js';
import * as Y from 'yjs';
const customStoreName = 'custom';
const updatesStoreName = 'updates';
export const PREFERRED_TRIM_SIZE = 500;
/**
* @param {IndexeddbPersistence} idbPersistence
*/
export const fetchUpdates = (idbPersistence) => {
const [updatesStore] = idb.transact(/** @type {IDBDatabase} */ (idbPersistence.db), [updatesStoreName]); // , 'readonly')
return idb
.getAll(updatesStore, idb.createIDBKeyRangeLowerBound(idbPersistence._dbref, false))
.then((updates) =>
idbPersistence._mux(() =>
Y.transact(
idbPersistence.doc,
() => {
updates.forEach((val) => Y.applyUpdate(idbPersistence.doc, val));
},
idbPersistence,
false
)
)
)
.then(() =>
idb.getLastKey(updatesStore).then((lastKey) => {
idbPersistence._dbref = lastKey + 1;
})
)
.then(() =>
idb.count(updatesStore).then((cnt) => {
idbPersistence._dbsize = cnt;
})
)
.then(() => updatesStore);
};
/**
* @param {IndexeddbPersistence} idbPersistence
* @param {boolean} forceStore
*/
export const storeState = (idbPersistence, forceStore = true) =>
fetchUpdates(idbPersistence).then((updatesStore) => {
if (forceStore || idbPersistence._dbsize >= PREFERRED_TRIM_SIZE) {
idb
.addAutoKey(updatesStore, Y.encodeStateAsUpdate(idbPersistence.doc))
.then(() => idb.del(updatesStore, idb.createIDBKeyRangeUpperBound(idbPersistence._dbref, true)))
.then(() =>
idb.count(updatesStore).then((cnt) => {
idbPersistence._dbsize = cnt;
})
);
}
});
/**
* @param {string} name
*/
export const clearDocument = (name) => idb.deleteDB(name);
/**
* @extends Observable<string>
*/
export class IndexeddbPersistence extends Observable {
/**
* @param {string} name
* @param {Y.Doc} doc
*/
constructor(name, doc) {
super();
this.doc = doc;
this.name = name;
this._mux = mutex.createMutex();
this._dbref = 0;
this._dbsize = 0;
/**
* @type {IDBDatabase|null}
*/
this.db = null;
this.synced = false;
this._db = idb.openDB(name, (db) => idb.createStores(db, [['updates', { autoIncrement: true }], ['custom']]));
/**
* @type {Promise<IndexeddbPersistence>}
*/
this.whenSynced = this._db.then((db) => {
this.db = db;
const currState = Y.encodeStateAsUpdate(doc);
return fetchUpdates(this)
.then((updatesStore) => idb.addAutoKey(updatesStore, currState))
.then(() => {
this.emit('synced', [this]);
this.synced = true;
return this;
});
});
/**
* Timeout in ms untill data is merged and persisted in idb.
*/
this._storeTimeout = 1000;
/**
* @type {any}
*/
this._storeTimeoutId = null;
/**
* @param {Uint8Array} update
*/
this._storeUpdate = (update) =>
this._mux(() => {
if (this.db) {
const [updatesStore] = idb.transact(/** @type {IDBDatabase} */ (this.db), [updatesStoreName]);
idb.addAutoKey(updatesStore, update);
if (++this._dbsize >= PREFERRED_TRIM_SIZE) {
// debounce store call
if (this._storeTimeoutId !== null) {
clearTimeout(this._storeTimeoutId);
}
this._storeTimeoutId = setTimeout(() => {
storeState(this, false);
this._storeTimeoutId = null;
}, this._storeTimeout);
}
}
});
doc.on('update', this._storeUpdate);
this.destroy = this.destroy.bind(this);
doc.on('destroy', this.destroy);
}
destroy() {
if (this._storeTimeoutId) {
clearTimeout(this._storeTimeoutId);
}
this.doc.off('update', this._storeUpdate);
this.doc.off('destroy', this.destroy);
return this._db.then((db) => {
db.close();
});
}
/**
* Destroys this instance and removes all data from indexeddb.
*
* @return {Promise<void>}
*/
clearData() {
return this.destroy().then(() => {
idb.deleteDB(this.name);
});
}
/**
* @param {String | number | ArrayBuffer | Date} key
* @return {Promise<String | number | ArrayBuffer | Date | any>}
*/
get(key) {
return this._db.then((db) => {
const [custom] = idb.transact(db, [customStoreName], 'readonly');
return idb.get(custom, key);
});
}
/**
* @param {String | number | ArrayBuffer | Date} key
* @param {String | number | ArrayBuffer | Date} value
* @return {Promise<String | number | ArrayBuffer | Date>}
*/
set(key, value) {
return this._db.then((db) => {
const [custom] = idb.transact(db, [customStoreName]);
return idb.put(custom, value, key);
});
}
/**
* @param {String | number | ArrayBuffer | Date} key
* @return {Promise<undefined>}
*/
del(key) {
return this._db.then((db) => {
const [custom] = idb.transact(db, [customStoreName]);
return idb.del(custom, key);
});
}
}