-
-
Save darwis059/7ddfad95d702fa731272ce65291e4595 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Observable, | |
Subscription, | |
debounceTime, | |
filter, | |
mergeAll, | |
} from 'rxjs'; | |
import { getParentUuid } from './utils'; | |
import { blockUpdated, currentPage, pageUpdateObs } from './subject'; | |
import { | |
observeOnMutation, | |
pipeFinishEdit, | |
pipeStartEdit, | |
pipePageTagged, | |
pipeDoneTask, | |
} from './rxjsPipe'; | |
const targetNode: HTMLElement = <HTMLElement>( | |
document.getElementById('app-container') | |
); | |
const config = { attributes: false, childList: true, subtree: true }; | |
let observeFinishEditBlock: Subscription; | |
let observeEditingBlock: Subscription; | |
let observeStartEditBlock: Observable<string[]>; | |
let observePageUpdate: Subscription; | |
let observePageTagged: Subscription; | |
let observeDoneTask: Subscription; | |
const startMutationObservable = () => { | |
observePageUpdate = pageUpdateObs.subscribe(console.log); | |
const mutationObs = observeOnMutation(targetNode, config).pipe( | |
mergeAll() | |
// tap(console.log) | |
); | |
observeStartEditBlock = pipeStartEdit(mutationObs); | |
observeFinishEditBlock = pipeFinishEdit( | |
mutationObs, | |
observeStartEditBlock | |
).subscribe(([finishEdit, startEdit]) => { | |
// console.log({ finishEdit, startEdit }); | |
if ( | |
!( | |
finishEdit.content === startEdit[0] || | |
finishEdit.content === startEdit[1] | |
) | |
) { | |
blockUpdated.next({ | |
type: 'finished', | |
uuids: finishEdit.parentUuid, | |
page: encodeURIComponent(location.hash).split('/page/')?.[1], | |
}); | |
// backward compability | |
dispatchEvent( | |
new CustomEvent('blockupdated', { | |
detail: { uuid: finishEdit.parentUuid }, | |
}) | |
); | |
} | |
}); | |
// export the observeFinishEditBlock before subscribed. import on needed component to subscribe it | |
// or create subject for edited block, call subject.next in observeFinishEditBlock subscribe function. | |
// able to trigger block updated event from anywhere not only from mutation observer. | |
observeEditingBlock = mutationObs | |
.pipe( | |
filter((m) => (<HTMLElement>m.target).id === 'mock-text'), | |
debounceTime(300) | |
) | |
.subscribe((m) => { | |
const targetEl = <HTMLElement>m.target; | |
const uuid = targetEl.closest('div[blockid]')?.getAttribute('blockid'); | |
const parentUuid = getParentUuid(targetEl); | |
// console.log({ uuid, parentUuid }); | |
blockUpdated.next({ | |
type: 'editing', | |
uuids: parentUuid, | |
page: encodeURIComponent(location.hash).split('/page/')?.[1], | |
}); | |
// dispatchEvent( | |
// new CustomEvent('newblockupdated', { | |
// detail: { type: 'editing', uuids: parentUuid }, | |
// }) | |
// ); | |
}); | |
observePageTagged = pipePageTagged(mutationObs).subscribe((tagsDom) => { | |
const pageNames = tagsDom.map((t) => ({ | |
el: t, | |
tag: decodeURIComponent(t.querySelector('a').getAttribute('href')).split( | |
'/page/' | |
)[1], | |
})); | |
const relatedPage = logseq.api | |
.datascript_query( | |
`[:find (pull ?b [:db/id :block/uuid :block/name {:block/tags [:block/name :db/id]}]) | |
:in $ ?names | |
:where | |
[?b :block/name ?name] | |
[(contains? ?names ?name)] | |
]`, | |
`#{${pageNames.map((s) => `"${s.tag}"`).join(' ')}}` | |
) | |
.flat() as LogseqPage[]; | |
pageNames.forEach((pageName) => { | |
const page = relatedPage.filter((p) => p.name === pageName.tag)[0]; | |
if (page.tags) { | |
const spanDom = document.createElement('span'); | |
spanDom.classList.add('ml-4'); | |
page.tags | |
.filter((t) => t.name !== currentPage) | |
.forEach((tag) => { | |
const tagDom = document.createElement('a'); | |
tagDom.setAttribute( | |
'href', | |
`#/page/${encodeURIComponent(tag.name)}` | |
); | |
tagDom.textContent = `#${tag.name}`; | |
tagDom.classList.add('px-1', 'text-green-300'); | |
spanDom.appendChild(tagDom); | |
}); | |
pageName.el.appendChild(spanDom); | |
} | |
}); | |
}); | |
observeDoneTask = pipeDoneTask(mutationObs).subscribe((uuid) => { | |
console.log(uuid); | |
const today = new Date(); | |
logseq.api.upsert_block_property( | |
uuid, | |
'done', | |
`${today.getFullYear()}${(today.getMonth() + 1) | |
.toString() | |
.padStart(2, '0')}${today.getDate()}` | |
); | |
}); | |
}; | |
export { startMutationObservable }; | |
if (import.meta.hot) { | |
import.meta.hot.accept((obs) => { | |
// import.meta.hot.invalidate(); | |
if (obs) { | |
observeFinishEditBlock.unsubscribe(); | |
observeEditingBlock.unsubscribe(); | |
observePageUpdate.unsubscribe(); | |
observePageTagged.unsubscribe(); | |
observeDoneTask.unsubscribe(); | |
obs?.startMutationObservable(); | |
console.log('updated observer module', obs); | |
} | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const checkNodes = ( | |
node: NodeList, | |
checkFn: (el: HTMLElement) => Boolean | |
) => | |
Array.from(node) | |
.filter((n) => n.nodeType !== Node.TEXT_NODE) | |
.some( | |
(n) => { | |
// const nEl = n as HTMLElement; | |
const nEl = 'querySelector' in n ? <HTMLElement>n : n.parentElement; | |
return checkFn(nEl); //nEl.classList.contains(className); | |
} //n.parentElement?.classList.contains('editor-wrapper') | |
); | |
export const pipeDoneTask = (obs: Observable<MutationRecord>) => | |
obs.pipe( | |
filter( | |
(m) => | |
(<HTMLElement>m.target)?.tagName === 'SPAN' && | |
(<HTMLElement>m.target).classList.contains('done') | |
), | |
throttleTime(200), | |
map((m) => getUuid(m.target.parentElement)) | |
); | |
export const pipePageTagged = (obs: Observable<MutationRecord>) => | |
obs.pipe( | |
filter( | |
(m) => | |
m.addedNodes.length > 0 && | |
checkNodes( | |
m.addedNodes, | |
(el) => !!el.querySelector('div.references.page-tags') | |
) | |
), | |
map((m) => | |
Array.from( | |
(m.addedNodes[0] as HTMLElement) | |
.querySelector('div.references.page-tags') | |
.querySelectorAll('li') | |
) | |
) | |
// mergeAll() | |
); | |
export const pipeStartEdit = (obs: Observable<MutationRecord>) => | |
obs.pipe( | |
filter( | |
(m) => | |
m.addedNodes.length > 0 && | |
checkNodes(m.addedNodes, (el) => el.classList.contains('editor-inner')) | |
// Array.from(m.addedNodes) | |
// .filter((n) => n.nodeType !== Node.TEXT_NODE) | |
// .some( | |
// (n) => { | |
// const nEl = n as HTMLElement; | |
// return nEl.classList.contains('editor-inner'); | |
// } //n.parentElement?.classList.contains('editor-wrapper') | |
// ) | |
), | |
map((m) => | |
(<HTMLElement>m.target).querySelector('textarea').textContent.trim() | |
), | |
pairwise() | |
// tap(console.log) | |
); | |
export const pipeFinishEdit = ( | |
obs: Observable<MutationRecord>, | |
observeStartEditBlock: Observable<any> | |
) => | |
obs.pipe( | |
filter( | |
(m) => | |
m.removedNodes.length > 0 && | |
checkNodes(m.removedNodes, (el) => | |
el.classList.contains('editor-inner') | |
) | |
// Array.from(m.removedNodes) | |
// .filter((n) => n.nodeType !== Node.TEXT_NODE) | |
// .some( | |
// (n) => { | |
// const nEl = n as HTMLElement; | |
// return nEl.classList.contains('editor-inner'); | |
// } //n.parentElement?.classList.contains('editor-wrapper') | |
// ) | |
), | |
map((m) => { | |
const targetEl = <HTMLElement>m.target; | |
const uuid = targetEl.closest('div[blockid]')?.getAttribute('blockid'); | |
const logseqContent = logseq.api.get_block(uuid).content; | |
return { | |
uuid, | |
parentUuid: getParentUuid(targetEl), | |
content: logseqContent || targetEl.textContent.trim(), | |
}; | |
}), | |
withLatestFrom(observeStartEditBlock) | |
); | |
export const observeOnMutation = ( | |
target, | |
config | |
): Observable<MutationRecord[]> => { | |
return new Observable((observer) => { | |
const mutation = new MutationObserver((mutations, instance) => { | |
pageUpdated.next(decodeURIComponent(location.hash)); | |
observer.next(mutations); | |
}); | |
mutation.observe(target, config); | |
const unsubscribe = () => { | |
mutation.disconnect(); | |
}; | |
return unsubscribe; | |
}); | |
}; | |
if (import.meta.hot) { | |
import.meta.hot.accept((obs) => { | |
// import.meta.hot.invalidate(); | |
console.log('updated util module', obs); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Subject, filter, pairwise, tap } from 'rxjs'; | |
let currentPage = ''; | |
const blockUpdated = new Subject<{ | |
type: 'finished' | 'editing'; | |
uuids: string[]; | |
page: string; | |
}>(); | |
const pageUpdated = new Subject<string>(); //.pipe(pairwise(),tap(console.log)); | |
pageUpdated.next(''); | |
const pageUpdateObs = pageUpdated.pipe( | |
pairwise(), | |
tap((p) => (currentPage = p[1].split('/page/')[1])), | |
filter((p) => p[0] !== p[1]) | |
); | |
export { blockUpdated, pageUpdated, pageUpdateObs, currentPage }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function getParentUuid(el: HTMLElement, uuid: string[] = []): string[] { | |
const blockEl = el.closest('div[blockid]') as HTMLElement; | |
// console.log({ blockEl }); | |
if (blockEl) { | |
uuid.push(blockEl.getAttribute('blockid')); | |
return getParentUuid(blockEl.parentElement, uuid); | |
} | |
return uuid; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment