mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
Synchronize workspace edits across windows, close window when workspace is deleted (#1540)
This commit is contained in:
parent
1ba370e4dd
commit
78f3cd0472
70
frontend/app/tab/workspaceeditor.scss
Normal file
70
frontend/app/tab/workspaceeditor.scss
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
.workspace-editor {
|
||||||
|
width: 100%;
|
||||||
|
.input {
|
||||||
|
margin: 5px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-selector {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(15px, 15px)); // Ensures each color circle has a fixed 14px size
|
||||||
|
grid-gap: 18.5px; // Space between items
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
border-bottom: 1px solid var(--modal-border-color);
|
||||||
|
|
||||||
|
.color-circle {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
// Border offset outward
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -3px;
|
||||||
|
left: -3px;
|
||||||
|
right: -3px;
|
||||||
|
bottom: -3px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected:before {
|
||||||
|
border-color: var(--main-text-color); // Highlight for the selected circle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-selector {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(16px, 16px)); // Ensures each color circle has a fixed 14px size
|
||||||
|
grid-column-gap: 17.5px; // Space between items
|
||||||
|
grid-row-gap: 13px; // Space between items
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
|
.icon-item {
|
||||||
|
font-size: 15px;
|
||||||
|
color: oklch(from var(--modal-bg-color) calc(l * 1.5) c h);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
|
&.selected,
|
||||||
|
&:hover {
|
||||||
|
color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-ws-btn-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
125
frontend/app/tab/workspaceeditor.tsx
Normal file
125
frontend/app/tab/workspaceeditor.tsx
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { fireAndForget, makeIconClass } from "@/util/util";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { memo, useEffect, useRef, useState } from "react";
|
||||||
|
import { Button } from "../element/button";
|
||||||
|
import { Input } from "../element/input";
|
||||||
|
import { WorkspaceService } from "../store/services";
|
||||||
|
import "./workspaceeditor.scss";
|
||||||
|
|
||||||
|
interface ColorSelectorProps {
|
||||||
|
colors: string[];
|
||||||
|
selectedColor?: string;
|
||||||
|
onSelect: (color: string) => void;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ColorSelector = memo(({ colors, selectedColor, onSelect, className }: ColorSelectorProps) => {
|
||||||
|
const handleColorClick = (color: string) => {
|
||||||
|
onSelect(color);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx("color-selector", className)}>
|
||||||
|
{colors.map((color) => (
|
||||||
|
<div
|
||||||
|
key={color}
|
||||||
|
className={clsx("color-circle", { selected: selectedColor === color })}
|
||||||
|
style={{ backgroundColor: color }}
|
||||||
|
onClick={() => handleColorClick(color)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IconSelectorProps {
|
||||||
|
icons: string[];
|
||||||
|
selectedIcon?: string;
|
||||||
|
onSelect: (icon: string) => void;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IconSelector = memo(({ icons, selectedIcon, onSelect, className }: IconSelectorProps) => {
|
||||||
|
const handleIconClick = (icon: string) => {
|
||||||
|
onSelect(icon);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx("icon-selector", className)}>
|
||||||
|
{icons.map((icon) => {
|
||||||
|
const iconClass = makeIconClass(icon, true);
|
||||||
|
return (
|
||||||
|
<i
|
||||||
|
key={icon}
|
||||||
|
className={clsx(iconClass, "icon-item", { selected: selectedIcon === icon })}
|
||||||
|
onClick={() => handleIconClick(icon)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
interface WorkspaceEditorProps {
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
color: string;
|
||||||
|
focusInput: boolean;
|
||||||
|
onTitleChange: (newTitle: string) => void;
|
||||||
|
onColorChange: (newColor: string) => void;
|
||||||
|
onIconChange: (newIcon: string) => void;
|
||||||
|
onDeleteWorkspace: () => void;
|
||||||
|
}
|
||||||
|
const WorkspaceEditorComponent = ({
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
color,
|
||||||
|
focusInput,
|
||||||
|
onTitleChange,
|
||||||
|
onColorChange,
|
||||||
|
onIconChange,
|
||||||
|
onDeleteWorkspace,
|
||||||
|
}: WorkspaceEditorProps) => {
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const [colors, setColors] = useState<string[]>([]);
|
||||||
|
const [icons, setIcons] = useState<string[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fireAndForget(async () => {
|
||||||
|
const colors = await WorkspaceService.GetColors();
|
||||||
|
const icons = await WorkspaceService.GetIcons();
|
||||||
|
setColors(colors);
|
||||||
|
setIcons(icons);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (focusInput && inputRef.current) {
|
||||||
|
inputRef.current.focus();
|
||||||
|
inputRef.current.select();
|
||||||
|
}
|
||||||
|
}, [focusInput]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="workspace-editor">
|
||||||
|
<Input
|
||||||
|
ref={inputRef}
|
||||||
|
className={clsx("vertical-padding-3", { error: title === "" })}
|
||||||
|
onChange={onTitleChange}
|
||||||
|
value={title}
|
||||||
|
autoFocus
|
||||||
|
autoSelect
|
||||||
|
/>
|
||||||
|
<ColorSelector selectedColor={color} colors={colors} onSelect={onColorChange} />
|
||||||
|
<IconSelector selectedIcon={icon} icons={icons} onSelect={onIconChange} />
|
||||||
|
<div className="delete-ws-btn-wrapper">
|
||||||
|
<Button className="ghost red font-size-12 bold" onClick={onDeleteWorkspace}>
|
||||||
|
Delete workspace
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WorkspaceEditor = memo(WorkspaceEditorComponent) as typeof WorkspaceEditorComponent;
|
@ -26,25 +26,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-left,
|
|
||||||
.icon-right {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
width: 1px;
|
|
||||||
height: 20px;
|
|
||||||
background: rgba(255, 255, 255, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollable {
|
|
||||||
max-height: 400px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-switcher-content {
|
.workspace-switcher-content {
|
||||||
min-height: auto;
|
min-height: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -55,6 +36,25 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0px 8px 24px 0px var(--modal-shadow-color);
|
box-shadow: 0px 8px 24px 0px var(--modal-shadow-color);
|
||||||
|
|
||||||
|
.icon-left,
|
||||||
|
.icon-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 20px;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollable {
|
||||||
|
max-height: 400px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 19px;
|
line-height: 19px;
|
||||||
@ -144,83 +144,6 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-editor {
|
|
||||||
width: 100%;
|
|
||||||
.input {
|
|
||||||
margin: 5px 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-selector {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(
|
|
||||||
auto-fit,
|
|
||||||
minmax(15px, 15px)
|
|
||||||
); // Ensures each color circle has a fixed 14px size
|
|
||||||
grid-gap: 18.5px; // Space between items
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 5px;
|
|
||||||
padding-bottom: 15px;
|
|
||||||
border-bottom: 1px solid var(--modal-border-color);
|
|
||||||
|
|
||||||
.color-circle {
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
border-radius: 50%;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
// Border offset outward
|
|
||||||
&:before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: -3px;
|
|
||||||
left: -3px;
|
|
||||||
right: -3px;
|
|
||||||
bottom: -3px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected:before {
|
|
||||||
border-color: var(--main-text-color); // Highlight for the selected circle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-selector {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(
|
|
||||||
auto-fit,
|
|
||||||
minmax(16px, 16px)
|
|
||||||
); // Ensures each color circle has a fixed 14px size
|
|
||||||
grid-column-gap: 17.5px; // Space between items
|
|
||||||
grid-row-gap: 13px; // Space between items
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.icon-item {
|
|
||||||
font-size: 15px;
|
|
||||||
color: oklch(from var(--modal-bg-color) calc(l * 1.5) c h);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
|
|
||||||
&.selected,
|
|
||||||
&:hover {
|
|
||||||
color: var(--main-text-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-ws-btn-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 3px 0;
|
padding: 3px 0;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright 2024, Command Line
|
// Copyright 2024, Command Line
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import { Button } from "@/element/button";
|
|
||||||
import {
|
import {
|
||||||
ExpandableMenu,
|
ExpandableMenu,
|
||||||
ExpandableMenuItem,
|
ExpandableMenuItem,
|
||||||
@ -10,139 +9,22 @@ import {
|
|||||||
ExpandableMenuItemLeftElement,
|
ExpandableMenuItemLeftElement,
|
||||||
ExpandableMenuItemRightElement,
|
ExpandableMenuItemRightElement,
|
||||||
} from "@/element/expandablemenu";
|
} from "@/element/expandablemenu";
|
||||||
import { Input } from "@/element/input";
|
|
||||||
import { Popover, PopoverButton, PopoverContent } from "@/element/popover";
|
import { Popover, PopoverButton, PopoverContent } from "@/element/popover";
|
||||||
import { fireAndForget, makeIconClass, useAtomValueSafe } from "@/util/util";
|
import { fireAndForget, makeIconClass, useAtomValueSafe } from "@/util/util";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
import { atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||||
import { splitAtom } from "jotai/utils";
|
import { splitAtom } from "jotai/utils";
|
||||||
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
|
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
|
||||||
import { CSSProperties, forwardRef, memo, useCallback, useEffect, useRef, useState } from "react";
|
import { CSSProperties, forwardRef, useCallback, useEffect } from "react";
|
||||||
import WorkspaceSVG from "../asset/workspace.svg";
|
import WorkspaceSVG from "../asset/workspace.svg";
|
||||||
import { IconButton } from "../element/iconbutton";
|
import { IconButton } from "../element/iconbutton";
|
||||||
import { atoms, getApi } from "../store/global";
|
import { atoms, getApi } from "../store/global";
|
||||||
import { WorkspaceService } from "../store/services";
|
import { WorkspaceService } from "../store/services";
|
||||||
import { getObjectValue, makeORef, setObjectValue } from "../store/wos";
|
import { getObjectValue, makeORef } from "../store/wos";
|
||||||
|
import { waveEventSubscribe } from "../store/wps";
|
||||||
|
import { WorkspaceEditor } from "./workspaceeditor";
|
||||||
import "./workspaceswitcher.scss";
|
import "./workspaceswitcher.scss";
|
||||||
|
|
||||||
interface ColorSelectorProps {
|
|
||||||
colors: string[];
|
|
||||||
selectedColor?: string;
|
|
||||||
onSelect: (color: string) => void;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ColorSelector = memo(({ colors, selectedColor, onSelect, className }: ColorSelectorProps) => {
|
|
||||||
const handleColorClick = (color: string) => {
|
|
||||||
onSelect(color);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={clsx("color-selector", className)}>
|
|
||||||
{colors.map((color) => (
|
|
||||||
<div
|
|
||||||
key={color}
|
|
||||||
className={clsx("color-circle", { selected: selectedColor === color })}
|
|
||||||
style={{ backgroundColor: color }}
|
|
||||||
onClick={() => handleColorClick(color)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
interface IconSelectorProps {
|
|
||||||
icons: string[];
|
|
||||||
selectedIcon?: string;
|
|
||||||
onSelect: (icon: string) => void;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const IconSelector = memo(({ icons, selectedIcon, onSelect, className }: IconSelectorProps) => {
|
|
||||||
const handleIconClick = (icon: string) => {
|
|
||||||
onSelect(icon);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={clsx("icon-selector", className)}>
|
|
||||||
{icons.map((icon) => {
|
|
||||||
const iconClass = makeIconClass(icon, true);
|
|
||||||
return (
|
|
||||||
<i
|
|
||||||
key={icon}
|
|
||||||
className={clsx(iconClass, "icon-item", { selected: selectedIcon === icon })}
|
|
||||||
onClick={() => handleIconClick(icon)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
interface WorkspaceEditorProps {
|
|
||||||
title: string;
|
|
||||||
icon: string;
|
|
||||||
color: string;
|
|
||||||
focusInput: boolean;
|
|
||||||
onTitleChange: (newTitle: string) => void;
|
|
||||||
onColorChange: (newColor: string) => void;
|
|
||||||
onIconChange: (newIcon: string) => void;
|
|
||||||
onDeleteWorkspace: () => void;
|
|
||||||
}
|
|
||||||
const WorkspaceEditor = memo(
|
|
||||||
({
|
|
||||||
title,
|
|
||||||
icon,
|
|
||||||
color,
|
|
||||||
focusInput,
|
|
||||||
onTitleChange,
|
|
||||||
onColorChange,
|
|
||||||
onIconChange,
|
|
||||||
onDeleteWorkspace,
|
|
||||||
}: WorkspaceEditorProps) => {
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
const [colors, setColors] = useState<string[]>([]);
|
|
||||||
const [icons, setIcons] = useState<string[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fireAndForget(async () => {
|
|
||||||
const colors = await WorkspaceService.GetColors();
|
|
||||||
const icons = await WorkspaceService.GetIcons();
|
|
||||||
setColors(colors);
|
|
||||||
setIcons(icons);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (focusInput && inputRef.current) {
|
|
||||||
inputRef.current.focus();
|
|
||||||
inputRef.current.select();
|
|
||||||
}
|
|
||||||
}, [focusInput]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="workspace-editor">
|
|
||||||
<Input
|
|
||||||
ref={inputRef}
|
|
||||||
className={clsx("vertical-padding-3", { error: title === "" })}
|
|
||||||
onChange={onTitleChange}
|
|
||||||
value={title}
|
|
||||||
autoFocus
|
|
||||||
autoSelect
|
|
||||||
/>
|
|
||||||
<ColorSelector selectedColor={color} colors={colors} onSelect={onColorChange} />
|
|
||||||
<IconSelector selectedIcon={icon} icons={icons} onSelect={onIconChange} />
|
|
||||||
<div className="delete-ws-btn-wrapper">
|
|
||||||
<Button className="ghost red font-size-12 bold" onClick={onDeleteWorkspace}>
|
|
||||||
Delete workspace
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
type WorkspaceListEntry = {
|
type WorkspaceListEntry = {
|
||||||
windowId: string;
|
windowId: string;
|
||||||
workspace: Workspace;
|
workspace: Workspace;
|
||||||
@ -175,15 +57,21 @@ const WorkspaceSwitcher = forwardRef<HTMLDivElement>((_, ref) => {
|
|||||||
setWorkspaceList(newList);
|
setWorkspaceList(newList);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() =>
|
||||||
|
waveEventSubscribe({
|
||||||
|
eventType: "workspace:update",
|
||||||
|
handler: () => fireAndForget(updateWorkspaceList),
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fireAndForget(updateWorkspaceList);
|
fireAndForget(updateWorkspaceList);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onDeleteWorkspace = useCallback((workspaceId: string) => {
|
const onDeleteWorkspace = useCallback((workspaceId: string) => {
|
||||||
getApi().deleteWorkspace(workspaceId);
|
getApi().deleteWorkspace(workspaceId);
|
||||||
setTimeout(() => {
|
|
||||||
fireAndForget(updateWorkspaceList);
|
|
||||||
}, 10);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isActiveWorkspaceSaved = !!(activeWorkspace.name && activeWorkspace.icon);
|
const isActiveWorkspaceSaved = !!(activeWorkspace.name && activeWorkspace.icon);
|
||||||
@ -266,9 +154,16 @@ const WorkspaceSwitcherItem = ({
|
|||||||
|
|
||||||
const setWorkspace = useCallback((newWorkspace: Workspace) => {
|
const setWorkspace = useCallback((newWorkspace: Workspace) => {
|
||||||
if (newWorkspace.name != "") {
|
if (newWorkspace.name != "") {
|
||||||
setObjectValue({ ...newWorkspace, otype: "workspace" }, undefined, true);
|
fireAndForget(() =>
|
||||||
|
WorkspaceService.UpdateWorkspace(
|
||||||
|
workspace.oid,
|
||||||
|
newWorkspace.name,
|
||||||
|
newWorkspace.icon,
|
||||||
|
newWorkspace.color,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
setWorkspaceEntry({ ...workspaceEntry, workspace: newWorkspace });
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isActive = !!workspaceEntry.windowId;
|
const isActive = !!workspaceEntry.windowId;
|
||||||
|
@ -50,6 +50,9 @@ func (svc *WorkspaceService) UpdateWorkspace(ctx context.Context, workspaceId st
|
|||||||
return nil, fmt.Errorf("error updating workspace: %w", err)
|
return nil, fmt.Errorf("error updating workspace: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wps.Broker.Publish(wps.WaveEvent{
|
||||||
|
Event: wps.Event_WorkspaceUpdate})
|
||||||
|
|
||||||
updates := waveobj.ContextGetUpdatesRtn(ctx)
|
updates := waveobj.ContextGetUpdatesRtn(ctx)
|
||||||
go func() {
|
go func() {
|
||||||
defer panichandler.PanicHandler("WorkspaceService:UpdateWorkspace:SendUpdateEvents")
|
defer panichandler.PanicHandler("WorkspaceService:UpdateWorkspace:SendUpdateEvents")
|
||||||
|
@ -28,7 +28,8 @@ func SwitchWorkspace(ctx context.Context, windowId string, workspaceId string) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting window: %w", err)
|
return nil, fmt.Errorf("error getting window: %w", err)
|
||||||
}
|
}
|
||||||
if window.WorkspaceId == workspaceId {
|
curWsId := window.WorkspaceId
|
||||||
|
if curWsId == workspaceId {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,24 +46,24 @@ func SwitchWorkspace(ctx context.Context, windowId string, workspaceId string) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
window.WorkspaceId = workspaceId
|
||||||
curWs, err := GetWorkspace(ctx, window.WorkspaceId)
|
err = wstore.DBUpdate(ctx, window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting current workspace: %w", err)
|
return nil, fmt.Errorf("error updating window: %w", err)
|
||||||
}
|
}
|
||||||
deleted, err := DeleteWorkspace(ctx, curWs.OID, false)
|
|
||||||
|
deleted, err := DeleteWorkspace(ctx, curWsId, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error deleting current workspace: %w", err)
|
return nil, fmt.Errorf("error deleting current workspace: %w", err)
|
||||||
}
|
}
|
||||||
if !deleted {
|
if !deleted {
|
||||||
log.Printf("current workspace %s was not deleted\n", curWs.OID)
|
log.Printf("current workspace %s was not deleted\n", curWsId)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("deleted current workspace %s\n", curWs.OID)
|
log.Printf("deleted current workspace %s\n", curWsId)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.WorkspaceId = workspaceId
|
|
||||||
log.Printf("switching window %s to workspace %s\n", windowId, workspaceId)
|
log.Printf("switching window %s to workspace %s\n", windowId, workspaceId)
|
||||||
return ws, wstore.DBUpdate(ctx, window)
|
return ws, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetWindow(ctx context.Context, windowId string) (*waveobj.Window, error) {
|
func GetWindow(ctx context.Context, windowId string) (*waveobj.Window, error) {
|
||||||
|
@ -122,6 +122,7 @@ func DeleteWorkspace(ctx context.Context, workspaceId string, force bool) (bool,
|
|||||||
return false, fmt.Errorf("error closing tab: %w", err)
|
return false, fmt.Errorf("error closing tab: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
windowId, err := wstore.DBFindWindowForWorkspaceId(ctx, workspaceId)
|
||||||
err = wstore.DBDelete(ctx, waveobj.OType_Workspace, workspaceId)
|
err = wstore.DBDelete(ctx, waveobj.OType_Workspace, workspaceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("error deleting workspace: %w", err)
|
return false, fmt.Errorf("error deleting workspace: %w", err)
|
||||||
@ -129,6 +130,12 @@ func DeleteWorkspace(ctx context.Context, workspaceId string, force bool) (bool,
|
|||||||
log.Printf("deleted workspace %s\n", workspaceId)
|
log.Printf("deleted workspace %s\n", workspaceId)
|
||||||
wps.Broker.Publish(wps.WaveEvent{
|
wps.Broker.Publish(wps.WaveEvent{
|
||||||
Event: wps.Event_WorkspaceUpdate})
|
Event: wps.Event_WorkspaceUpdate})
|
||||||
|
if windowId != "" {
|
||||||
|
err = CloseWindow(ctx, windowId, false)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error closing window: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user