Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 83 additions & 9 deletions frontend/app/view/webview/webview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import { BlockNodeModel } from "@/app/block/blocktypes";
import { getApi, getSettingsKeyAtom, openLink } from "@/app/store/global";
import { getApi, getBlockMetaKeyAtom, getSettingsKeyAtom, openLink } from "@/app/store/global";
import { getSimpleControlShiftAtom } from "@/app/store/keymodel";
import { ObjectService } from "@/app/store/services";
import { RpcApi } from "@/app/store/wshclientapi";
Expand Down Expand Up @@ -49,13 +49,13 @@ export class WebViewModel implements ViewModel {
mediaPlaying: PrimitiveAtom<boolean>;
mediaMuted: PrimitiveAtom<boolean>;
modifyExternalUrl?: (url: string) => string;
domReady: PrimitiveAtom<boolean>;

constructor(blockId: string, nodeModel: BlockNodeModel) {
this.nodeModel = nodeModel;
this.viewType = "web";
this.blockId = blockId;
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);

this.url = atom();
const defaultUrlAtom = getSettingsKeyAtom("web:defaulturl");
this.homepageUrl = atom((get) => {
Expand All @@ -71,6 +71,7 @@ export class WebViewModel implements ViewModel {
this.viewName = atom("Web");
this.urlInputRef = createRef<HTMLInputElement>();
this.webviewRef = createRef<WebviewTag>();
this.domReady = atom(false);

this.mediaPlaying = atom(false);
this.mediaMuted = atom(false);
Expand Down Expand Up @@ -339,7 +340,7 @@ export class WebViewModel implements ViewModel {
const defaultSearchAtom = getSettingsKeyAtom("web:defaultsearch");
const searchTemplate = globalStore.get(defaultSearchAtom);
const nextUrl = this.ensureUrlScheme(newUrl, searchTemplate);
console.log("webview loadUrl", reason, nextUrl, "cur=", this.webviewRef?.current.getURL());
console.log("webview loadUrl", reason, nextUrl, "cur=", this.webviewRef.current.getURL());
if (!this.webviewRef.current) {
return;
}
Expand Down Expand Up @@ -414,7 +415,7 @@ export class WebViewModel implements ViewModel {
return true;
}
if (checkKeyPressed(e, "Cmd:r")) {
this.webviewRef?.current?.reload();
this.webviewRef.current?.reload();
return true;
}
if (checkKeyPressed(e, "Cmd:ArrowLeft")) {
Expand All @@ -428,7 +429,61 @@ export class WebViewModel implements ViewModel {
return false;
}

setZoomFactor(factor: number | null) {
// null is ok (will reset to default)
if (factor != null && factor < 0.1) {
factor = 0.1;
}
if (factor != null && factor > 5) {
factor = 5;
}
const domReady = globalStore.get(this.domReady);
if (!domReady) {
return;
}
this.webviewRef.current?.setZoomFactor(factor || 1);
RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("block", this.blockId),
meta: { "web:zoom": factor }, // allow null so we can remove the zoom factor here
});
}

getSettingsMenuItems(): ContextMenuItem[] {
const zoomSubMenu: ContextMenuItem[] = [];
let curZoom = 1;
if (globalStore.get(this.domReady)) {
curZoom = this.webviewRef.current?.getZoomFactor() || 1;
}
const model = this; // for the closure to work (this is getting unset)
function makeZoomFactorMenuItem(label: string, factor: number): ContextMenuItem {
return {
label: label,
type: "checkbox",
click: () => {
model.setZoomFactor(factor);
},
checked: curZoom == factor,
};
}
zoomSubMenu.push({
label: "Reset",
click: () => {
model.setZoomFactor(null);
},
});
zoomSubMenu.push(makeZoomFactorMenuItem("25%", 0.25));
zoomSubMenu.push(makeZoomFactorMenuItem("50%", 0.5));
zoomSubMenu.push(makeZoomFactorMenuItem("70%", 0.7));
zoomSubMenu.push(makeZoomFactorMenuItem("80%", 0.8));
zoomSubMenu.push(makeZoomFactorMenuItem("90%", 0.9));
zoomSubMenu.push(makeZoomFactorMenuItem("100%", 1));
zoomSubMenu.push(makeZoomFactorMenuItem("110%", 1.1));
zoomSubMenu.push(makeZoomFactorMenuItem("120%", 1.2));
zoomSubMenu.push(makeZoomFactorMenuItem("130%", 1.3));
zoomSubMenu.push(makeZoomFactorMenuItem("150%", 1.5));
zoomSubMenu.push(makeZoomFactorMenuItem("175%", 1.75));
zoomSubMenu.push(makeZoomFactorMenuItem("200%", 2));

return [
{
label: "Set Block Homepage",
Expand All @@ -441,6 +496,10 @@ export class WebViewModel implements ViewModel {
{
type: "separator",
},
{
label: "Set Zoom Factor",
submenu: zoomSubMenu,
},
{
label: this.webviewRef.current?.isDevToolsOpened() ? "Close DevTools" : "Open DevTools",
click: () => {
Expand Down Expand Up @@ -476,12 +535,13 @@ const WebView = memo(({ model, onFailLoad }: WebViewProps) => {
let metaUrl = blockData?.meta?.url || defaultUrl;
metaUrl = model.ensureUrlScheme(metaUrl, defaultSearch);
const metaUrlRef = useRef(metaUrl);
const zoomFactor = useAtomValue(getBlockMetaKeyAtom(model.blockId, "web:zoom")) || 1;

// The initial value of the block metadata URL when the component first renders. Used to set the starting src value for the webview.
const [metaUrlInitial] = useState(metaUrl);

const [webContentsId, setWebContentsId] = useState(null);
const [domReady, setDomReady] = useState(false);
const domReady = useAtomValue(model.domReady);

const [errorText, setErrorText] = useState("");

Expand Down Expand Up @@ -510,13 +570,27 @@ const WebView = memo(({ model, onFailLoad }: WebViewProps) => {
}

useEffect(() => {
if (model.webviewRef.current && domReady) {
return () => {
globalStore.set(model.domReady, false);
};
}, []);

useEffect(() => {
if (model.webviewRef.current == null || !domReady) {
return;
}
try {
const wcId = model.webviewRef.current.getWebContentsId?.();
if (wcId) {
setWebContentsId(wcId);
if (model.webviewRef.current.getZoomFactor() != zoomFactor) {
model.webviewRef.current.setZoomFactor(zoomFactor);
}
}
} catch (e) {
console.error("Failed to get webcontentsid / setzoomlevel (webview)", e);
}
}, [model.webviewRef.current, domReady]);
}, [model.webviewRef.current, domReady, zoomFactor]);

// Load a new URL if the block metadata is updated.
useEffect(() => {
Expand Down Expand Up @@ -560,7 +634,7 @@ const WebView = memo(({ model, onFailLoad }: WebViewProps) => {
console.error(errorMessage);
setErrorText(errorMessage);
if (onFailLoad) {
const curUrl = model.webviewRef?.current.getURL();
const curUrl = model.webviewRef.current.getURL();
onFailLoad(curUrl);
}
}
Expand All @@ -573,7 +647,7 @@ const WebView = memo(({ model, onFailLoad }: WebViewProps) => {
getApi().setWebviewFocus(null);
};
const handleDomReady = () => {
setDomReady(true);
globalStore.set(model.domReady, true);
setBgColor();
};
const handleMediaPlaying = () => {
Expand Down
1 change: 1 addition & 0 deletions frontend/types/gotypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ declare global {
"term:scrollback"?: number;
"term:vdomblockid"?: string;
"term:vdomtoolbarblockid"?: string;
"web:zoom"?: number;
"vdom:*"?: boolean;
"vdom:initialized"?: boolean;
"vdom:correlationid"?: string;
Expand Down
2 changes: 2 additions & 0 deletions pkg/waveobj/metaconsts.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ const (
MetaKey_TermVDomSubBlockId = "term:vdomblockid"
MetaKey_TermVDomToolbarBlockId = "term:vdomtoolbarblockid"

MetaKey_WebZoom = "web:zoom"

MetaKey_VDomClear = "vdom:*"
MetaKey_VDomInitialized = "vdom:initialized"
MetaKey_VDomCorrelationId = "vdom:correlationid"
Expand Down
2 changes: 2 additions & 0 deletions pkg/waveobj/wtypemeta.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ type MetaTSType struct {
TermVDomSubBlockId string `json:"term:vdomblockid,omitempty"`
TermVDomToolbarBlockId string `json:"term:vdomtoolbarblockid,omitempty"`

WebZoom float64 `json:"web:zoom,omitempty"`

VDomClear bool `json:"vdom:*,omitempty"`
VDomInitialized bool `json:"vdom:initialized,omitempty"`
VDomCorrelationId string `json:"vdom:correlationid,omitempty"`
Expand Down