From 09b143cb08835f92f6c645cfd935cf87e8cebacb Mon Sep 17 00:00:00 2001 From: reya Date: Mon, 15 Apr 2024 13:30:55 +0700 Subject: [PATCH] feat: auto resize mini webview when main webview resized --- apps/desktop2/src/components/col.tsx | 43 +++++++++++----- apps/desktop2/src/components/repost.tsx | 1 + apps/desktop2/src/components/text.tsx | 1 + apps/desktop2/src/routes/$account.home.tsx | 59 ++++++++++++++------- apps/desktop2/src/routes/auth/settings.tsx | 9 ++++ apps/desktop2/src/routes/newsfeed.tsx | 5 ++ packages/ui/src/note/buttons/downvote.tsx | 60 ---------------------- packages/ui/src/note/buttons/pin.tsx | 50 +++++++++--------- packages/ui/src/note/buttons/reaction.tsx | 11 ---- packages/ui/src/note/buttons/reply.tsx | 5 +- packages/ui/src/note/buttons/upvote.tsx | 60 ---------------------- packages/ui/src/note/index.ts | 3 +- src-tauri/src/commands/window.rs | 19 +++++++ src-tauri/src/main.rs | 3 +- 14 files changed, 135 insertions(+), 194 deletions(-) delete mode 100644 packages/ui/src/note/buttons/downvote.tsx delete mode 100644 packages/ui/src/note/buttons/reaction.tsx delete mode 100644 packages/ui/src/note/buttons/upvote.tsx diff --git a/apps/desktop2/src/components/col.tsx b/apps/desktop2/src/components/col.tsx index 466c8a2c..0eda43af 100644 --- a/apps/desktop2/src/components/col.tsx +++ b/apps/desktop2/src/components/col.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { LumeColumn } from "@lume/types"; import { invoke } from "@tauri-apps/api/core"; import { Spinner } from "@lume/ui"; @@ -7,33 +7,50 @@ export function Col({ column, account, isScroll, + isResize, }: { column: LumeColumn; account: string; isScroll: boolean; + isResize: boolean; }) { - const webview = useRef(undefined); const container = useRef(null); + const [webview, setWebview] = useState(undefined); const repositionWebview = async () => { - if (webview.current && webview.current.length > 1) { + if (webview && webview.length > 1) { const newRect = container.current.getBoundingClientRect(); await invoke("reposition_column", { - label: webview.current, + label: webview, x: newRect.x, y: newRect.y, }); } }; - useEffect(() => { - if (isScroll) { - repositionWebview(); + const resizeWebview = async () => { + if (webview && webview.length > 1) { + const newRect = container.current.getBoundingClientRect(); + await invoke("resize_column", { + label: webview, + width: newRect.width, + height: newRect.height, + }); } + }; + + useEffect(() => { + resizeWebview(); + }, [isResize]); + + useEffect(() => { + if (isScroll) repositionWebview(); }, [isScroll]); useEffect(() => { (async () => { + if (webview && webview.length > 1) return; + const rect = container.current.getBoundingClientRect(); const windowLabel = `column-${column.label}`; const url = @@ -41,7 +58,7 @@ export function Col({ `?account=${account}&label=${column.label}&name=${column.name}`; // create new webview - webview.current = await invoke("create_column", { + const label: string = await invoke("create_column", { label: windowLabel, x: rect.x, y: rect.y, @@ -49,19 +66,19 @@ export function Col({ height: rect.height, url, }); + + setWebview(label); })(); // close webview when unmounted return () => { - if (webview.current && webview.current.length > 1) { + if (webview && webview.length > 1) { invoke("close_column", { - label: webview.current, - }).then(() => { - webview.current = undefined; + label: webview, }); } }; - }, []); + }, [webview]); return (
diff --git a/apps/desktop2/src/components/repost.tsx b/apps/desktop2/src/components/repost.tsx index 1a0b4206..5ee96d5a 100644 --- a/apps/desktop2/src/components/repost.tsx +++ b/apps/desktop2/src/components/repost.tsx @@ -104,6 +104,7 @@ export function RepostNote({
+ {settings.zap ? : null}
diff --git a/apps/desktop2/src/components/text.tsx b/apps/desktop2/src/components/text.tsx index 3c8b70c9..2ee62e13 100644 --- a/apps/desktop2/src/components/text.tsx +++ b/apps/desktop2/src/components/text.tsx @@ -30,6 +30,7 @@ export function TextNote({
+ {settings.zap ? : null}
diff --git a/apps/desktop2/src/routes/$account.home.tsx b/apps/desktop2/src/routes/$account.home.tsx index 97330d14..9f9dad30 100644 --- a/apps/desktop2/src/routes/$account.home.tsx +++ b/apps/desktop2/src/routes/$account.home.tsx @@ -6,10 +6,11 @@ import { Spinner } from "@lume/ui"; import { createFileRoute } from "@tanstack/react-router"; import { listen } from "@tauri-apps/api/event"; import { resolveResource } from "@tauri-apps/api/path"; +import { getCurrent } from "@tauri-apps/api/webviewWindow"; import { readTextFile } from "@tauri-apps/plugin-fs"; import { nanoid } from "nanoid"; import { useEffect, useRef, useState } from "react"; -import { useDebouncedCallback } from "use-debounce"; +import { useDebounce, useDebouncedCallback } from "use-debounce"; import { VList, VListHandle } from "virtua"; export const Route = createFileRoute("/$account/home")({ @@ -30,14 +31,15 @@ export const Route = createFileRoute("/$account/home")({ }); function Screen() { + const vlistRef = useRef(null); + const { account } = Route.useParams(); const { ark, storedColumns } = Route.useRouteContext(); const [selectedIndex, setSelectedIndex] = useState(-1); - const [isScroll, setIsScroll] = useState(false); const [columns, setColumns] = useState(storedColumns); - - const vlistRef = useRef(null); + const [isScroll, setIsScroll] = useState(false); + const [isResize, setIsResize] = useState(false); const goLeft = () => { const prevIndex = Math.max(selectedIndex - 1, 0); @@ -56,13 +58,23 @@ function Screen() { }; const add = useDebouncedCallback((column: LumeColumn) => { + // update col label column["label"] = column.label + "-" + nanoid(); - setColumns((state) => [...state, column]); - setSelectedIndex(columns.length + 1); + // create new cols + const cols = [...columns]; + const openColIndex = cols.findIndex((col) => col.label === "open"); + const newCols = [ + ...cols.slice(0, openColIndex), + column, + ...cols.slice(openColIndex), + ]; - // scroll to the last column - vlistRef.current.scrollToIndex(columns.length + 1, { + setColumns(newCols); + setSelectedIndex(cols.length - 1); + + // scroll to the newest column + vlistRef.current.scrollToIndex(cols.length - 1, { align: "end", }); }, 150); @@ -77,24 +89,38 @@ function Screen() { }); }, 150); + const startResize = useDebouncedCallback( + () => setIsResize((prev) => !prev), + 150, + ); + useEffect(() => { // save state ark.set_columns(columns); }, [columns]); useEffect(() => { - let unlisten: Awaited> | undefined = undefined; + let unlistenColEvent: Awaited> | undefined = + undefined; + let unlistenWindowResize: Awaited> | undefined = + undefined; (async () => { - if (unlisten) return; - unlisten = await listen("columns", (data) => { + if (unlistenColEvent && unlistenWindowResize) return; + + unlistenColEvent = await listen("columns", (data) => { if (data.payload.type === "add") add(data.payload.column); if (data.payload.type === "remove") remove(data.payload.label); }); + + unlistenWindowResize = await getCurrent().listen("tauri://resize", () => { + startResize(); + }); })(); return () => { - if (unlisten) unlisten(); + if (unlistenColEvent) unlistenColEvent(); + if (unlistenWindowResize) unlistenWindowResize(); }; }, []); @@ -106,12 +132,8 @@ function Screen() { tabIndex={-1} itemSize={440} overscan={3} - onScroll={() => { - setIsScroll(true); - }} - onScrollEnd={() => { - setIsScroll(false); - }} + onScroll={() => setIsScroll(true)} + onScrollEnd={() => setIsScroll(false)} className="scrollbar-none h-full w-full overflow-x-auto focus:outline-none" > {columns.map((column, index) => ( @@ -120,6 +142,7 @@ function Screen() { column={column} account={account} isScroll={isScroll} + isResize={isResize} /> ))} diff --git a/apps/desktop2/src/routes/auth/settings.tsx b/apps/desktop2/src/routes/auth/settings.tsx index a104e95b..16676347 100644 --- a/apps/desktop2/src/routes/auth/settings.tsx +++ b/apps/desktop2/src/routes/auth/settings.tsx @@ -38,6 +38,7 @@ function Screen() { const { ark, settings } = Route.useRouteContext(); const [newSettings, setNewSettings] = useState(settings); + const [loading, setLoading] = useState(false); const toggleNofitication = async () => { await requestPermission(); @@ -70,11 +71,18 @@ function Screen() { const submit = async () => { try { + // start loading + setLoading(true); + + // publish settings const eventId = await ark.set_settings(settings); + if (eventId) { + console.log("event_id: ", eventId); navigate({ to: "/$account/home", params: { account }, replace: true }); } } catch (e) { + setLoading(false); toast.error(e); } }; @@ -169,6 +177,7 @@ function Screen() { - - - - {t("note.buttons.downvote")} - - - - - - ); -} diff --git a/packages/ui/src/note/buttons/pin.tsx b/packages/ui/src/note/buttons/pin.tsx index cc1bc9a7..9c98ef94 100644 --- a/packages/ui/src/note/buttons/pin.tsx +++ b/packages/ui/src/note/buttons/pin.tsx @@ -1,31 +1,31 @@ -import { PinIcon } from "@lume/icons"; +import { LinkIcon } from "@lume/icons"; import * as Tooltip from "@radix-ui/react-tooltip"; -import { useTranslation } from "react-i18next"; +import { useRouteContext } from "@tanstack/react-router"; import { useNoteContext } from "../provider"; export function NotePin() { - const event = useNoteContext(); - const { t } = useTranslation(); + const event = useNoteContext(); + const { ark } = useRouteContext({ strict: false }); - return ( - - - - - - - - {t("note.buttons.pinTooltip")} - - - - - - ); + return ( + + + + + + + + Open as new window + + + + + + ); } diff --git a/packages/ui/src/note/buttons/reaction.tsx b/packages/ui/src/note/buttons/reaction.tsx deleted file mode 100644 index 431d8de6..00000000 --- a/packages/ui/src/note/buttons/reaction.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { NoteUpvote } from "./upvote"; -import { NoteDownvote } from "./downvote"; - -export function NoteReaction() { - return ( -
- - -
- ); -} diff --git a/packages/ui/src/note/buttons/reply.tsx b/packages/ui/src/note/buttons/reply.tsx index 8f8f87aa..6ef06869 100644 --- a/packages/ui/src/note/buttons/reply.tsx +++ b/packages/ui/src/note/buttons/reply.tsx @@ -1,14 +1,11 @@ import { ReplyIcon } from "@lume/icons"; import * as Tooltip from "@radix-ui/react-tooltip"; -import { useTranslation } from "react-i18next"; import { useNoteContext } from "../provider"; import { useRouteContext } from "@tanstack/react-router"; export function NoteReply() { const event = useNoteContext(); - const { ark } = useRouteContext({ strict: false }); - const { t } = useTranslation(); return ( @@ -24,7 +21,7 @@ export function NoteReply() { - {t("note.menu.viewThread")} + Reply diff --git a/packages/ui/src/note/buttons/upvote.tsx b/packages/ui/src/note/buttons/upvote.tsx deleted file mode 100644 index c8ae5d42..00000000 --- a/packages/ui/src/note/buttons/upvote.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { ArrowUpIcon } from "@lume/icons"; -import { useState } from "react"; -import { useNoteContext } from "../provider"; -import { cn } from "@lume/utils"; -import * as Tooltip from "@radix-ui/react-tooltip"; -import { useTranslation } from "react-i18next"; -import { useRouteContext } from "@tanstack/react-router"; -import { Spinner } from "../../spinner"; - -export function NoteUpvote() { - const { ark } = useRouteContext({ strict: false }); - const event = useNoteContext(); - - const [t] = useTranslation(); - const [reaction, setReaction] = useState<"+" | null>(null); - const [loading, setLoading] = useState(false); - - const up = async () => { - // start loading - setLoading(true); - - const res = await ark.upvote(event.id, event.pubkey); - if (res) setReaction("+"); - - // stop loading - setLoading(false); - }; - - return ( - - - - - - - - {t("note.buttons.upvote")} - - - - - - ); -} diff --git a/packages/ui/src/note/index.ts b/packages/ui/src/note/index.ts index b61053b8..ac3d0de3 100644 --- a/packages/ui/src/note/index.ts +++ b/packages/ui/src/note/index.ts @@ -1,5 +1,4 @@ import { NotePin } from "./buttons/pin"; -import { NoteReaction } from "./buttons/reaction"; import { NoteReply } from "./buttons/reply"; import { NoteRepost } from "./buttons/repost"; import { NoteZap } from "./buttons/zap"; @@ -18,7 +17,7 @@ export const Note = { Menu: NoteMenu, Reply: NoteReply, Repost: NoteRepost, - Reaction: NoteReaction, + Pin: NotePin, Content: NoteContent, Zap: NoteZap, Pin: NotePin, diff --git a/src-tauri/src/commands/window.rs b/src-tauri/src/commands/window.rs index 3c9f6a50..0fa76c60 100644 --- a/src-tauri/src/commands/window.rs +++ b/src-tauri/src/commands/window.rs @@ -66,3 +66,22 @@ pub fn reposition_column( None => Err("Webview not found".into()), } } + +#[tauri::command] +pub fn resize_column( + label: &str, + width: f32, + height: f32, + app_handle: tauri::AppHandle, +) -> Result<(), String> { + match app_handle.get_webview(label) { + Some(webview) => { + if let Ok(_) = webview.set_size(LogicalSize::new(width, height)) { + Ok(()) + } else { + Err("Reposition column failed".into()) + } + } + None => Err("Webview not found".into()), + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index c6763436..f4ca0210 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -133,7 +133,8 @@ fn main() { commands::opg::fetch_opg, commands::window::create_column, commands::window::close_column, - commands::window::reposition_column + commands::window::reposition_column, + commands::window::resize_column ]) .run(tauri::generate_context!()) .expect("error while running tauri application")