mirror of
https://github.com/logseq/logseq.git
synced 2026-05-05 11:26:26 +00:00
* Squashed commit of the following: commitea9af272e4Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 00:23:24 2021 +0800 feat: type c to git commit also, fixed an issue that backspace can delete selected blocks when there's a dialog. commit78e24f7479Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 00:01:21 2021 +0800 fix: add patch parser worker to yarn watch commit7f6e777bcdAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Thu Aug 26 23:57:46 2021 +0800 fix: add several shortcuts to the Others category commit509697b276Author: Tienson Qin <tiensonqin@gmail.com> Date: Thu Aug 26 23:55:40 2021 +0800 fix: git username and email configuration * Squashed commit of the following: commit401d85be5fAuthor: Peng Xiao <pengxiao@outlook.com> Date: Fri Aug 27 11:10:32 2021 +0800 feat: add protobuf mode commitdc1e9fdfc9Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 13:33:47 2021 +0800 chore: replace : with comma for git path ':' is a reserved character on Windows commitea9af272e4Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 00:23:24 2021 +0800 feat: type c to git commit also, fixed an issue that backspace can delete selected blocks when there's a dialog. commit78e24f7479Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 00:01:21 2021 +0800 fix: add patch parser worker to yarn watch commit7f6e777bcdAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Thu Aug 26 23:57:46 2021 +0800 fix: add several shortcuts to the Others category commit509697b276Author: Tienson Qin <tiensonqin@gmail.com> Date: Thu Aug 26 23:55:40 2021 +0800 fix: git username and email configuration * feat(plugin): ui of marketplace plugins list * improve(plugin): support reload * improve(plugin): installation from marketplace * fix conflicts * improve(plugin): installation from github public repo * chore: remove unwanted dependency * chore: remove console * improve(plugin): add shortcuts * ui(plugin): polish LOADING indicator * improve(plugin): support up-to-date of marketplace plugin * fix: remove debug option * improve(plugin): better interaction of themes picker * improve(plugin): better experience when installing theme from marketplace * fix(plugin): downloads label of marketplace plugin * improve(plugin): update package name field * improve(plugin): change marketplace packages repo to `logseq/marketplace` * fix(plugin): plugin title when updating notification * fix: conflicts * enhance(plugin): i18n related marketplace & lifecycle of plugin installation * improve(plugin): handle offline situation * ui(plugin): header plugin icons container * fix(ui): add class identity for journal page with date page name * improve(plugin): remote readme for marketplace plugin * enhance(plugin): polish plugin card * chore(plugin): build libs core * Squashed commit of the following: commit751db4828cAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Tue Sep 7 16:58:25 2021 +0800 enhance: log git errors commitc2dbbc77bfAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Tue Sep 7 16:27:00 2021 +0800 enhance: display refresh status commitf734b6db37Author: Tienson Qin <tiensonqin@gmail.com> Date: Tue Sep 7 16:03:57 2021 +0800 fix: .git doesn't work well with third-party cloud services commit7e44d81f1dAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Tue Sep 7 13:52:38 2021 +0800 fix: git init into the current graph folder instead of a separate git directory because .gitdir might has different paths on multiple devices, another reason is that the graph might have different histories considering the .git directory is not synced. commitb86a801514Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 23:23:27 2021 +0800 enhance: don't show diff if there's only blank changes commit0b55d119aaAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 22:39:06 2021 +0800 fix: save the previous content in Logseq first and commit it to avoid overwritten when syncing with iCloud/Dropbox/syncthing. commite0baf4b05cAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 22:18:45 2021 +0800 fix: close file watcher when exit the app commit10e7a9fbd6Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 21:05:38 2021 +0800 fix: disable cut selections in the query result block commit90c2bd7cc2Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 18:42:17 2021 +0800 fix: terminate parser and persist dbs when reloading the app (electron) commit571c81af30Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 17:57:33 2021 +0800 enhance: add sync from local files (the old refresh way) commita16e5c98baAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 17:23:57 2021 +0800 fix: Dragging blocks to update notes does not synchronize updates to the notes file in real time. close #2744 commit6897a22a3fAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 16:43:30 2021 +0800 fix: disable page/block auto-complete once the cursor went outside commitfeb4404874Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 13:28:47 2021 +0800 fix: wrong page metadata saved for another graph commitb96332122fAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 13:08:28 2021 +0800 enhance: catch errors when app quits commit1ee0c240c3Author: Jiang Hailong <gombiuda@gmail.com> Date: Wed Sep 1 20:54:13 2021 +0800 FIX: Linked reference is not refresh after file altering #2694 commit0550c8a876Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 12:25:39 2021 +0800 fix: display logbook for scheduled tasks commit2a5f0cee7cAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 12:21:19 2021 +0800 fix: spent hours for logbook commit1f2c9e4d3fAuthor: leizhe <lzhes43@gmail.com> Date: Sat Sep 4 12:59:09 2021 +0900 fix(timestamp): remove old SCHEDULED/DEADLINE timestamp When using `date-picker` to update the SCHEDULED/DEADLINE timestamp by clicking an existing one, logseq will add a new timestamp instead of updating the old one. This patch fixs this issue. commitcb23b967e4Author: leizhe <lzhes43@gmail.com> Date: Sat Sep 4 11:36:34 2021 +0900 fix(repeat): more consistent with orgmode style Ref: https://orgmode.org/manual/Tracking-your-habits.html commit229c7f2594Author: leizhe <lzhes43@gmail.com> Date: Sat Sep 4 15:11:07 2021 +0900 enhance(property): remove empty properties drawer commita76df9ce97Author: leizhe <lzhes43@gmail.com> Date: Sat Sep 4 10:27:30 2021 +0900 fix(clock): duplicate clock-in log twice 1. `set-marker` will not log time anymore. The time logging is moved to `with-timetracking` 2. Concat `logbook` only if `new-clocks` is nil, which fixs the duplication of clock-in log. commitc79e9f9e3eAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 12:04:16 2021 +0800 code: cleanup commit9ec85db09aAuthor: DarshanSudhakar <$K3Ug1i&> Date: Mon Sep 6 08:32:07 2021 +0530 Fixing typo for the tooltip 'Block reference' commiteec677873bAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 11:49:58 2021 +0800 enhance: page history support reverting back commit7273112a00Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 11:23:31 2021 +0800 git: revert back commitcd853b5864Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 10:39:08 2021 +0800 git: should compare ignored-files with disk content commita84dfb5effAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 10:31:16 2021 +0800 git: add ignore-files to avoid repeated notifications commit86577e7ebfAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 10:07:39 2021 +0800 fix: ignore permission denied error when git add all commit8dc0ca9ff5Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 09:54:03 2021 +0800 fix: run git config core.safecrlf false on windows commit9edaae559dAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 09:20:06 2021 +0800 refactor: extract file ops commitf12f58d3faAuthor: tiagodevezas <tiagodevezas@gmail.com> Date: Fri Sep 3 23:25:07 2021 +0100 fix typos commit9e82f0117cAuthor: tiagodevezas <tiagodevezas@gmail.com> Date: Fri Sep 3 21:32:03 2021 +0100 Translate shortcuts to Portuguese (pt-PT) commit9a2c17bb05Author: tiagodevezas <tiagodevezas@gmail.com> Date: Fri Sep 3 20:58:33 2021 +0100 Translate new settings to pt-PT commitc05034cc34Author: Sebastian Bensusan <sbensu@gmail.com> Date: Sat Sep 4 07:57:07 2021 -0700 feat(calc): Understand percentages commitbfe6a5d6cbAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 08:46:30 2021 +0800 enhance: commit the content in logseq when detecting any disk changes * improve(plugin): add install plugin api * fix(plugin): protected plugin installation api * improve(plugin): tweak readme display for local * fix: conflicts * fix(plugin): get block option with include children * improve(plugin): copy more marketplace manifest fields to plugin * fix: conflicts * improve(plugin): shortcut for copying plugin id * Squashed commit of the following: commite51ea54a75Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 13 10:40:25 2021 +0800 fix: change ack timeout * fix(plugin): non blank icon string * fix: conflicts * fix: e name * fix: remove debug from state * chore(plugin): bump libs minor version Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
338 lines
8.3 KiB
TypeScript
338 lines
8.3 KiB
TypeScript
import Debug from 'debug'
|
|
import { Postmate, Model, ParentAPI, ChildAPI } from './postmate'
|
|
import EventEmitter from 'eventemitter3'
|
|
import { PluginLocal } from './LSPlugin.core'
|
|
import { deferred, IS_DEV } from './helpers'
|
|
import { LSPluginShadowFrame } from './LSPlugin.shadow'
|
|
|
|
const debug = Debug('LSPlugin:caller')
|
|
|
|
type DeferredActor = ReturnType<typeof deferred>
|
|
|
|
export const FLAG_AWAIT = '#await#response#'
|
|
export const LSPMSG = '#lspmsg#'
|
|
export const LSPMSG_ERROR_TAG = '#lspmsg#error#'
|
|
export const LSPMSG_SETTINGS = '#lspmsg#settings#'
|
|
export const LSPMSG_BEFORE_UNLOAD = '#lspmsg#beforeunload#'
|
|
export const LSPMSG_SYNC = '#lspmsg#reply#'
|
|
export const LSPMSG_READY = '#lspmsg#ready#'
|
|
export const LSPMSGFn = (id: string) => `${LSPMSG}${id}`
|
|
export const AWAIT_LSPMSGFn = (id: string) => `${FLAG_AWAIT}${id}`
|
|
|
|
/**
|
|
* Call between core and user
|
|
*/
|
|
class LSPluginCaller extends EventEmitter {
|
|
private _connected: boolean = false
|
|
|
|
private _parent?: ParentAPI
|
|
private _child?: ChildAPI
|
|
|
|
private _shadow?: LSPluginShadowFrame
|
|
|
|
private _status?: 'pending' | 'timeout'
|
|
private _userModel: any = {}
|
|
|
|
private _call?: (type: string, payload: any, actor?: DeferredActor) => Promise<any>
|
|
private _callUserModel?: (type: string, payload: any) => Promise<any>
|
|
|
|
private _debugTag = ''
|
|
|
|
constructor (
|
|
private _pluginLocal: PluginLocal | null
|
|
) {
|
|
super()
|
|
|
|
if (_pluginLocal) {
|
|
this._debugTag = _pluginLocal.debugTag
|
|
}
|
|
}
|
|
|
|
async connectToChild () {
|
|
if (this._connected) return
|
|
|
|
const { shadow } = this._pluginLocal!
|
|
|
|
if (shadow) {
|
|
await this._setupShadowSandbox()
|
|
} else {
|
|
await this._setupIframeSandbox()
|
|
}
|
|
}
|
|
|
|
async connectToParent (userModel = {}) {
|
|
if (this._connected) return
|
|
|
|
const caller = this
|
|
const isShadowMode = this._pluginLocal != null
|
|
|
|
let syncGCTimer: any = 0
|
|
let syncTag = 0
|
|
const syncActors = new Map<number, DeferredActor>()
|
|
const readyDeferred = deferred(1000 * 5)
|
|
|
|
const model: any = this._extendUserModel({
|
|
[LSPMSG_READY]: async () => {
|
|
await readyDeferred.resolve()
|
|
},
|
|
|
|
[LSPMSG_BEFORE_UNLOAD]: async (e) => {
|
|
const actor = deferred(10 * 1000)
|
|
caller.emit('beforeunload', Object.assign({ actor }, e))
|
|
await actor.promise
|
|
},
|
|
|
|
[LSPMSG_SETTINGS]: async ({ type, payload }) => {
|
|
caller.emit('settings:changed', payload)
|
|
},
|
|
|
|
[LSPMSG]: async ({ ns, type, payload }: any) => {
|
|
debug(`[call from host] ${this._debugTag}`, ns, type, payload)
|
|
|
|
if (ns && ns.startsWith('hook')) {
|
|
caller.emit(`${ns}:${type}`, payload)
|
|
return
|
|
}
|
|
|
|
caller.emit(type, payload)
|
|
},
|
|
|
|
[LSPMSG_SYNC]: ({ _sync, result }: any) => {
|
|
debug(`[sync reply] #${_sync}`, result)
|
|
|
|
if (syncActors.has(_sync)) {
|
|
const actor = syncActors.get(_sync)
|
|
|
|
if (actor) {
|
|
if (result?.hasOwnProperty(LSPMSG_ERROR_TAG)) {
|
|
actor.reject(result[LSPMSG_ERROR_TAG])
|
|
} else {
|
|
actor.resolve(result)
|
|
}
|
|
|
|
syncActors.delete(_sync)
|
|
}
|
|
}
|
|
},
|
|
|
|
...userModel
|
|
})
|
|
|
|
if (isShadowMode) {
|
|
await readyDeferred.promise
|
|
return JSON.parse(JSON.stringify(this._pluginLocal?.toJSON()))
|
|
}
|
|
|
|
const pm = new Model(model)
|
|
const handshake = pm.sendHandshakeReply()
|
|
|
|
this._status = 'pending'
|
|
|
|
await handshake.then((refParent: ChildAPI) => {
|
|
this._child = refParent
|
|
this._connected = true
|
|
|
|
this._call = async (type, payload = {}, actor) => {
|
|
if (actor) {
|
|
const tag = ++syncTag
|
|
syncActors.set(tag, actor)
|
|
payload._sync = tag
|
|
|
|
actor.setTag(`async call #${tag}`)
|
|
debug('async call #', tag)
|
|
}
|
|
|
|
refParent.emit(LSPMSGFn(model.baseInfo.id), { type, payload })
|
|
|
|
return actor?.promise as Promise<any>
|
|
}
|
|
|
|
this._callUserModel = async (type, payload) => {
|
|
try {
|
|
model[type](payload)
|
|
} catch (e) {
|
|
debug(`[model method] #${type} not existed`)
|
|
}
|
|
}
|
|
|
|
// actors GC
|
|
syncGCTimer = setInterval(() => {
|
|
if (syncActors.size > 100) {
|
|
for (const [k, v] of syncActors) {
|
|
if (v.settled) {
|
|
syncActors.delete(k)
|
|
}
|
|
}
|
|
}
|
|
}, 1000 * 60 * 30)
|
|
}).finally(() => {
|
|
this._status = undefined
|
|
})
|
|
|
|
await readyDeferred.promise
|
|
|
|
return model.baseInfo
|
|
}
|
|
|
|
async call (type: any, payload: any = {}) {
|
|
return this._call?.call(this, type, payload)
|
|
}
|
|
|
|
async callAsync (type: any, payload: any = {}) {
|
|
const actor = deferred(1000 * 10)
|
|
return this._call?.call(this, type, payload, actor)
|
|
}
|
|
|
|
async callUserModel (type: string, payload: any = {}) {
|
|
return this._callUserModel?.call(this, type, payload)
|
|
}
|
|
|
|
async _setupIframeSandbox () {
|
|
const cnt = document.body
|
|
const pl = this._pluginLocal!
|
|
const id = pl.id
|
|
const url = new URL(pl.options.entry!)
|
|
|
|
url.searchParams
|
|
.set(`__v__`, IS_DEV ? Date.now().toString() : pl.options.version)
|
|
|
|
// clear zombie sandbox
|
|
const zb = cnt.querySelector(`#${id}`)
|
|
if (zb) zb.parentElement.removeChild(zb)
|
|
|
|
const pt = new Postmate({
|
|
id, container: cnt, url: url.href,
|
|
classListArray: ['lsp-iframe-sandbox'],
|
|
model: { baseInfo: JSON.parse(JSON.stringify(pl.toJSON())) }
|
|
})
|
|
|
|
let handshake = pt.sendHandshake()
|
|
this._status = 'pending'
|
|
|
|
// timeout for handshake
|
|
let timer
|
|
|
|
return new Promise((resolve, reject) => {
|
|
timer = setTimeout(() => {
|
|
reject(new Error(`handshake Timeout`))
|
|
}, 3 * 1000) // 3secs
|
|
|
|
handshake.then((refChild: ParentAPI) => {
|
|
this._parent = refChild
|
|
this._connected = true
|
|
this.emit('connected')
|
|
|
|
refChild.on(LSPMSGFn(pl.id), ({ type, payload }: any) => {
|
|
debug(`[call from plugin] `, type, payload)
|
|
|
|
this._pluginLocal?.emit(type, payload || {})
|
|
})
|
|
|
|
this._call = async (...args: any) => {
|
|
// parent all will get message before handshaked
|
|
await refChild.call(LSPMSGFn(pl.id), {
|
|
type: args[0], payload: Object.assign(args[1] || {}, {
|
|
$$pid: pl.id
|
|
})
|
|
})
|
|
}
|
|
|
|
this._callUserModel = async (type, payload: any) => {
|
|
if (type.startsWith(FLAG_AWAIT)) {
|
|
// TODO: attach payload with method call
|
|
return await refChild.get(type.replace(FLAG_AWAIT, ''))
|
|
} else {
|
|
refChild.call(type, payload)
|
|
}
|
|
}
|
|
|
|
resolve(null)
|
|
}).catch(e => {
|
|
reject(e)
|
|
}).finally(() => {
|
|
clearTimeout(timer)
|
|
})
|
|
}).catch(e => {
|
|
debug('[iframe sandbox] error', e)
|
|
throw e
|
|
}).finally(() => {
|
|
this._status = undefined
|
|
})
|
|
}
|
|
|
|
async _setupShadowSandbox () {
|
|
const pl = this._pluginLocal!
|
|
const shadow = this._shadow = new LSPluginShadowFrame(pl)
|
|
|
|
try {
|
|
this._status = 'pending'
|
|
|
|
await shadow.load()
|
|
|
|
this._connected = true
|
|
this.emit('connected')
|
|
|
|
this._call = async (type, payload = {}, actor) => {
|
|
actor && (payload.actor = actor)
|
|
|
|
// @ts-ignore Call in same thread
|
|
this._pluginLocal?.emit(type, Object.assign(payload, {
|
|
$$pid: pl.id
|
|
}))
|
|
|
|
return actor?.promise
|
|
}
|
|
|
|
this._callUserModel = async (...args: any) => {
|
|
let type = args[0] as string
|
|
|
|
if (type?.startsWith(FLAG_AWAIT)) {
|
|
type = type.replace(FLAG_AWAIT, '')
|
|
}
|
|
|
|
const payload = args[1] || {}
|
|
const fn = this._userModel[type]
|
|
|
|
if (typeof fn === 'function') {
|
|
await fn.call(null, payload)
|
|
}
|
|
}
|
|
} catch (e) {
|
|
debug('[shadow sandbox] error', e)
|
|
throw e
|
|
} finally {
|
|
this._status = undefined
|
|
}
|
|
}
|
|
|
|
_extendUserModel (model: any) {
|
|
return Object.assign(this._userModel, model)
|
|
}
|
|
|
|
_getSandboxIframeContainer () {
|
|
return this._parent?.frame
|
|
}
|
|
|
|
_getSandboxShadowContainer () {
|
|
return this._shadow?.frame
|
|
}
|
|
|
|
set debugTag (value: string) {
|
|
this._debugTag = value
|
|
}
|
|
|
|
async destroy () {
|
|
if (this._parent) {
|
|
await this._parent.destroy()
|
|
}
|
|
|
|
if (this._shadow) {
|
|
this._shadow.destroy()
|
|
}
|
|
}
|
|
}
|
|
|
|
export {
|
|
LSPluginCaller
|
|
}
|