mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	feat(react/settings): port sync options
This commit is contained in:
		| @@ -374,6 +374,11 @@ async function openInAppHelp($button: JQuery<HTMLElement>) { | |||||||
|  |  | ||||||
|     const inAppHelpPage = $button.attr("data-in-app-help"); |     const inAppHelpPage = $button.attr("data-in-app-help"); | ||||||
|     if (inAppHelpPage) { |     if (inAppHelpPage) { | ||||||
|  |         openInAppHelpFromUrl(inAppHelpPage); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function openInAppHelpFromUrl(inAppHelpPage: string) { | ||||||
|     // Dynamic import to avoid import issues in tests. |     // Dynamic import to avoid import issues in tests. | ||||||
|     const appContext = (await import("../components/app_context.js")).default; |     const appContext = (await import("../components/app_context.js")).default; | ||||||
|     const activeContext = appContext.tabManager.getActiveContext(); |     const activeContext = appContext.tabManager.getActiveContext(); | ||||||
| @@ -399,8 +404,6 @@ async function openInAppHelp($button: JQuery<HTMLElement>) { | |||||||
|         // There is already a help window open, make sure it opens on the right note. |         // There is already a help window open, make sure it opens on the right note. | ||||||
|         helpSubcontext.setNote(targetNote, { viewScope }); |         helpSubcontext.setNote(targetNote, { viewScope }); | ||||||
|     } |     } | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| function initHelpButtons($el: JQuery<HTMLElement> | JQuery<Window>) { | function initHelpButtons($el: JQuery<HTMLElement> | JQuery<Window>) { | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| import type { InputHTMLAttributes, RefObject } from "preact/compat"; | import type { InputHTMLAttributes, RefObject } from "preact/compat"; | ||||||
| import FormText from "./FormText"; |  | ||||||
|  |  | ||||||
| interface FormTextBoxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "value"> { | interface FormTextBoxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "value"> { | ||||||
|     id?: string; |     id?: string; | ||||||
| @@ -11,9 +10,11 @@ interface FormTextBoxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, " | |||||||
| export default function FormTextBox({ inputRef, className, type, currentValue, onChange, ...rest}: FormTextBoxProps) { | export default function FormTextBox({ inputRef, className, type, currentValue, onChange, ...rest}: FormTextBoxProps) { | ||||||
|     if (type === "number" && currentValue) { |     if (type === "number" && currentValue) { | ||||||
|         const { min, max } = rest; |         const { min, max } = rest; | ||||||
|         if (min && currentValue < min) { |         console.log(currentValue , min, max); | ||||||
|  |         const currentValueNum = parseInt(currentValue, 10); | ||||||
|  |         if (min && currentValueNum < parseInt(String(min), 10)) { | ||||||
|             currentValue = String(min); |             currentValue = String(min); | ||||||
|         } else if (max && currentValue > max) { |         } else if (max && currentValueNum > parseInt(String(max), 10)) { | ||||||
|             currentValue = String(max); |             currentValue = String(max); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import { OptionNames } from "@triliumnext/commons"; | |||||||
| import options, { type OptionValue } from "../../services/options"; | import options, { type OptionValue } from "../../services/options"; | ||||||
| import utils, { reloadFrontendApp } from "../../services/utils"; | import utils, { reloadFrontendApp } from "../../services/utils"; | ||||||
| import Component from "../../components/component"; | import Component from "../../components/component"; | ||||||
|  | import server from "../../services/server"; | ||||||
|  |  | ||||||
| type TriliumEventHandler<T extends EventNames> = (data: EventData<T>) => void; | type TriliumEventHandler<T extends EventNames> = (data: EventData<T>) => void; | ||||||
| const registeredHandlers: Map<Component, Map<EventNames, TriliumEventHandler<any>[]>> = new Map(); | const registeredHandlers: Map<Component, Map<EventNames, TriliumEventHandler<any>[]>> = new Map(); | ||||||
| @@ -150,6 +151,19 @@ export function useTriliumOptionJson<T>(name: OptionNames): [ T, (newValue: T) = | |||||||
|     ]; |     ]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function useTriliumOptions<T extends OptionNames>(...names: T[]) { | ||||||
|  |     const values: Record<string, string> = {}; | ||||||
|  |     for (const name of names) { | ||||||
|  |         values[name] = options.get(name); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const setValue = (newValues: Record<T, string>) => server.put<void>("options", newValues); | ||||||
|  |     return [ | ||||||
|  |         values as Record<T, string>, | ||||||
|  |         setValue | ||||||
|  |     ] as const; | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Generates a unique name via a random alphanumeric string of a fixed length. |  * Generates a unique name via a random alphanumeric string of a fixed length. | ||||||
|  *  |  *  | ||||||
|   | |||||||
| @@ -13,7 +13,6 @@ import PasswordOptions from "./options/password/password.js"; | |||||||
| import ProtectedSessionTimeoutOptions from "./options/password/protected_session_timeout.js"; | import ProtectedSessionTimeoutOptions from "./options/password/protected_session_timeout.js"; | ||||||
| import EtapiOptions from "./options/etapi.js"; | import EtapiOptions from "./options/etapi.js"; | ||||||
| import BackupOptions from "./options/backup.js"; | import BackupOptions from "./options/backup.js"; | ||||||
| import SyncOptions from "./options/sync.js"; |  | ||||||
| import SearchEngineOptions from "./options/other/search_engine.js"; | import SearchEngineOptions from "./options/other/search_engine.js"; | ||||||
| import TrayOptions from "./options/other/tray.js"; | import TrayOptions from "./options/other/tray.js"; | ||||||
| import NoteErasureTimeoutOptions from "./options/other/note_erasure_timeout.js"; | import NoteErasureTimeoutOptions from "./options/other/note_erasure_timeout.js"; | ||||||
| @@ -40,6 +39,7 @@ import { renderReactWidget } from "../react/ReactBasicWidget.jsx"; | |||||||
| import ImageSettings from "./options/images.jsx"; | import ImageSettings from "./options/images.jsx"; | ||||||
| import AdvancedSettings from "./options/advanced.jsx"; | import AdvancedSettings from "./options/advanced.jsx"; | ||||||
| import InternationalizationOptions from "./options/i18n.jsx"; | import InternationalizationOptions from "./options/i18n.jsx"; | ||||||
|  | import SyncOptions from "./options/sync.jsx"; | ||||||
|  |  | ||||||
| const TPL = /*html*/`<div class="note-detail-content-widget note-detail-printable"> | const TPL = /*html*/`<div class="note-detail-content-widget note-detail-printable"> | ||||||
|     <style> |     <style> | ||||||
| @@ -102,9 +102,7 @@ const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", ((typeof NoteContextA | |||||||
|     _optionsBackup: [ |     _optionsBackup: [ | ||||||
|         BackupOptions |         BackupOptions | ||||||
|     ], |     ], | ||||||
|     _optionsSync: [ |     _optionsSync: <SyncOptions />, | ||||||
|         SyncOptions |  | ||||||
|     ], |  | ||||||
|     _optionsAi: [AiSettingsOptions], |     _optionsAi: [AiSettingsOptions], | ||||||
|     _optionsOther: [ |     _optionsOther: [ | ||||||
|         SearchEngineOptions, |         SearchEngineOptions, | ||||||
|   | |||||||
| @@ -1,100 +0,0 @@ | |||||||
| import server from "../../../services/server.js"; |  | ||||||
| import toastService from "../../../services/toast.js"; |  | ||||||
| import OptionsWidget from "./options_widget.js"; |  | ||||||
| import { t } from "../../../services/i18n.js"; |  | ||||||
| import type { OptionMap } from "@triliumnext/commons"; |  | ||||||
|  |  | ||||||
| const TPL = /*html*/` |  | ||||||
| <div class="options-section"> |  | ||||||
|     <h4 style="margin-top: 0px;">${t("sync_2.config_title")}</h4> |  | ||||||
|  |  | ||||||
|     <form class="sync-setup-form"> |  | ||||||
|         <div class="form-group"> |  | ||||||
|             <label for="sync-server-host" >${t("sync_2.server_address")}</label> |  | ||||||
|             <input id="sync-server-host" class="sync-server-host form-control" placeholder="https://<host>:<port>"> |  | ||||||
|         </div> |  | ||||||
|  |  | ||||||
|         <div class="form-group"> |  | ||||||
|             <label for="sync-proxy form-control" >${t("sync_2.proxy_label")}</label> |  | ||||||
|             <input id="sync-proxy form-control" class="sync-proxy form-control" placeholder="https://<host>:<port>"> |  | ||||||
|  |  | ||||||
|             <p class="form-text"><strong>${t("sync_2.note")}:</strong> ${t("sync_2.note_description")}</p> |  | ||||||
|             <p class="form-text">${t("sync_2.special_value_description")}</p> |  | ||||||
|         </div> |  | ||||||
|  |  | ||||||
|         <div class="form-group"> |  | ||||||
|             <label for="sync-server-timeout">${t("sync_2.timeout")}</label> |  | ||||||
|             <label class="input-group tn-number-unit-pair"> |  | ||||||
|                 <input id="sync-server-timeout" class="sync-server-timeout form-control" min="1" max="10000000" type="number" style="text-align: left;"> |  | ||||||
|                 <span class="input-group-text">${t("sync_2.timeout_unit")}</span> |  | ||||||
|             </label> |  | ||||||
|         </div> |  | ||||||
|  |  | ||||||
|         <div style="display: flex; justify-content: space-between;"> |  | ||||||
|             <button class="btn btn-primary">${t("sync_2.save")}</button> |  | ||||||
|  |  | ||||||
|             <button class="btn btn-secondary" type="button" data-help-page="synchronization.html">${t("sync_2.help")}</button> |  | ||||||
|         </div> |  | ||||||
|     </form> |  | ||||||
| </div> |  | ||||||
|  |  | ||||||
| <div class="options-section"> |  | ||||||
|     <h4>${t("sync_2.test_title")}</h4> |  | ||||||
|  |  | ||||||
|     <p>${t("sync_2.test_description")}</p> |  | ||||||
|  |  | ||||||
|     <button class="test-sync-button btn btn-secondary">${t("sync_2.test_button")}</button> |  | ||||||
| </div>`; |  | ||||||
|  |  | ||||||
| // TODO: Deduplicate |  | ||||||
| interface TestResponse { |  | ||||||
|     success: boolean; |  | ||||||
|     message: string; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default class SyncOptions extends OptionsWidget { |  | ||||||
|  |  | ||||||
|     private $form!: JQuery<HTMLElement>; |  | ||||||
|     private $syncServerHost!: JQuery<HTMLElement>; |  | ||||||
|     private $syncServerTimeout!: JQuery<HTMLElement>; |  | ||||||
|     private $syncProxy!: JQuery<HTMLElement>; |  | ||||||
|     private $testSyncButton!: JQuery<HTMLElement>; |  | ||||||
|  |  | ||||||
|     doRender() { |  | ||||||
|         this.$widget = $(TPL); |  | ||||||
|  |  | ||||||
|         this.$form = this.$widget.find(".sync-setup-form"); |  | ||||||
|         this.$syncServerHost = this.$widget.find(".sync-server-host"); |  | ||||||
|         this.$syncServerTimeout = this.$widget.find(".sync-server-timeout"); |  | ||||||
|         this.$syncProxy = this.$widget.find(".sync-proxy"); |  | ||||||
|         this.$testSyncButton = this.$widget.find(".test-sync-button"); |  | ||||||
|  |  | ||||||
|         this.$form.on("submit", () => this.save()); |  | ||||||
|  |  | ||||||
|         this.$testSyncButton.on("click", async () => { |  | ||||||
|             const result = await server.post<TestResponse>("sync/test"); |  | ||||||
|  |  | ||||||
|             if (result.success) { |  | ||||||
|                 toastService.showMessage(result.message); |  | ||||||
|             } else { |  | ||||||
|                 toastService.showError(t("sync_2.handshake_failed", { message: result.message })); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     optionsLoaded(options: OptionMap) { |  | ||||||
|         this.$syncServerHost.val(options.syncServerHost); |  | ||||||
|         this.$syncServerTimeout.val(options.syncServerTimeout); |  | ||||||
|         this.$syncProxy.val(options.syncProxy); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     save() { |  | ||||||
|         this.updateMultipleOptions({ |  | ||||||
|             syncServerHost: String(this.$syncServerHost.val()), |  | ||||||
|             syncServerTimeout: String(this.$syncServerTimeout.val()), |  | ||||||
|             syncProxy: String(this.$syncProxy.val()) |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										62
									
								
								apps/client/src/widgets/type_widgets/options/sync.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								apps/client/src/widgets/type_widgets/options/sync.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | import { useRef } from "preact/hooks"; | ||||||
|  | import { t } from "../../../services/i18n"; | ||||||
|  | import { openInAppHelpFromUrl } from "../../../services/utils"; | ||||||
|  | import Button from "../../react/Button"; | ||||||
|  | import FormGroup from "../../react/FormGroup"; | ||||||
|  | import FormTextBox, { FormTextBoxWithUnit } from "../../react/FormTextBox"; | ||||||
|  | import RawHtml from "../../react/RawHtml"; | ||||||
|  | import OptionsSection from "./components/OptionsSection"; | ||||||
|  | import { useTriliumOptions } from "../../react/hooks"; | ||||||
|  |  | ||||||
|  | export default function SyncOptions() { | ||||||
|  |     const [ options, setOptions ] = useTriliumOptions("syncServerHost", "syncServerTimeout", "syncProxy"); | ||||||
|  |     const syncServerHost = useRef(options.syncServerHost); | ||||||
|  |     const syncServerTimeout = useRef(options.syncServerTimeout); | ||||||
|  |     const syncProxy = useRef(options.syncProxy); | ||||||
|  |  | ||||||
|  |     return ( | ||||||
|  |         <OptionsSection title={t("sync_2.config_title")}> | ||||||
|  |             <form onSubmit={(e) => { | ||||||
|  |                 setOptions({ | ||||||
|  |                     syncServerHost: syncServerHost.current, | ||||||
|  |                     syncServerTimeout: syncServerTimeout.current, | ||||||
|  |                     syncProxy: syncProxy.current | ||||||
|  |                 }); | ||||||
|  |                 e.preventDefault(); | ||||||
|  |             }}> | ||||||
|  |                 <FormGroup label={t("sync_2.server_address")}> | ||||||
|  |                     <FormTextBox | ||||||
|  |                         name="sync-server-host" | ||||||
|  |                         placeholder="https://<host>:<port>" | ||||||
|  |                         currentValue={syncServerHost.current} onChange={(newValue) => syncServerHost.current = newValue} | ||||||
|  |                     /> | ||||||
|  |                 </FormGroup> | ||||||
|  |  | ||||||
|  |                 <FormGroup label={t("sync_2.proxy_label")} description={<> | ||||||
|  |                     <strong>{t("sync_2.note")}:</strong> {t("sync_2.note_description")}<br/> | ||||||
|  |                     <RawHtml html={t("sync_2.special_value_description")} /></>} | ||||||
|  |                 > | ||||||
|  |                     <FormTextBox | ||||||
|  |                         name="sync-proxy" | ||||||
|  |                         placeholder="https://<host>:<port>" | ||||||
|  |                         currentValue={syncProxy.current} onChange={(newValue) => syncProxy.current = newValue} | ||||||
|  |                     /> | ||||||
|  |                 </FormGroup> | ||||||
|  |  | ||||||
|  |                 <FormGroup label={t("sync_2.timeout")}> | ||||||
|  |                     <FormTextBoxWithUnit | ||||||
|  |                         name="sync-server-timeout" | ||||||
|  |                         min={1} max={10000000} type="number" | ||||||
|  |                         unit={t("sync_2.timeout_unit")} | ||||||
|  |                         currentValue={syncServerTimeout.current} onChange={(newValue) => syncServerTimeout.current = newValue} | ||||||
|  |                     /> | ||||||
|  |                 </FormGroup> | ||||||
|  |  | ||||||
|  |                 <div style={{ display: "flex", justifyContent: "spaceBetween"}}> | ||||||
|  |                     <Button text={t("sync_2.save")} primary /> | ||||||
|  |                     <Button text={t("sync_2.help")} onClick={() => openInAppHelpFromUrl("cbkrhQjrkKrh")} /> | ||||||
|  |                 </div> | ||||||
|  |             </form> | ||||||
|  |         </OptionsSection> | ||||||
|  |     ) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user