refactor: sunset Omnivore integration (#1953)

* refactor: remove Omnivore integration and related settings

* refactor: remove Omnivore integration from localization files

* refactor: remove Omnivore integration from settings and command types

* refactor: remove Omnivore entries from localization files
This commit is contained in:
Whitewater 2024-12-01 18:56:12 +08:00 committed by GitHub
parent f7eca73f5b
commit 605c3d66b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1 additions and 660 deletions

View File

@ -14,11 +14,6 @@ export const createDefaultSettings = (): IntegrationSettings => ({
instapaperUsername: "",
instapaperPassword: "",
// omnivore
enableOmnivore: false,
omnivoreEndpoint: "",
omnivoreToken: "",
// obsidian
enableObsidian: false,
obsidianVaultPath: "",

View File

@ -104,10 +104,6 @@ export const useEntryActions = ({ entryId, view }: { entryId: string; view?: Fee
id: COMMAND_ID.integration.saveToInstapaper,
onClick: runCmdFn(COMMAND_ID.integration.saveToInstapaper, [{ entryId }]),
},
{
id: COMMAND_ID.integration.saveToOmnivore,
onClick: runCmdFn(COMMAND_ID.integration.saveToOmnivore, [{ entryId }]),
},
{
id: COMMAND_ID.integration.saveToObsidian,
onClick: runCmdFn(COMMAND_ID.integration.saveToObsidian, [{ entryId }]),

View File

@ -1,438 +0,0 @@
import {
SimpleIconsEagle,
SimpleIconsInstapaper,
SimpleIconsObsidian,
SimpleIconsOmnivore,
SimpleIconsOutline,
SimpleIconsReadwise,
} from "@follow/components/ui/platform-icon/icons.js"
import type { CombinedEntryModel } from "@follow/models/types"
import { IN_ELECTRON } from "@follow/shared/constants"
import { useMutation, useQuery } from "@tanstack/react-query"
import type { FetchError } from "ofetch"
import { ofetch } from "ofetch"
import type { ReactNode } from "react"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { toast } from "sonner"
import { getReadabilityContent, getReadabilityStatus, ReadabilityStatus } from "~/atoms/readability"
import { useIntegrationSettingValue } from "~/atoms/settings/integration"
import { tipcClient } from "~/lib/client"
import { parseHtml } from "~/lib/parse-html"
import type { FlatEntryModel } from "~/store/entry"
import { getFeedById, useFeedById } from "~/store/feed"
import { useInboxById } from "~/store/inbox"
export const useIntegrationActions = ({
view,
entry,
}: {
view?: number
entry?: FlatEntryModel | null
}) => {
const { t } = useTranslation()
const feed = useFeedById(entry?.feedId, (feed) => {
return {
type: feed.type,
ownerUserId: feed.ownerUserId,
id: feed.id,
}
})
const inbox = useInboxById(entry?.inboxId)
const populatedEntry = useMemo(() => {
if (!entry) return null
if (!feed?.id && !inbox?.id) return null
return {
...entry,
feeds: feed ? getFeedById(feed.id) : undefined,
inboxes: inbox,
} as CombinedEntryModel
}, [entry, feed, inbox])
const {
enableEagle,
enableReadwise,
enableInstapaper,
enableOmnivore,
enableObsidian,
enableOutline,
readwiseToken,
instapaperUsername,
instapaperPassword,
omnivoreToken,
omnivoreEndpoint,
obsidianVaultPath,
outlineToken,
outlineEndpoint,
outlineCollection,
} = useIntegrationSettingValue()
const isObsidianEnabled = enableObsidian && !!obsidianVaultPath
const saveToObsidian = useMutation({
mutationKey: ["save-to-obsidian"],
mutationFn: async (data: {
url: string
title: string
content: string
author: string
publishedAt: string
vaultPath: string
}) => {
return await tipcClient?.saveToObsidian(data)
},
onSuccess: (data) => {
if (data?.success) {
toast.success(t("entry_actions.saved_to_obsidian"), {
duration: 3000,
})
} else {
toast.error(`${t("entry_actions.failed_to_save_to_obsidian")}: ${data?.error}`, {
duration: 3000,
})
}
},
})
const checkEagle = useQuery({
queryKey: ["check-eagle"],
enabled: ELECTRON && enableEagle && !!entry?.entries.url && view !== undefined,
queryFn: async () => {
try {
await ofetch("http://localhost:41595")
return true
} catch (error: unknown) {
return (error as FetchError).data?.code === 401
}
},
refetchOnMount: false,
refetchOnWindowFocus: false,
})
const items = useMemo(() => {
if (!populatedEntry || view === undefined) return []
const getEntryContentAsMarkdown = async () => {
const isReadabilityReady =
getReadabilityStatus()[populatedEntry.entries.id] === ReadabilityStatus.SUCCESS
const content =
(isReadabilityReady
? getReadabilityContent()[populatedEntry.entries.id].content
: populatedEntry.entries.content) || ""
const [toMarkdown, toMdast, gfmTableToMarkdown] = await Promise.all([
import("mdast-util-to-markdown").then((m) => m.toMarkdown),
import("hast-util-to-mdast").then((m) => m.toMdast),
import("mdast-util-gfm-table").then((m) => m.gfmTableToMarkdown),
])
return toMarkdown(toMdast(parseHtml(content).hastTree), {
extensions: [gfmTableToMarkdown()],
})
}
const items: {
key: string
className?: string
shortcut?: string
name: string
icon?: ReactNode
hide?: boolean
active?: boolean
disabled?: boolean
onClick: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void
}[] = [
{
name: t("entry_actions.save_media_to_eagle"),
icon: <SimpleIconsEagle />,
key: "saveToEagle",
hide:
!enableEagle ||
(checkEagle.isLoading ? true : !checkEagle.data) ||
!populatedEntry.entries.media?.length,
onClick: async () => {
if (!populatedEntry.entries.url || !populatedEntry.entries.media?.length) {
return
}
window.analytics?.capture("integration", {
type: "eagle",
event: "save",
})
const response = await tipcClient?.saveToEagle({
url: populatedEntry.entries.url,
mediaUrls: populatedEntry.entries.media.map((m) => m.url),
})
if (response?.status === "success") {
toast.success(t("entry_actions.saved_to_eagle"), {
duration: 3000,
})
} else {
toast.error(t("entry_actions.failed_to_save_to_eagle"), {
duration: 3000,
})
}
},
},
{
name: t("entry_actions.save_to_readwise"),
icon: <SimpleIconsReadwise />,
key: "saveToReadwise",
hide: !enableReadwise || !readwiseToken || !populatedEntry.entries.url,
onClick: async () => {
try {
window.analytics?.capture("integration", {
type: "readwise",
event: "save",
})
const data = await ofetch("https://readwise.io/api/v3/save/", {
method: "POST",
headers: {
Authorization: `Token ${readwiseToken}`,
},
body: {
url: populatedEntry.entries.url,
html: populatedEntry.entries.content || undefined,
title: populatedEntry.entries.title || undefined,
author: populatedEntry.entries.author || undefined,
summary: populatedEntry.entries.description || undefined,
published_date: populatedEntry.entries.publishedAt || undefined,
image_url: populatedEntry.entries.media?.[0]?.url || undefined,
saved_using: "Follow",
},
})
toast.success(
<>
{t("entry_actions.saved_to_readwise")},{" "}
<a target="_blank" className="underline" href={data.url}>
view
</a>
</>,
{
duration: 3000,
},
)
} catch {
toast.error(t("entry_actions.failed_to_save_to_readwise"), {
duration: 3000,
})
}
},
},
{
name: t("entry_actions.save_to_instapaper"),
icon: <SimpleIconsInstapaper />,
key: "saveToInstapaper",
hide:
!enableInstapaper ||
!instapaperPassword ||
!instapaperUsername ||
!populatedEntry.entries.url,
onClick: async () => {
try {
window.analytics?.capture("integration", {
type: "instapaper",
event: "save",
})
const data = await ofetch("https://www.instapaper.com/api/add", {
query: {
url: populatedEntry.entries.url,
title: populatedEntry.entries.title,
},
method: "POST",
headers: {
Authorization: `Basic ${btoa(`${instapaperUsername}:${instapaperPassword}`)}`,
},
parseResponse: JSON.parse,
})
toast.success(
<>
{t("entry_actions.saved_to_instapaper")},{" "}
<a
target="_blank"
className="underline"
href={`https://www.instapaper.com/read/${data.bookmark_id}`}
>
view
</a>
</>,
{
duration: 3000,
},
)
} catch {
toast.error(t("entry_actions.failed_to_save_to_instapaper"), {
duration: 3000,
})
}
},
},
{
name: t("entry_actions.save_to_omnivore"),
icon: <SimpleIconsOmnivore />,
key: "saveToomnivore",
hide: !enableOmnivore || !omnivoreToken || !omnivoreEndpoint || !populatedEntry.entries.url,
onClick: async () => {
const saveUrlQuery = `
mutation SaveUrl($input: SaveUrlInput!) {
saveUrl(input: $input) {
... on SaveSuccess {
url
clientRequestId
}
... on SaveError {
errorCodes
message
}
}
}
`
window.analytics?.capture("integration", {
type: "omnivore",
event: "save",
})
try {
const data = await ofetch(omnivoreEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: omnivoreToken,
},
body: {
query: saveUrlQuery,
variables: {
input: {
url: populatedEntry.entries.url,
source: "Follow",
clientRequestId: globalThis.crypto.randomUUID(),
publishedAt: new Date(populatedEntry.entries.publishedAt),
},
},
},
})
toast.success(
<>
{t("entry_actions.saved_to_omnivore")},{" "}
<a target="_blank" className="underline" href={data.data.saveUrl.url}>
view
</a>
</>,
{
duration: 3000,
},
)
} catch {
toast.error(t("entry_actions.failed_to_save_to_omnivore"), {
duration: 3000,
})
}
},
},
{
name: t("entry_actions.save_to_obsidian"),
icon: <SimpleIconsObsidian />,
key: "saveToObsidian",
hide: !isObsidianEnabled || !populatedEntry?.entries?.url || !IN_ELECTRON,
onClick: async () => {
if (!isObsidianEnabled || !populatedEntry?.entries?.url || !IN_ELECTRON) return
const markdownContent = await getEntryContentAsMarkdown()
window.analytics?.capture("integration", {
type: "obsidian",
event: "save",
})
saveToObsidian.mutate({
url: populatedEntry.entries.url,
title: populatedEntry.entries.title || "",
content: markdownContent,
author: populatedEntry.entries.author || "",
publishedAt: populatedEntry.entries.publishedAt || "",
vaultPath: obsidianVaultPath,
})
},
},
{
name: t("entry_actions.save_to_outline"),
icon: <SimpleIconsOutline />,
key: "saveToOutline",
hide:
!IN_ELECTRON ||
!enableOutline ||
!outlineToken ||
!outlineEndpoint ||
!outlineCollection ||
!populatedEntry.entries.title,
onClick: async () => {
try {
const request = async (method: string, params: Record<string, unknown>) => {
return await ofetch(`${outlineEndpoint.replace(/\/$/, "")}/${method}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${outlineToken}`,
},
body: params,
})
}
let collectionId = outlineCollection
if (!/^[a-f\d]{8}(?:-[a-f\d]{4}){3}-[a-f\d]{12}$/i.test(collectionId)) {
const collection = await request("collections.info", {
id: collectionId,
})
collectionId = collection.data.id
}
const markdownContent = await getEntryContentAsMarkdown()
await request("documents.create", {
title: populatedEntry.entries.title,
text: markdownContent,
collectionId,
publish: true,
})
toast.success(t("entry_actions.saved_to_outline"), {
duration: 3000,
})
} catch {
toast.error(t("entry_actions.failed_to_save_to_outline"), {
duration: 3000,
})
}
},
},
]
return items
}, [
populatedEntry,
view,
t,
enableEagle,
checkEagle.isLoading,
checkEagle.data,
enableReadwise,
readwiseToken,
enableInstapaper,
instapaperPassword,
instapaperUsername,
enableOmnivore,
omnivoreToken,
omnivoreEndpoint,
isObsidianEnabled,
enableOutline,
outlineToken,
outlineEndpoint,
outlineCollection,
saveToObsidian,
obsidianVaultPath,
])
return {
items,
}
}

View File

@ -19,7 +19,6 @@ export const COMMAND_ID = {
saveToEagle: "integration:save-to-eagle",
saveToReadwise: "integration:save-to-readwise",
saveToInstapaper: "integration:save-to-instapaper",
saveToOmnivore: "integration:save-to-omnivore",
saveToObsidian: "integration:save-to-obsidian",
saveToOutline: "integration:save-to-outline",
},

View File

@ -2,7 +2,6 @@ import {
SimpleIconsEagle,
SimpleIconsInstapaper,
SimpleIconsObsidian,
SimpleIconsOmnivore,
SimpleIconsOutline,
SimpleIconsReadwise,
} from "@follow/components/ui/platform-icon/icons.js"
@ -29,7 +28,6 @@ export const useRegisterIntegrationCommands = () => {
useRegisterEagleCommands()
useRegisterReadwiseCommands()
useRegisterInstapaperCommands()
useRegisterOmnivoreCommands()
useRegisterObsidianCommands()
useRegisterOutlineCommands()
}
@ -236,90 +234,6 @@ const useRegisterInstapaperCommands = () => {
)
}
const useRegisterOmnivoreCommands = () => {
const { t } = useTranslation()
const enableOmnivore = useIntegrationSettingKey("enableOmnivore")
const omnivoreToken = useIntegrationSettingKey("omnivoreToken")
const omnivoreEndpoint = useIntegrationSettingKey("omnivoreEndpoint")
const isOmnivoreAvailable = enableOmnivore && !!omnivoreToken && !!omnivoreEndpoint
useRegisterCommandEffect(
!isOmnivoreAvailable
? []
: defineFollowCommand({
id: COMMAND_ID.integration.saveToOmnivore,
label: t("entry_actions.save_to_omnivore"),
icon: <SimpleIconsOmnivore />,
run: async ({ entryId }) => {
const entry = useEntryStore.getState().flatMapEntries[entryId]
if (!entry) {
toast.error("Failed to save to Omnivore: entry is not available", { duration: 3000 })
return
}
const saveUrlQuery = `
mutation SaveUrl($input: SaveUrlInput!) {
saveUrl(input: $input) {
... on SaveSuccess {
url
clientRequestId
}
... on SaveError {
errorCodes
message
}
}
}
`
window.analytics?.capture("integration", {
type: "omnivore",
event: "save",
})
try {
const data = await ofetch(omnivoreEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: omnivoreToken,
},
body: {
query: saveUrlQuery,
variables: {
input: {
url: entry.entries.url,
source: "Follow",
clientRequestId: globalThis.crypto.randomUUID(),
publishedAt: new Date(entry.entries.publishedAt),
},
},
},
})
toast.success(
<>
{t("entry_actions.saved_to_omnivore")},{" "}
<a target="_blank" className="underline" href={data.data.saveUrl.url}>
view
</a>
</>,
{
duration: 3000,
},
)
} catch {
toast.error(t("entry_actions.failed_to_save_to_omnivore"), {
duration: 3000,
})
}
},
}),
{
deps: [isOmnivoreAvailable],
},
)
}
const getEntryContentAsMarkdown = async (entry: FlatEntryModel) => {
const isReadabilityReady = getReadabilityStatus()[entry.entries.id] === ReadabilityStatus.SUCCESS
const content =

View File

@ -106,11 +106,6 @@ export type SaveToInstapaperCommand = Command<{
fn: (payload: { entryId: string }) => void
}>
export type SaveToOmnivoreCommand = Command<{
id: typeof COMMAND_ID.integration.saveToOmnivore
fn: (payload: { entryId: string }) => void
}>
export type SaveToObsidianCommand = Command<{
id: typeof COMMAND_ID.integration.saveToObsidian
fn: (payload: { entryId: string }) => void
@ -125,7 +120,6 @@ export type IntegrationCommand =
| SaveToEagleCommand
| SaveToReadwiseCommand
| SaveToInstapaperCommand
| SaveToOmnivoreCommand
| SaveToObsidianCommand
| SaveToOutlineCommand

View File

@ -3,7 +3,6 @@ import {
SimpleIconsEagle,
SimpleIconsInstapaper,
SimpleIconsObsidian,
SimpleIconsOmnivore,
SimpleIconsOutline,
SimpleIconsReadwise,
} from "@follow/components/ui/platform-icon/icons.js"
@ -103,56 +102,6 @@ export const SettingIntegration = () => {
labelClassName: "w-[150px]",
},
}),
{
type: "title",
value: (
<span className="flex items-center gap-2 font-bold">
<SimpleIconsOmnivore />
{t("integration.omnivore.title")}
</span>
),
},
defineSettingItem("enableOmnivore", {
label: t("integration.omnivore.enable.label"),
description: t("integration.omnivore.enable.description"),
}),
defineSettingItem("omnivoreEndpoint", {
label: t("integration.omnivore.endpoint.label"),
vertical: true,
description: (
<>
{t("integration.omnivore.endpoint.description")}{" "}
<a
target="_blank"
className="underline"
rel="noreferrer noopener"
href="https://api-prod.omnivore.app/api/graphql"
>
https://api-prod.omnivore.app/api/graphql
</a>
.
</>
),
}),
defineSettingItem("omnivoreToken", {
label: t("integration.omnivore.token.label"),
vertical: true,
type: "password",
description: (
<>
{t("integration.omnivore.token.description")}{" "}
<a
target="_blank"
className="underline"
rel="noreferrer noopener"
href="https://omnivore.app/settings/api"
>
omnivore.app/settings/api
</a>
.
</>
),
}),
{
type: "title",
value: (

View File

@ -111,7 +111,6 @@
"entry_actions.failed_to_save_to_eagle": "Failed to save to Eagle.",
"entry_actions.failed_to_save_to_instapaper": "Failed to save to Instapaper.",
"entry_actions.failed_to_save_to_obsidian": "Failed to save to Obsidian",
"entry_actions.failed_to_save_to_omnivore": "Failed to save to Omnivore.",
"entry_actions.failed_to_save_to_outline": "Failed to save to Outline.",
"entry_actions.failed_to_save_to_readwise": "Failed to save to Readwise.",
"entry_actions.mark_as_read": "Mark as Read",
@ -121,13 +120,11 @@
"entry_actions.save_media_to_eagle": "Save Media To Eagle",
"entry_actions.save_to_instapaper": "Save To Instapaper",
"entry_actions.save_to_obsidian": "Save to Obsidian",
"entry_actions.save_to_omnivore": "Save To Omnivore",
"entry_actions.save_to_outline": "Save To Outline",
"entry_actions.save_to_readwise": "Save To Readwise",
"entry_actions.saved_to_eagle": "Saved To Eagle.",
"entry_actions.saved_to_instapaper": "Saved To Instapaper.",
"entry_actions.saved_to_obsidian": "Saved to Obsidian",
"entry_actions.saved_to_omnivore": "Saved To Omnivore.",
"entry_actions.saved_to_outline": "Saved To Outline.",
"entry_actions.saved_to_readwise": "Saved To Readwise.",
"entry_actions.share": "Share",

View File

@ -111,7 +111,6 @@
"entry_actions.failed_to_save_to_eagle": "Eagle への保存に失敗しました。",
"entry_actions.failed_to_save_to_instapaper": "Instapaper への保存に失敗しました。",
"entry_actions.failed_to_save_to_obsidian": "Obsidian への保存に失敗しました。",
"entry_actions.failed_to_save_to_omnivore": "Omnivore への保存に失敗しました。",
"entry_actions.failed_to_save_to_outline": "Outline への保存に失敗しました。",
"entry_actions.failed_to_save_to_readwise": "Readwise への保存に失敗しました。",
"entry_actions.mark_as_read": "既読にする",
@ -121,13 +120,11 @@
"entry_actions.save_media_to_eagle": "メディアを Eagle に保存",
"entry_actions.save_to_instapaper": "Instapaper に保存",
"entry_actions.save_to_obsidian": "Obsidian に保存",
"entry_actions.save_to_omnivore": "Omnivore に保存",
"entry_actions.save_to_outline": "Outline に保存",
"entry_actions.save_to_readwise": "Readwise に保存",
"entry_actions.saved_to_eagle": "Eagle に保存されました。",
"entry_actions.saved_to_instapaper": "Instapaper に保存されました。",
"entry_actions.saved_to_obsidian": "Obsidian に保存されました。",
"entry_actions.saved_to_omnivore": "Omnivore に保存されました。",
"entry_actions.saved_to_outline": "Outline に保存されました。",
"entry_actions.saved_to_readwise": "Readwise に保存されました。",
"entry_actions.share": "共有",
@ -244,7 +241,7 @@
"new_user_guide.step.behavior.unread_question.option2": "バランス: ホバーしたりスクロールが終わると自動で既読にします。",
"new_user_guide.step.behavior.unread_question.option3": "コンサバ: クリックしたときのみ既読にします。",
"new_user_guide.step.features.actions.description": "アクションルールはフィードごとに違ったアクションを実行できます。\n - AI を使った要約や翻訳 \n - エントリーの読み方の設定 \n - 新着エントリーの通知の有効/無効 \n - 特定のエントリーの書き換えやブロック \n - 新着エントリーの Webhook アドレスへの送信など",
"new_user_guide.step.features.integration.description": "統合により、他のサービスにエントリーを保存することができます。現在対応しているサービスは以下のとおりです:\n- Eagle \n- Readwise \n- Instapaper \n- Obsidian \n- Omnivore \n- Outline",
"new_user_guide.step.features.integration.description": "統合により、他のサービスにエントリーを保存することができます。現在対応しているサービスは以下のとおりです:\n- Eagle \n- Readwise \n- Instapaper \n- Obsidian \n- Outline",
"new_user_guide.step.migrate.profile": "プロファイルをセットアップ",
"new_user_guide.step.migrate.title": "OPML ファイルを構成する",
"new_user_guide.step.migrate.wallet": "ウォレットをチェック",

View File

@ -111,7 +111,6 @@
"entry_actions.failed_to_save_to_eagle": "保存到 Eagle 失败。",
"entry_actions.failed_to_save_to_instapaper": "保存到 Instapaper 失败。",
"entry_actions.failed_to_save_to_obsidian": "保存到 Obsidian 失败。",
"entry_actions.failed_to_save_to_omnivore": "保存到 Omnivore 失败。",
"entry_actions.failed_to_save_to_outline": "保存到 Outline 失败。",
"entry_actions.failed_to_save_to_readwise": "保存到 Readwise 失败。",
"entry_actions.mark_as_read": "标记为已读",
@ -121,13 +120,11 @@
"entry_actions.save_media_to_eagle": "保存到 Eagle",
"entry_actions.save_to_instapaper": "保存到 Instapaper",
"entry_actions.save_to_obsidian": "保存到 Obsidian",
"entry_actions.save_to_omnivore": "保存到 Omnivore",
"entry_actions.save_to_outline": "保存到 Outline",
"entry_actions.save_to_readwise": "保存到 Readwise",
"entry_actions.saved_to_eagle": "已保存到 Eagle。",
"entry_actions.saved_to_instapaper": "已保存到 Instapaper。",
"entry_actions.saved_to_obsidian": "已保存到 Obsidian。",
"entry_actions.saved_to_omnivore": "已保存到 Omnivore。",
"entry_actions.saved_to_outline": "已保存到 Outline。",
"entry_actions.saved_to_readwise": "已保存到 Readwise。",
"entry_actions.share": "分享",

View File

@ -111,7 +111,6 @@
"entry_actions.failed_to_save_to_eagle": "無法儲存至 Eagle",
"entry_actions.failed_to_save_to_instapaper": "無法儲存至 Instapaper",
"entry_actions.failed_to_save_to_obsidian": "無法儲存至 Obsidian ",
"entry_actions.failed_to_save_to_omnivore": "無法儲存至 Omnivore",
"entry_actions.failed_to_save_to_outline": "無法儲存至 Outline",
"entry_actions.failed_to_save_to_readwise": "無法儲存至 Readwise",
"entry_actions.mark_as_read": "標記為已讀",
@ -121,13 +120,11 @@
"entry_actions.save_media_to_eagle": "儲存媒體至 Eagle",
"entry_actions.save_to_instapaper": "儲存至 Instapaper",
"entry_actions.save_to_obsidian": "儲存至 Obsidian",
"entry_actions.save_to_omnivore": "儲存至 Omnivore",
"entry_actions.save_to_outline": "儲存至 Outline",
"entry_actions.save_to_readwise": "儲存至 Readwise",
"entry_actions.saved_to_eagle": "已儲存至 Eagle",
"entry_actions.saved_to_instapaper": "已儲存至 Instapaper",
"entry_actions.saved_to_obsidian": "已儲存至 Obsidian",
"entry_actions.saved_to_omnivore": "已儲存至 Omnivore",
"entry_actions.saved_to_outline": "已儲存至 Outline",
"entry_actions.saved_to_readwise": "已儲存至 Readwise",
"entry_actions.share": "分享",

View File

@ -111,7 +111,6 @@
"entry_actions.failed_to_save_to_eagle": "無法儲存到 Eagle。",
"entry_actions.failed_to_save_to_instapaper": "無法儲存到 Instapaper。",
"entry_actions.failed_to_save_to_obsidian": "儲存到 Obsidian 失敗",
"entry_actions.failed_to_save_to_omnivore": "無法儲存到 Omnivore。",
"entry_actions.failed_to_save_to_outline": "無法儲存到 Outline。",
"entry_actions.failed_to_save_to_readwise": "無法儲存到 Readwise。",
"entry_actions.mark_as_read": "標記為已讀",
@ -121,13 +120,11 @@
"entry_actions.save_media_to_eagle": "儲存媒體至 Eagle",
"entry_actions.save_to_instapaper": "儲存到 Instapaper",
"entry_actions.save_to_obsidian": "儲存到 Obsidian",
"entry_actions.save_to_omnivore": "儲存到 Omnivore",
"entry_actions.save_to_outline": "儲存到 Outline",
"entry_actions.save_to_readwise": "儲存到 Readwise",
"entry_actions.saved_to_eagle": "已儲存至 Eagle。",
"entry_actions.saved_to_instapaper": "已儲存至 Instapaper。",
"entry_actions.saved_to_obsidian": "已儲存到 Obsidian",
"entry_actions.saved_to_omnivore": "已儲存至 Omnivore。",
"entry_actions.saved_to_outline": "已儲存至 Outline。",
"entry_actions.saved_to_readwise": "已儲存至 Readwise。",
"entry_actions.share": "分享",

View File

@ -175,13 +175,6 @@
"integration.obsidian.title": "Obsidian",
"integration.obsidian.vaultPath.description": "The path to your Obsidian vault.",
"integration.obsidian.vaultPath.label": "Obsidian Vault Path",
"integration.omnivore.enable.description": "Display 'Save to Omnivore' button when available.",
"integration.omnivore.enable.label": "Enable",
"integration.omnivore.endpoint.description": "Omnivore Official Endpoint is: ",
"integration.omnivore.endpoint.label": "Omnivore Endpoint",
"integration.omnivore.title": "Omnivore",
"integration.omnivore.token.description": "You can get it here: ",
"integration.omnivore.token.label": "Omnivore API Key",
"integration.outline.collection.description": "The UUID or urlId of the collection where the documents is saved.",
"integration.outline.collection.label": "Outline Collection",
"integration.outline.enable.description": "Display 'Save to Outline' button when available.",

View File

@ -172,13 +172,6 @@
"integration.obsidian.title": "Obsidian",
"integration.obsidian.vaultPath.description": "Obsidian vaultのパスを指定します",
"integration.obsidian.vaultPath.label": "Obsidian Vault パス",
"integration.omnivore.enable.description": "利用可能な場合、'Omnivore に保存' ボタンを表示します。",
"integration.omnivore.enable.label": "有効化",
"integration.omnivore.endpoint.description": "Omnivore の公式 Endpoint",
"integration.omnivore.endpoint.label": "Omnivore Endpoint",
"integration.omnivore.title": "Omnivore",
"integration.omnivore.token.description": "こちらで取得できます:",
"integration.omnivore.token.label": "Omnivore API Key",
"integration.outline.collection.description": "ドキュメントに保存された UUID または urlId コレクションです。",
"integration.outline.collection.label": "Outline コレクション",
"integration.outline.enable.description": "利用可能なとき 'Outline に保存' ボタンを表示します。",

View File

@ -172,13 +172,6 @@
"integration.obsidian.title": "Obsidian",
"integration.obsidian.vaultPath.description": "你的 Obsidian 仓库的路径",
"integration.obsidian.vaultPath.label": "Obsidian 仓库路径",
"integration.omnivore.enable.description": "显示「保存到 Omnivore」按钮如果可用。",
"integration.omnivore.enable.label": "启用",
"integration.omnivore.endpoint.description": "Omnivore 官方接口",
"integration.omnivore.endpoint.label": "Omnivore 访问域名",
"integration.omnivore.title": "Omnivore",
"integration.omnivore.token.description": "在这里获取 API 密钥",
"integration.omnivore.token.label": "Omnivore API 密钥",
"integration.outline.collection.description": "保存文档的文档集的 UUID 或 urlId。",
"integration.outline.collection.label": "Outline 文档集",
"integration.outline.enable.description": "显示「保存到 Outline」按钮如果可用。",

View File

@ -168,13 +168,6 @@
"integration.obsidian.title": "Obsidian",
"integration.obsidian.vaultPath.description": "你的 Obsidian 儲存庫的路徑",
"integration.obsidian.vaultPath.label": "Obsidian 儲存庫路徑",
"integration.omnivore.enable.description": "顯示「儲存到 Omnivore」按钮如果可用",
"integration.omnivore.enable.label": "啟用",
"integration.omnivore.endpoint.description": "Omnivore 官方端口",
"integration.omnivore.endpoint.label": "Omnivore 端口",
"integration.omnivore.title": "Omnivore",
"integration.omnivore.token.description": "你可以在這裡獲取",
"integration.omnivore.token.label": "Omnivore API 密鑰",
"integration.outline.collection.description": "儲存文件的集合的 UUID 或 urlId",
"integration.outline.collection.label": "Outline Collection",
"integration.outline.enable.description": "顯示「儲存到 Outline」按钮如果可用",

View File

@ -160,13 +160,6 @@
"integration.obsidian.title": "Obsidian",
"integration.obsidian.vaultPath.description": "您的 Obsidian 儲存庫的路徑",
"integration.obsidian.vaultPath.label": "Obsidian 儲存庫路徑",
"integration.omnivore.enable.description": "顯示\"保存到 Omnivore\"按钮(如果可用)。",
"integration.omnivore.enable.label": "啟用",
"integration.omnivore.endpoint.description": "Omnivore 官方端口",
"integration.omnivore.endpoint.label": "Omnivore 端口",
"integration.omnivore.title": "Omnivore",
"integration.omnivore.token.description": "你可以在這裡獲取",
"integration.omnivore.token.label": "Omnivore API 密鑰",
"integration.outline.collection.description": "儲存文檔的文檔集的 UUID 或 urlId",
"integration.outline.collection.label": "Outline 文檔集",
"integration.outline.enable.description": "顯示\"儲存到 Outline\"按鈕(如果可用)",

View File

@ -1,12 +0,0 @@
import type { SVGProps } from "react"
export function SimpleIconsOmnivore(props: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" {...props}>
<path
fill="currentColor"
d="M15.9 7.801c0 .507-.123 1.12-.248 1.656v.004l-.001.003a2.87 2.87 0 0 1-2.793 2.186h-.036c-1.625 0-2.649-1.334-2.649-2.828v-2.14l-1.21 1.794-.067.055a1.404 1.404 0 0 1-1.793 0l-.065-.053-1.248-1.82v4.414H4.6V6.268c0-.91 1.078-1.439 1.794-.802l.055.048 1.46 2.13a.21.21 0 0 0 .179 0l1.43-2.119.065-.054c.68-.567 1.78-.138 1.78.815v2.536c0 .971.619 1.638 1.46 1.638h.035c.78 0 1.45-.527 1.636-1.277.125-.534.216-1.026.216-1.378-.017-3.835-3.262-6.762-7.188-6.498-3.311.23-5.986 2.905-6.216 6.216A6.705 6.705 0 0 0 8 14.693v1.19a7.895 7.895 0 0 1-7.882-8.44C.39 3.536 3.536.39 7.44.118 12.017-.19 15.88 3.242 15.9 7.8z"
/>
</svg>
)
}

View File

@ -1,7 +1,6 @@
export * from "./collections/eagle"
export * from "./collections/instapaper"
export * from "./collections/obsidian"
export * from "./collections/omnivore"
export * from "./collections/outline"
export * from "./collections/readwise"
export * from "./collections/rss3"

View File

@ -64,11 +64,6 @@ export interface IntegrationSettings {
instapaperUsername: string
instapaperPassword: string
// omnivore
enableOmnivore: boolean
omnivoreEndpoint: string
omnivoreToken: string
// obsidian
enableObsidian: boolean
obsidianVaultPath: string