mirror of
https://github.com/zadam/trilium.git
synced 2026-02-20 20:54:26 +01:00
feat(web_view): add a screen if web view is disabled
This commit is contained in:
parent
964633f426
commit
4b8d341e00
@ -168,6 +168,42 @@ function isAffecting(attrRow: AttributeRow, affectedNote: FNote | null | undefin
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles whether a dangerous attribute is enabled or not. When an attribute is disabled, its name is prefixed with `disabled:`.
|
||||
*
|
||||
* Note that this work for non-dangerous attributes as well.
|
||||
*
|
||||
* @param note the note whose attribute to change.
|
||||
* @param type the type of dangerous attribute (label or relation).
|
||||
* @param name the name of the dangerous attribute.
|
||||
* @param willEnable whether to enable or disable the attribute.
|
||||
* @returns a promise that will resolve when the request to the server completes.
|
||||
*/
|
||||
async function toggleDangerousAttribute(note: FNote, type: "label" | "relation", name: string, willEnable: boolean) {
|
||||
const attr = note.getOwnedAttribute(type, name) ?? note.getOwnedAttribute(type, `disabled:${name}`);
|
||||
if (!attr) return;
|
||||
const baseName = getNameWithoutDangerousPrefix(attr.name);
|
||||
const newName = willEnable ? baseName : `disabled:${baseName}`;
|
||||
if (newName === attr.name) return;
|
||||
|
||||
// We are adding and removing afterwards to avoid a flicker (because for a moment there would be no active content attribute anymore) because the operations are done in sequence and not atomically.
|
||||
if (attr.type === "label") {
|
||||
await setLabel(note.noteId, newName, attr.value);
|
||||
} else {
|
||||
await setRelation(note.noteId, newName, attr.value);
|
||||
}
|
||||
await removeAttributeById(note.noteId, attr.attributeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of an attribute without the `disabled:` prefix, or the same name if it's not disabled.
|
||||
* @param name the name of an attribute.
|
||||
* @returns the name without the `disabled:` prefix.
|
||||
*/
|
||||
function getNameWithoutDangerousPrefix(name: string) {
|
||||
return name.startsWith("disabled:") ? name.substring(9) : name;
|
||||
}
|
||||
|
||||
export default {
|
||||
addLabel,
|
||||
setLabel,
|
||||
@ -177,5 +213,7 @@ export default {
|
||||
removeAttributeById,
|
||||
removeOwnedLabelByName,
|
||||
removeOwnedRelationByName,
|
||||
isAffecting
|
||||
isAffecting,
|
||||
toggleDangerousAttribute,
|
||||
getNameWithoutDangerousPrefix
|
||||
};
|
||||
|
||||
@ -1075,7 +1075,9 @@
|
||||
"url_placeholder": "Enter or paste the website address, for example https://triliumnotes.org",
|
||||
"create_button": "Create Web View",
|
||||
"invalid_url_title": "Invalid address",
|
||||
"invalid_url_message": "Insert a valid web address, for example https://triliumnotes.org."
|
||||
"invalid_url_message": "Insert a valid web address, for example https://triliumnotes.org.",
|
||||
"disabled_description": "This web view was imported from an external source. To help protect you from phishing or malicious content, it isn’t loading automatically. You can enable it if you trust the source.",
|
||||
"disabled_button_enable": "Enable web view"
|
||||
},
|
||||
"backend_log": {
|
||||
"refresh": "Refresh"
|
||||
|
||||
@ -206,31 +206,15 @@ function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentI
|
||||
const attrs = note.getOwnedAttributes()
|
||||
.filter(attr => {
|
||||
if (attr.isInheritable) return false;
|
||||
const baseName = getNameWithoutPrefix(attr.name);
|
||||
const baseName = attributes.getNameWithoutDangerousPrefix(attr.name);
|
||||
return DANGEROUS_ATTRIBUTES.some(item => item.name === baseName && item.type === attr.type);
|
||||
});
|
||||
|
||||
for (const attr of attrs) {
|
||||
const baseName = getNameWithoutPrefix(attr.name);
|
||||
const newName = willEnable ? baseName : `disabled:${baseName}`;
|
||||
if (newName === attr.name) continue;
|
||||
|
||||
// We are adding and removing afterwards to avoid a flicker (because for a moment there would be no active content attribute anymore) because the operations are done in sequence and not atomically.
|
||||
if (attr.type === "label") {
|
||||
await attributes.setLabel(note.noteId, newName, attr.value);
|
||||
} else {
|
||||
await attributes.setRelation(note.noteId, newName, attr.value);
|
||||
}
|
||||
await attributes.removeAttributeById(note.noteId, attr.attributeId);
|
||||
}
|
||||
await Promise.all(attrs.map(a => attributes.toggleDangerousAttribute(note, a.type, a.name, willEnable)));
|
||||
}}
|
||||
/>;
|
||||
}
|
||||
|
||||
function getNameWithoutPrefix(name: string) {
|
||||
return name.startsWith("disabled:") ? name.substring(9) : name;
|
||||
}
|
||||
|
||||
function useActiveContentInfo(note: FNote | null | undefined) {
|
||||
const [ info, setInfo ] = useState<ActiveContentInfo | null>(null);
|
||||
|
||||
|
||||
@ -32,4 +32,8 @@
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
.tn-link {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,23 +3,32 @@ import "./WebView.css";
|
||||
import { useCallback, useState } from "preact/hooks";
|
||||
|
||||
import FNote from "../../entities/fnote";
|
||||
import attributes from "../../services/attributes";
|
||||
import { t } from "../../services/i18n";
|
||||
import toast from "../../services/toast";
|
||||
import utils from "../../services/utils";
|
||||
import utils, { openInAppHelpFromUrl } from "../../services/utils";
|
||||
import Button from "../react/Button";
|
||||
import FormGroup from "../react/FormGroup";
|
||||
import FormTextBox from "../react/FormTextBox";
|
||||
import { useNoteLabel } from "../react/hooks";
|
||||
import LinkButton from "../react/LinkButton";
|
||||
import { TypeWidgetProps } from "./type_widget";
|
||||
|
||||
const isElectron = utils.isElectron();
|
||||
|
||||
export default function WebView({ note }: TypeWidgetProps) {
|
||||
const [ webViewSrc ] = useNoteLabel(note, "webViewSrc");
|
||||
const [ disabledWebViewSrc ] = useNoteLabel(note, "disabled:webViewSrc");
|
||||
|
||||
return (webViewSrc
|
||||
? <WebViewContent src={webViewSrc} />
|
||||
: <SetupWebView note={note} />
|
||||
);
|
||||
if (disabledWebViewSrc) {
|
||||
return <DisabledWebView note={note} url={disabledWebViewSrc} />;
|
||||
}
|
||||
|
||||
if (!webViewSrc) {
|
||||
return <SetupWebView note={note} />;
|
||||
}
|
||||
|
||||
return <WebViewContent src={webViewSrc} />;
|
||||
}
|
||||
|
||||
function WebViewContent({ src }: { src: string }) {
|
||||
@ -48,24 +57,56 @@ function SetupWebView({note}: {note: FNote}) {
|
||||
setSrcLabel(url);
|
||||
}, [ setSrcLabel ]);
|
||||
|
||||
return <div class="web-view-setup-form">
|
||||
<form class="tn-centered-form" onSubmit={() => submit(src)}>
|
||||
<span className="bx bx-globe-alt form-icon" />
|
||||
return (
|
||||
<div class="web-view-setup-form">
|
||||
<form class="tn-centered-form" onSubmit={() => submit(src)}>
|
||||
<span className="bx bx-globe-alt form-icon" />
|
||||
|
||||
<FormGroup name="web-view-src-detail" label={t("web_view_setup.title")}>
|
||||
<input className="form-control"
|
||||
type="text"
|
||||
value={src}
|
||||
placeholder={t("web_view_setup.url_placeholder")}
|
||||
onChange={(e) => {setSrc((e.target as HTMLInputElement)?.value);}}
|
||||
<FormGroup name="web-view-src-detail" label={t("web_view_setup.title")}>
|
||||
<input className="form-control"
|
||||
type="text"
|
||||
value={src}
|
||||
placeholder={t("web_view_setup.url_placeholder")}
|
||||
onChange={(e) => {setSrc((e.target as HTMLInputElement)?.value);}}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<Button
|
||||
text={t("web_view_setup.create_button")}
|
||||
primary
|
||||
keyboardShortcut="Enter"
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<Button
|
||||
text={t("web_view_setup.create_button")}
|
||||
primary
|
||||
keyboardShortcut="Enter"
|
||||
/>
|
||||
</form>
|
||||
</div>;
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DisabledWebView({ note, url }: { note: FNote, url: string }) {
|
||||
return (
|
||||
<div class="web-view-setup-form">
|
||||
<form class="tn-centered-form">
|
||||
<span className="bx bx-globe-alt form-icon" />
|
||||
|
||||
<FormGroup name="web-view-src-detail" label={t("web_view_setup.disabled_description")}>
|
||||
<FormTextBox
|
||||
type="url"
|
||||
currentValue={url}
|
||||
disabled
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<Button
|
||||
text={t("web_view_setup.disabled_button_enable")}
|
||||
icon="bx bx-check-shield"
|
||||
onClick={() => attributes.toggleDangerousAttribute(note, "label", "webViewSrc", true)}
|
||||
primary
|
||||
/>
|
||||
|
||||
<LinkButton
|
||||
text="Learn more"
|
||||
onClick={() => openInAppHelpFromUrl("1vHRoWCEjj0L")}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -61,6 +61,7 @@ type Labels = {
|
||||
|
||||
// Note-type specific
|
||||
webViewSrc: string;
|
||||
"disabled:webViewSrc": string;
|
||||
readOnly: boolean;
|
||||
mapType: string;
|
||||
mapRootNoteId: string;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user