From 4070abadded8cdf1c03f21db1c170694352ed3f0 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 11 Dec 2024 12:52:15 -0800 Subject: [PATCH] Squash some leftover bugs (#1495) Only create new tab in `CheckAndFixWindow` if no tabs or pinned tabs exist Update `resolvers.resolveTabNum` to account for pinned tabs Remove obsolete and unused `wstore.DeleteTab` Only show accelerators for first 9 workspaces in workspace app menu to be consistent with other keybindings Fix tabbar spacing to remove min size for drag right spacer, account for workspace switcher button size Fix updatebanner size calculations --- build/icons/48x48.png | Bin 0 -> 1888 bytes build/icons/64x64.png | Bin 0 -> 2455 bytes emain/menu.ts | 9 ++------ frontend/app/tab/tabbar.scss | 14 ++++++------ frontend/app/tab/tabbar.tsx | 29 +++++++++++++++++++------ frontend/app/tab/updatebanner.scss | 20 ++++++++++------- frontend/app/tab/updatebanner.tsx | 25 +++++++++++---------- frontend/app/tab/workspaceswitcher.tsx | 7 +++--- pkg/wcore/window.go | 2 +- pkg/wshrpc/wshserver/resolvers.go | 14 +++++++++--- pkg/wstore/wstore.go | 22 ------------------- 11 files changed, 72 insertions(+), 70 deletions(-) create mode 100644 build/icons/48x48.png create mode 100644 build/icons/64x64.png diff --git a/build/icons/48x48.png b/build/icons/48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..e1250a8964d3cac3e5660b3edbc7c4177f52c98c GIT binary patch literal 1888 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 E1F{WZ%m4rY literal 0 HcmV?d00001 diff --git a/emain/menu.ts b/emain/menu.ts index 1bf125982..99f175778 100644 --- a/emain/menu.ts +++ b/emain/menu.ts @@ -49,13 +49,8 @@ async function getWorkspaceMenu(ww?: WaveBrowserWindow): Promise { const appMenuButtonRef = useRef(null); const tabWidthRef = useRef(TAB_DEFAULT_WIDTH); const scrollableRef = useRef(false); - const updateStatusButtonRef = useRef(null); + const updateStatusBannerRef = useRef(null); const configErrorButtonRef = useRef(null); const prevAllLoadedRef = useRef(false); const activeTabId = useAtomValue(atoms.staticTabId); @@ -227,8 +226,9 @@ const TabBar = memo(({ workspace }: TabBarProps) => { const tabbarWrapperWidth = tabbarWrapperRef.current.getBoundingClientRect().width; const windowDragLeftWidth = draggerLeftRef.current.getBoundingClientRect().width; + const windowDragRightWidth = draggerRightRef.current.getBoundingClientRect().width; const addBtnWidth = addBtnRef.current.getBoundingClientRect().width; - const updateStatusLabelWidth = updateStatusButtonRef.current?.getBoundingClientRect().width ?? 0; + const updateStatusLabelWidth = updateStatusBannerRef.current?.getBoundingClientRect().width ?? 0; const configErrorWidth = configErrorButtonRef.current?.getBoundingClientRect().width ?? 0; const appMenuButtonWidth = appMenuButtonRef.current?.getBoundingClientRect().width ?? 0; const workspaceSwitcherWidth = workspaceSwitcherRef.current?.getBoundingClientRect().width ?? 0; @@ -236,7 +236,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => { const nonTabElementsWidth = windowDragLeftWidth + - DRAGGER_RIGHT_MIN_WIDTH + + windowDragRightWidth + addBtnWidth + updateStatusLabelWidth + configErrorWidth + @@ -256,6 +256,21 @@ const TabBar = memo(({ workspace }: TabBarProps) => { // Determine if the tab bar needs to be scrollable const newScrollable = idealTabWidth * numberOfTabs > spaceForTabs; + console.log( + "tabbarWrapperWidth", + tabbarWrapperWidth, + "nonTabElementsWidth", + nonTabElementsWidth, + "idealTabWidth", + idealTabWidth, + "spaceForTabs", + spaceForTabs, + "newScrollable", + newScrollable, + "devLabelWidth", + devLabelWidth + ); + // Apply the calculated width and position to all tabs tabRefs.current.forEach((ref, index) => { if (ref.current) { @@ -653,7 +668,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => { {appMenuButton} {devLabel} - +
{tabIds.map((tabId, index) => { @@ -683,8 +698,8 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
- - + +
); diff --git a/frontend/app/tab/updatebanner.scss b/frontend/app/tab/updatebanner.scss index 8b99c3265..14b144ac5 100644 --- a/frontend/app/tab/updatebanner.scss +++ b/frontend/app/tab/updatebanner.scss @@ -1,11 +1,15 @@ -.update-available-button { - height: 80%; - margin: auto 4px; - color: black; - background-color: var(--accent-color); - flex: 0 0 fit-content; +.update-available-banner { + display: flex; + height: 100%; + .button { + color: black; + height: 80%; + margin: auto 4px; + background-color: var(--accent-color); + flex: 0 0 fit-content; - &:disabled { - opacity: unset !important; + &:disabled { + opacity: unset !important; + } } } diff --git a/frontend/app/tab/updatebanner.tsx b/frontend/app/tab/updatebanner.tsx index 971ca2c65..fb1836693 100644 --- a/frontend/app/tab/updatebanner.tsx +++ b/frontend/app/tab/updatebanner.tsx @@ -1,10 +1,10 @@ import { Button } from "@/element/button"; import { atoms, getApi } from "@/store/global"; import { useAtomValue } from "jotai"; -import { memo, useEffect, useState } from "react"; +import { forwardRef, memo, useEffect, useState } from "react"; import "./updatebanner.scss"; -const UpdateStatusBannerComponent = ({ buttonRef }: { buttonRef: React.RefObject }) => { +const UpdateStatusBannerComponent = forwardRef((_, ref) => { const appUpdateStatus = useAtomValue(atoms.updaterStatusAtom); let [updateStatusMessage, setUpdateStatusMessage] = useState(); const [dismissBannerTimeout, setDismissBannerTimeout] = useState(); @@ -54,17 +54,18 @@ const UpdateStatusBannerComponent = ({ buttonRef }: { buttonRef: React.RefObject } if (updateStatusMessage) { return ( - +
+ +
); } -}; +}); export const UpdateStatusBanner = memo(UpdateStatusBannerComponent) as typeof UpdateStatusBannerComponent; diff --git a/frontend/app/tab/workspaceswitcher.tsx b/frontend/app/tab/workspaceswitcher.tsx index f62c7bfe2..50080b64b 100644 --- a/frontend/app/tab/workspaceswitcher.tsx +++ b/frontend/app/tab/workspaceswitcher.tsx @@ -17,7 +17,7 @@ import clsx from "clsx"; import { atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai"; import { splitAtom } from "jotai/utils"; import { OverlayScrollbarsComponent } from "overlayscrollbars-react"; -import { CSSProperties, memo, useCallback, useEffect, useRef } from "react"; +import { CSSProperties, forwardRef, memo, useCallback, useEffect, useRef } from "react"; import WorkspaceSVG from "../asset/workspace.svg"; import { IconButton } from "../element/iconbutton"; import { atoms, getApi } from "../store/global"; @@ -167,7 +167,7 @@ type WorkspaceList = WorkspaceListEntry[]; const workspaceMapAtom = atom([]); const workspaceSplitAtom = splitAtom(workspaceMapAtom); const editingWorkspaceAtom = atom(); -const WorkspaceSwitcher = () => { +const WorkspaceSwitcher = forwardRef((_, ref) => { const setWorkspaceList = useSetAtom(workspaceMapAtom); const activeWorkspace = useAtomValueSafe(atoms.workspace); const workspaceList = useAtomValue(workspaceSplitAtom); @@ -231,6 +231,7 @@ const WorkspaceSwitcher = () => { className="workspace-switcher-popover" placement="bottom-start" onDismiss={() => setEditingWorkspace(null)} + ref={ref} > { ); -}; +}); const WorkspaceSwitcherItem = ({ entryAtom, diff --git a/pkg/wcore/window.go b/pkg/wcore/window.go index a38b5c2b3..1eda94d86 100644 --- a/pkg/wcore/window.go +++ b/pkg/wcore/window.go @@ -177,7 +177,7 @@ func CheckAndFixWindow(ctx context.Context, windowId string) *waveobj.Window { CloseWindow(ctx, windowId, false) return nil } - if len(ws.TabIds) == 0 { + if len(ws.TabIds) == 0 && len(ws.PinnedTabIds) == 0 { log.Printf("fixing workspace with no tabs %q (in checkAndFixWindow)\n", ws.OID) _, err = CreateTab(ctx, ws.OID, "", true, false, false) if err != nil { diff --git a/pkg/wshrpc/wshserver/resolvers.go b/pkg/wshrpc/wshserver/resolvers.go index 434fba6ef..23a4b9ac9 100644 --- a/pkg/wshrpc/wshserver/resolvers.go +++ b/pkg/wshrpc/wshserver/resolvers.go @@ -156,11 +156,19 @@ func resolveTabNum(ctx context.Context, data wshrpc.CommandResolveIdsData, value return nil, fmt.Errorf("error getting workspace: %v", err) } - if tabNum < 1 || tabNum > len(ws.TabIds) { - return nil, fmt.Errorf("tab num out of range, workspace has %d tabs", len(ws.TabIds)) + numPinnedTabs := len(ws.PinnedTabIds) + numTabs := len(ws.TabIds) + numPinnedTabs + if tabNum < 1 || tabNum > numTabs { + return nil, fmt.Errorf("tab num out of range, workspace has %d tabs", numTabs) } - resolvedTabId := ws.TabIds[tabNum-1] + tabIdx := tabNum - 1 + var resolvedTabId string + if tabIdx < numPinnedTabs { + resolvedTabId = ws.PinnedTabIds[tabIdx] + } else { + resolvedTabId = ws.TabIds[tabIdx-numPinnedTabs] + } return &waveobj.ORef{OType: waveobj.OType_Tab, OID: resolvedTabId}, nil } diff --git a/pkg/wstore/wstore.go b/pkg/wstore/wstore.go index 3ba3fab31..a473ad125 100644 --- a/pkg/wstore/wstore.go +++ b/pkg/wstore/wstore.go @@ -31,28 +31,6 @@ func UpdateTabName(ctx context.Context, tabId, name string) error { }) } -// must delete all blocks individually first -// also deletes LayoutState -func DeleteTab(ctx context.Context, workspaceId string, tabId string) error { - return WithTx(ctx, func(tx *TxWrap) error { - tab, _ := DBGet[*waveobj.Tab](tx.Context(), tabId) - if tab == nil { - return nil - } - if len(tab.BlockIds) != 0 { - return fmt.Errorf("tab has blocks, must delete blocks first") - } - ws, _ := DBGet[*waveobj.Workspace](tx.Context(), workspaceId) - if ws != nil { - ws.TabIds = utilfn.RemoveElemFromSlice(ws.TabIds, tabId) - DBUpdate(tx.Context(), ws) - } - DBDelete(tx.Context(), waveobj.OType_Tab, tabId) - DBDelete(tx.Context(), waveobj.OType_LayoutState, tab.LayoutState) - return nil - }) -} - func UpdateObjectMeta(ctx context.Context, oref waveobj.ORef, meta waveobj.MetaMapType, mergeSpecial bool) error { return WithTx(ctx, func(tx *TxWrap) error { if oref.IsEmpty() {