Skip to content

Commit

Permalink
fix: add a title block for highlights and add note as a variable in t…
Browse files Browse the repository at this point in the history
…he highlight template
  • Loading branch information
sywhb committed Apr 14, 2023
1 parent f8c89c8 commit 6ee5090
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 136 deletions.
20 changes: 8 additions & 12 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export enum UpdateReason {
export interface UpdatesSinceResponse {
data: {
updatesSince: {
edges: { updateReason: UpdateReason; node: { slug: string } }[]
edges: { updateReason: UpdateReason; node: Article }[]
pageInfo: {
hasNextPage: boolean
}
Expand All @@ -37,6 +37,7 @@ export enum PageType {
}

export interface Article {
id: string
title: string
siteName?: string
originalArticleUrl: string
Expand Down Expand Up @@ -74,13 +75,6 @@ export interface Highlight {
highlightPositionPercent?: number
}

export interface DeletedArticle {
updateReason: UpdateReason
node: {
slug: string
}
}

const ENDPOINT = 'https://api-prod.omnivore.app/api/graphql'
const requestHeaders = (apiKey: string) => ({
'Content-Type': 'application/json',
Expand All @@ -107,6 +101,7 @@ export const getOmnivoreArticles = async (
... on SearchSuccess {
edges {
node {
id
title
slug
siteName
Expand Down Expand Up @@ -171,7 +166,7 @@ export const getDeletedOmnivoreArticles = async (
first = 10,
updatedAt = '',
endpoint = ENDPOINT
): Promise<[DeletedArticle[], boolean]> => {
): Promise<[Article[], boolean]> => {
const res = await fetch(endpoint, {
headers: requestHeaders(apiKey),
body: JSON.stringify({
Expand All @@ -182,6 +177,7 @@ export const getDeletedOmnivoreArticles = async (
edges {
updateReason
node {
id
slug
}
}
Expand All @@ -204,9 +200,9 @@ export const getDeletedOmnivoreArticles = async (
})

const jsonRes = (await res.json()) as UpdatesSinceResponse
const deletedArticles = jsonRes.data.updatesSince.edges.filter(
(edge) => edge.updateReason === UpdateReason.DELETED
)
const deletedArticles = jsonRes.data.updatesSince.edges
.filter((edge) => edge.updateReason === UpdateReason.DELETED)
.map((edge) => edge.node)

return [deletedArticles, jsonRes.data.updatesSince.pageInfo.hasNextPage]
}
212 changes: 89 additions & 123 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
import { DateTime } from 'luxon'
import {
Article,
DeletedArticle,
HighlightType,
PageType,
getDeletedOmnivoreArticles,
Expand All @@ -27,7 +26,6 @@ import {
DATE_FORMAT,
compareHighlightsInFile,
delay,
escapeQuotationMarks,
getHighlightLocation,
parseDateTime,
} from './util'
Expand All @@ -39,12 +37,6 @@ const isValidCurrentGraph = async (): Promise<boolean> => {
return currentGraph?.name === settings.graph
}

const deleteBlocks = async (blocks: BlockEntity[]) => {
for await (const block of blocks) {
await logseq.Editor.removeBlock(block.uuid)
}
}

const startSyncJob = () => {
const settings = logseq.settings as Settings
// sync every frequency minutes
Expand Down Expand Up @@ -80,6 +72,29 @@ const resetState = () => {
resetSyncJob()
}

const getBlockByContent = async (
pageName: string,
parentBlockId: string,
content: string
): Promise<BlockEntity | undefined> => {
const blocks = (
await logseq.DB.datascriptQuery<BlockEntity[]>(
`[:find (pull ?b [*])
:where
[?b :block/page ?p]
[?p :block/original-name "${pageName}"]
[?b :block/parent ?parent]
[?parent :block/uuid ?u]
[(str ?u) ?s]
[(= ?s "${parentBlockId}")]
[?b :block/content ?c]
[(clojure.string/includes? ?c "${content}")]]`
)
).flat()

return blocks[0]
}

const fetchOmnivore = async (inBackground = false) => {
const {
syncAt,
Expand Down Expand Up @@ -126,6 +141,7 @@ const fetchOmnivore = async (inBackground = false) => {

const blockTitle = '## 🔖 Articles'
const fetchingTitle = '🚀 Fetching articles ...'
const highlightTitle = '### Highlights'

!inBackground && logseq.App.pushState('page', { name: pageName })

Expand Down Expand Up @@ -183,6 +199,12 @@ const fetchOmnivore = async (inBackground = false) => {
)
const articleBatch: IBatchBlock[] = []
for (const article of articles) {
// render article content
const articleContent = renderArticleContent(
articleTemplate,
article,
preferredDateFormat
)
// filter out notes and redactions
const highlights = article.highlights?.filter(
(h) => h.type === HighlightType.Highlight
Expand Down Expand Up @@ -214,134 +236,89 @@ const fetchOmnivore = async (inBackground = false) => {
}
const highlightBatch: IBatchBlock[] =
highlights?.map((it) => {
// Build content string based on template
// Render highlight content string based on highlight template
const content = renderHighlightContent(
highlightTemplate,
it,
article,
preferredDateFormat
)
const noteChild = it.annotation
? { content: it.annotation }
: undefined
return {
content,
children: noteChild ? [noteChild] : undefined,
properties: {
id: it.id,
},
}
}) || []

let isNewArticle = true
// update existing block if article is already in the page
const existingBlocks = (
await logseq.DB.datascriptQuery<BlockEntity[]>(
`[:find (pull ?b [*])
:where
[?b :block/page ?p]
[?p :block/original-name "${pageName}"]
[?b :block/parent ?parent]
[?parent :block/uuid ?u]
[(str ?u) ?s]
[(= ?s "${targetBlock.uuid}")]
[?b :block/content ?c]
[(clojure.string/includes? ?c "${article.slug}")]]`
)
).flat()
const articleContent = renderArticleContent(
articleTemplate,
article,
preferredDateFormat
// create highlight title block
const highlightTitleBlock: IBatchBlock = {
content: highlightTitle,
children: highlightBatch,
}
// update existing article block if article is already in the page
const existingArticleBlock = await getBlockByContent(
pageName,
targetBlock.uuid,
article.slug
)
if (existingBlocks.length > 0) {
isNewArticle = false
const existingBlock = existingBlocks[0]
// update the first existing block
if (existingBlock.content !== articleContent) {
await logseq.Editor.updateBlock(existingBlock.uuid, articleContent)
if (existingArticleBlock) {
// update the existing article block
if (existingArticleBlock.content !== articleContent) {
await logseq.Editor.updateBlock(
existingArticleBlock.uuid,
articleContent
)
}
// delete the rest of the existing blocks
await deleteBlocks(existingBlocks.slice(1))
// append highlights to existing block
for (const highlight of highlightBatch) {
const existingHighlights = (
await logseq.DB.datascriptQuery<BlockEntity[]>(
`[:find (pull ?b [*])
:where
[?b :block/parent ?p]
[?p :block/uuid ?u]
[(str ?u) ?s]
[(= ?s "${existingBlock.uuid}")]
[?b :block/content ?c]
[(clojure.string/includes? ?c "${
highlight.properties?.id as string
}")]]`
)
).flat()
if (existingHighlights.length > 0) {
const existingHighlight = existingHighlights[0]
// check if highlight title block exists
const existingHighlightTitleBlock = await getBlockByContent(
pageName,
existingArticleBlock.uuid,
highlightTitleBlock.content
)
const parentBlockId = existingHighlightTitleBlock
? existingHighlightTitleBlock.uuid
: existingArticleBlock.uuid
const existingHighlightBlock = await getBlockByContent(
pageName,
parentBlockId,
highlight.properties?.id as string
)
if (existingHighlightBlock) {
// update existing highlight if content is different
existingHighlight.content !== highlight.content &&
(await logseq.Editor.updateBlock(
existingHighlight.uuid,
if (existingHighlightBlock.content !== highlight.content) {
await logseq.Editor.updateBlock(
existingHighlightBlock.uuid,
highlight.content
))

// checking notes
const noteChild = highlight.children?.[0]
if (noteChild) {
const existingNotes = (
await logseq.DB.datascriptQuery<BlockEntity[]>(
`[:find (pull ?b [*])
:where
[?b :block/parent ?p]
[?p :block/uuid ?u]
[(str ?u) ?s]
[(= ?s "${existingHighlight.uuid}")]
[?b :block/content ?c]
[(= ?c "${escapeQuotationMarks(
noteChild.content
)}")]]`
)
).flat()
if (existingNotes.length == 0) {
// append new note
await logseq.Editor.insertBlock(
existingHighlight.uuid,
noteChild.content,
{ sibling: false }
)
}
)
}
} else {
// append new highlight
await logseq.Editor.insertBatchBlock(
existingBlock.uuid,
highlight,
{ sibling: false }
)
// append new highlight to existing article block
await logseq.Editor.insertBatchBlock(parentBlockId, highlight, {
sibling: false,
})
}
}
}

isNewArticle &&
} else {
// append new article block
articleBatch.unshift({
content: articleContent,
children: highlightBatch,
children: highlightBatch.length > 0 ? [highlightTitleBlock] : [], // add highlight title block if there are highlights
})
}
}

articleBatch.length > 0 &&
(await logseq.Editor.insertBatchBlock(targetBlock.uuid, articleBatch, {
if (articleBatch.length > 0) {
await logseq.Editor.insertBatchBlock(targetBlock.uuid, articleBatch, {
before: true,
sibling: false,
}))
})
}
}

// delete blocks where article has been deleted
// delete blocks where article has been deleted from omnivore
for (
let hasNextPage = true, deletedArticles: DeletedArticle[] = [], after = 0;
let hasNextPage = true, deletedArticles: Article[] = [], after = 0;
hasNextPage;
after += size
) {
Expand All @@ -352,26 +329,15 @@ const fetchOmnivore = async (inBackground = false) => {
parseDateTime(syncAt).toISO(),
endpoint
)

for (const deletedArticle of deletedArticles) {
const slug = deletedArticle.node.slug
const existingBlocks = (
await logseq.DB.datascriptQuery<BlockEntity[]>(
`[:find (pull ?b [*])
:where
[?b :block/page ?p]
[?p :block/original-name "${pageName}"]
[?b :block/parent ?parent]
[?parent :block/uuid ?u]
[(str ?u) ?s]
[(= ?s "${targetBlock.uuid}")]
[?b :block/content ?c]
[(clojure.string/includes? ?c "${slug}")]]`
)
).flat()

if (existingBlocks.length > 0) {
await deleteBlocks(existingBlocks)
const existingBlock = await getBlockByContent(
pageName,
targetBlock.uuid,
deletedArticle.slug
)

if (existingBlock) {
await logseq.Editor.removeBlock(existingBlock.uuid)
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/settings/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface HighlightVariables {
highlightUrl: string
dateHighlighted: string
rawDateHighlighted: string
note?: string
}

export type TemplateVariables = ArticleVariables & HighlightVariables
Expand All @@ -48,7 +49,9 @@ date-saved:: {{{dateSaved}}}
date-published:: {{{datePublished}}}
{{/datePublished}}`

export const defaultHighlightTemplate = `> {{{text}}} [⤴️]({{{highlightUrl}}}) {{#labels}} #[[{{{name}}}]] {{/labels}}`
export const defaultHighlightTemplate = `note:: {{{note}}}
> {{{text}}} [⤴️]({{{highlightUrl}}}) {{#labels}} #[[{{{name}}}]] {{/labels}}
`

const buildArticleVariables = (
article: Article,
Expand Down Expand Up @@ -117,6 +120,7 @@ export const renderHighlightContent = (
highlightUrl: `https://omnivore.app/me/${article.slug}#${highlight.id}`,
dateHighlighted,
rawDateHighlighted,
note: highlight.annotation,
}
return Mustache.render(template, highlightVariables)
}

0 comments on commit 6ee5090

Please sign in to comment.