mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +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"); | ||||
|     if (inAppHelpPage) { | ||||
|         openInAppHelpFromUrl(inAppHelpPage); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export async function openInAppHelpFromUrl(inAppHelpPage: string) { | ||||
|     // Dynamic import to avoid import issues in tests. | ||||
|     const appContext = (await import("../components/app_context.js")).default; | ||||
|     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. | ||||
|         helpSubcontext.setNote(targetNote, { viewScope }); | ||||
|     } | ||||
|         return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function initHelpButtons($el: JQuery<HTMLElement> | JQuery<Window>) { | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import type { InputHTMLAttributes, RefObject } from "preact/compat"; | ||||
| import FormText from "./FormText"; | ||||
|  | ||||
| interface FormTextBoxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "value"> { | ||||
|     id?: string; | ||||
| @@ -11,9 +10,11 @@ interface FormTextBoxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, " | ||||
| export default function FormTextBox({ inputRef, className, type, currentValue, onChange, ...rest}: FormTextBoxProps) { | ||||
|     if (type === "number" && currentValue) { | ||||
|         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); | ||||
|         } else if (max && currentValue > max) { | ||||
|         } else if (max && currentValueNum > parseInt(String(max), 10)) { | ||||
|             currentValue = String(max); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import { OptionNames } from "@triliumnext/commons"; | ||||
| import options, { type OptionValue } from "../../services/options"; | ||||
| import utils, { reloadFrontendApp } from "../../services/utils"; | ||||
| import Component from "../../components/component"; | ||||
| import server from "../../services/server"; | ||||
|  | ||||
| type TriliumEventHandler<T extends EventNames> = (data: EventData<T>) => void; | ||||
| 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. | ||||
|  *  | ||||
|   | ||||
| @@ -13,7 +13,6 @@ import PasswordOptions from "./options/password/password.js"; | ||||
| import ProtectedSessionTimeoutOptions from "./options/password/protected_session_timeout.js"; | ||||
| import EtapiOptions from "./options/etapi.js"; | ||||
| import BackupOptions from "./options/backup.js"; | ||||
| import SyncOptions from "./options/sync.js"; | ||||
| import SearchEngineOptions from "./options/other/search_engine.js"; | ||||
| import TrayOptions from "./options/other/tray.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 AdvancedSettings from "./options/advanced.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"> | ||||
|     <style> | ||||
| @@ -102,9 +102,7 @@ const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", ((typeof NoteContextA | ||||
|     _optionsBackup: [ | ||||
|         BackupOptions | ||||
|     ], | ||||
|     _optionsSync: [ | ||||
|         SyncOptions | ||||
|     ], | ||||
|     _optionsSync: <SyncOptions />, | ||||
|     _optionsAi: [AiSettingsOptions], | ||||
|     _optionsOther: [ | ||||
|         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