Skip to content

Commit 02c59ca

Browse files
LionChenAhyoban
andauthored
feat: add Cubox integration for saving entries (#3385)
* feat(integration): enhance Cubox integration with auto memo mode - Add Cubox integration with API token support - Add enableCuboxAutoMemo setting for auto memo mode - Add i18n translations for Cubox settings - Support saving selected text as memo when auto memo mode is enabled * update icon * update changelog --------- Co-authored-by: Stephen Zhou <[email protected]>
1 parent 55524b0 commit 02c59ca

File tree

14 files changed

+189
-0
lines changed

14 files changed

+189
-0
lines changed

apps/desktop/changelog/next.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## New Features
44

5+
- Cubox integration (#3385)
6+
57
## Improvements
68

79
## Bug Fixes

apps/desktop/src/renderer/src/hooks/biz/useEntryActions.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ export const useEntryActions = ({ entryId, view }: { entryId: string; view?: Fee
122122
id: COMMAND_ID.integration.saveToReadeck,
123123
onClick: runCmdFn(COMMAND_ID.integration.saveToReadeck, [{ entryId }]),
124124
},
125+
{
126+
id: COMMAND_ID.integration.saveToCubox,
127+
onClick: runCmdFn(COMMAND_ID.integration.saveToCubox, [{ entryId }]),
128+
},
125129
{
126130
id: COMMAND_ID.entry.tip,
127131
onClick: runCmdFn(COMMAND_ID.entry.tip, [

apps/desktop/src/renderer/src/modules/command/commands/id.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const COMMAND_ID = {
2020
saveToObsidian: "integration:save-to-obsidian",
2121
saveToOutline: "integration:save-to-outline",
2222
saveToReadeck: "integration:save-to-readeck",
23+
saveToCubox: "integration:save-to-cubox",
2324
},
2425
list: {
2526
edit: "list:edit",

apps/desktop/src/renderer/src/modules/command/commands/integration.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
SimpleIconsCubox,
23
SimpleIconsEagle,
34
SimpleIconsInstapaper,
45
SimpleIconsObsidian,
@@ -33,6 +34,7 @@ export const useRegisterIntegrationCommands = () => {
3334
useRegisterObsidianCommands()
3435
useRegisterOutlineCommands()
3536
useRegisterReadeckCommands()
37+
useRegisterCuboxCommands()
3638
}
3739

3840
const useRegisterEagleCommands = () => {
@@ -453,3 +455,87 @@ const useRegisterReadeckCommands = () => {
453455
},
454456
)
455457
}
458+
459+
const useRegisterCuboxCommands = () => {
460+
const { t } = useTranslation()
461+
462+
const enableCubox = useIntegrationSettingKey("enableCubox")
463+
const cuboxToken = useIntegrationSettingKey("cuboxToken")
464+
const enableCuboxAutoMemo = useIntegrationSettingKey("enableCuboxAutoMemo")
465+
const cuboxAvailable = enableCubox && !!cuboxToken
466+
467+
const buildUrlRequestBody = (entry: FlatEntryModel) => {
468+
return {
469+
type: "url",
470+
content: entry.entries.url || "",
471+
title: entry.entries.title || "",
472+
description: entry.entries.description || "",
473+
tags: [],
474+
folder: "",
475+
}
476+
}
477+
478+
const buildMemoRequestBody = (entry: FlatEntryModel, selectedText: string) => {
479+
return {
480+
type: "memo",
481+
content: selectedText,
482+
title: entry.entries.title || "",
483+
description: entry.entries.description || "",
484+
tags: [],
485+
folder: "",
486+
source_url: entry.entries.url,
487+
}
488+
}
489+
490+
useRegisterCommandEffect(
491+
!cuboxAvailable
492+
? []
493+
: defineFollowCommand({
494+
id: COMMAND_ID.integration.saveToCubox,
495+
label: t("entry_actions.save_to_cubox"),
496+
icon: <SimpleIconsCubox />,
497+
run: async ({ entryId }) => {
498+
const entry = useEntryStore.getState().flatMapEntries[entryId]
499+
if (!entry) {
500+
toast.error("Failed to save to Cubox: entry is not available", { duration: 3000 })
501+
return
502+
}
503+
try {
504+
tracker.integration({
505+
type: "cubox",
506+
event: "save",
507+
})
508+
509+
const selectedText = window.getSelection()?.toString() || ""
510+
511+
const requestBody =
512+
selectedText && enableCuboxAutoMemo
513+
? buildMemoRequestBody(entry, selectedText)
514+
: buildUrlRequestBody(entry)
515+
516+
await ofetch(cuboxToken, {
517+
method: "POST",
518+
body: requestBody,
519+
headers: {
520+
"Content-Type": "application/json",
521+
},
522+
})
523+
524+
toast.success(t("entry_actions.saved_to_cubox"), {
525+
duration: 3000,
526+
})
527+
} catch (error) {
528+
toast.error(
529+
`${t("entry_actions.failed_to_save_to_cubox")}: ${(error as FetchError)?.message || ""}`,
530+
{
531+
duration: 3000,
532+
},
533+
)
534+
}
535+
},
536+
}),
537+
{
538+
deps: [cuboxAvailable, cuboxToken, enableCuboxAutoMemo],
539+
},
540+
)
541+
}

apps/desktop/src/renderer/src/modules/command/commands/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,18 @@ export type SaveToReadeckCommand = Command<{
120120
fn: (payload: { entryId: string }) => void
121121
}>
122122

123+
export type SaveToCuboxCommand = Command<{
124+
id: typeof COMMAND_ID.integration.saveToCubox
125+
fn: (payload: { entryId: string }) => void
126+
}>
127+
123128
export type IntegrationCommand =
124129
| SaveToEagleCommand
125130
| SaveToReadwiseCommand
126131
| SaveToInstapaperCommand
127132
| SaveToObsidianCommand
128133
| SaveToOutlineCommand
129134
| SaveToReadeckCommand
135+
| SaveToCuboxCommand
130136

131137
export type BasicCommand = EntryCommand | SettingsCommand | IntegrationCommand

apps/desktop/src/renderer/src/modules/settings/tabs/integration.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Divider } from "@follow/components/ui/divider/index.js"
22
import {
3+
SimpleIconsCubox,
34
SimpleIconsEagle,
45
SimpleIconsInstapaper,
56
SimpleIconsObsidian,
@@ -174,6 +175,41 @@ export const SettingIntegration = () => {
174175
type: "password",
175176
description: t("integration.readeck.token.description"),
176177
}),
178+
{
179+
type: "title",
180+
value: (
181+
<span className="flex items-center gap-2 font-bold">
182+
<SimpleIconsCubox />
183+
{t("integration.cubox.title")}
184+
</span>
185+
),
186+
},
187+
defineSettingItem("enableCubox", {
188+
label: t("integration.cubox.enable.label"),
189+
description: t("integration.cubox.enable.description"),
190+
}),
191+
defineSettingItem("cuboxToken", {
192+
label: t("integration.cubox.token.label"),
193+
vertical: true,
194+
type: "password",
195+
description: (
196+
<>
197+
{t("integration.cubox.token.description")}{" "}
198+
<a
199+
target="_blank"
200+
className="underline"
201+
rel="noreferrer noopener"
202+
href="https://cubox.pro/my/settings/extensions"
203+
>
204+
https://cubox.pro/my/settings/extensions
205+
</a>
206+
</>
207+
),
208+
}),
209+
defineSettingItem("enableCuboxAutoMemo", {
210+
label: t("integration.cubox.autoMemo.label"),
211+
description: t("integration.cubox.autoMemo.description"),
212+
}),
177213
BottomTip,
178214
]}
179215
/>

locales/app/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"entry_actions.deleted": "Deleted.",
9090
"entry_actions.export_as_pdf": "Export as PDF",
9191
"entry_actions.failed_to_delete": "Failed to delete.",
92+
"entry_actions.failed_to_save_to_cubox": "Failed to save to Cubox.",
9293
"entry_actions.failed_to_save_to_eagle": "Failed to save to Eagle.",
9394
"entry_actions.failed_to_save_to_instapaper": "Failed to save to Instapaper.",
9495
"entry_actions.failed_to_save_to_obsidian": "Failed to save to Obsidian",
@@ -101,11 +102,13 @@
101102
"entry_actions.open_in_browser": "Open In {{which}}",
102103
"entry_actions.recent_reader": "Recent reader:",
103104
"entry_actions.save_media_to_eagle": "Save Media To Eagle",
105+
"entry_actions.save_to_cubox": "Save to Cubox",
104106
"entry_actions.save_to_instapaper": "Save To Instapaper",
105107
"entry_actions.save_to_obsidian": "Save to Obsidian",
106108
"entry_actions.save_to_outline": "Save To Outline",
107109
"entry_actions.save_to_readeck": "Save To Readeck",
108110
"entry_actions.save_to_readwise": "Save To Readwise",
111+
"entry_actions.saved_to_cubox": "Saved to Cubox",
109112
"entry_actions.saved_to_eagle": "Saved To Eagle.",
110113
"entry_actions.saved_to_instapaper": "Saved To Instapaper.",
111114
"entry_actions.saved_to_obsidian": "Saved to Obsidian",

locales/app/zh-CN.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"entry_actions.deleted": "已删除",
9090
"entry_actions.export_as_pdf": "导出为 PDF",
9191
"entry_actions.failed_to_delete": "删除失败",
92+
"entry_actions.failed_to_save_to_cubox": "保存到 Cubox 失败。",
9293
"entry_actions.failed_to_save_to_eagle": "保存到 Eagle 失败。",
9394
"entry_actions.failed_to_save_to_instapaper": "保存到 Instapaper 失败。",
9495
"entry_actions.failed_to_save_to_obsidian": "保存到 Obsidian 失败。",
@@ -106,6 +107,7 @@
106107
"entry_actions.save_to_outline": "保存到 Outline",
107108
"entry_actions.save_to_readeck": "保存到 Readeck",
108109
"entry_actions.save_to_readwise": "保存到 Readwise",
110+
"entry_actions.saved_to_cubox": "已保存到 Cubox",
109111
"entry_actions.saved_to_eagle": "已保存到 Eagle。",
110112
"entry_actions.saved_to_instapaper": "已保存到 Instapaper。",
111113
"entry_actions.saved_to_obsidian": "已保存到 Obsidian。",

locales/settings/en.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,13 @@
220220
"general.timeline": "Timeline",
221221
"general.unread": "Unread",
222222
"general.voices": "Voices",
223+
"integration.cubox.autoMemo.description": "Automatically use Memo mode when text is selected to save to Cubox.",
224+
"integration.cubox.autoMemo.label": "Auto Memo Mode",
225+
"integration.cubox.enable.description": "Show 'Save to Cubox' button if available.",
226+
"integration.cubox.enable.label": "Enable",
227+
"integration.cubox.title": "Cubox",
228+
"integration.cubox.token.description": "Please enter the complete Cubox API URL, format: https://cubox.pro/c/api/save/xxxxxxxxx. You can get it here: ",
229+
"integration.cubox.token.label": "Cubox API URL",
223230
"integration.eagle.enable.description": "Display 'Save media to Eagle' button when available.",
224231
"integration.eagle.enable.label": "Enable",
225232
"integration.eagle.title": "Eagle",

locales/settings/zh-CN.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,13 @@
220220
"general.timeline": "时间线",
221221
"general.unread": "未读",
222222
"general.voices": "声音",
223+
"integration.cubox.autoMemo.description": "自动使用 Memo 模式保存选中的文本到 Cubox。",
224+
"integration.cubox.autoMemo.label": "自动 Memo 模式",
225+
"integration.cubox.enable.description": "显示「保存到 Cubox」按钮(如果可用)。",
226+
"integration.cubox.enable.label": "启用",
227+
"integration.cubox.title": "Cubox",
228+
"integration.cubox.token.description": "请输入完整的 Cubox API URL,格式如:https://cubox.pro/c/api/save/xxxxxxxxx。你可以在此获取:",
229+
"integration.cubox.token.label": "Cubox API URL",
223230
"integration.eagle.enable.description": "显示「保存到 Eagle」按钮(如果可用)。",
224231
"integration.eagle.enable.label": "启用",
225232
"integration.eagle.title": "Eagle",
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { cn } from "@follow/utils/utils"
2+
import type { SVGProps } from "react"
3+
4+
export function SimpleIconsCubox({ className, ...props }: SVGProps<SVGSVGElement>) {
5+
return (
6+
<svg
7+
width="1em"
8+
height="1em"
9+
viewBox="0 0 100 100"
10+
xmlns="http://www.w3.org/2000/svg"
11+
className={cn("rounded", className)}
12+
{...props}
13+
>
14+
<path d="M0 0 H100 V100 H0 Z" fill="currentColor" />
15+
<path d="M10,100 A40,40 0 0,1 90,100 Z" fill="black" />
16+
17+
<circle cx="40" cy="75" r="8" fill="white" />
18+
<circle cx="42" cy="75" r="3" fill="black" />
19+
20+
<circle cx="60" cy="75" r="8" fill="white" />
21+
<circle cx="62" cy="75" r="3" fill="black" />
22+
</svg>
23+
)
24+
}

packages/components/src/ui/platform-icon/icons.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from "./collections/cubox"
12
export * from "./collections/eagle"
23
export * from "./collections/instapaper"
34
export * from "./collections/obsidian"

packages/shared/src/settings/defaults.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ export const defaultIntegrationSettings: IntegrationSettings = {
114114
enableReadeck: false,
115115
readeckEndpoint: "",
116116
readeckToken: "",
117+
118+
// cubox
119+
enableCubox: false,
120+
cuboxToken: "",
121+
enableCuboxAutoMemo: false,
117122
}
118123

119124
export const defaultSettings = {

packages/shared/src/settings/interface.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,9 @@ export interface IntegrationSettings {
100100
enableReadeck: boolean
101101
readeckEndpoint: string
102102
readeckToken: string
103+
104+
// cubox
105+
enableCubox: boolean
106+
cuboxToken: string
107+
enableCuboxAutoMemo: boolean
103108
}

0 commit comments

Comments
 (0)