Skip to content
Open
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
51 changes: 0 additions & 51 deletions apps/desktop/src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,6 @@ pub async fn set_onboarding_needed<R: tauri::Runtime>(
app.set_onboarding_needed(v).map_err(|e| e.to_string())
}

#[tauri::command]
#[specta::specta]
pub async fn get_dismissed_toasts<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
) -> Result<Vec<String>, String> {
app.get_dismissed_toasts()
}

#[tauri::command]
#[specta::specta]
pub async fn set_dismissed_toasts<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
v: Vec<String>,
) -> Result<(), String> {
app.set_dismissed_toasts(v)
}

#[tauri::command]
#[specta::specta]
pub async fn get_onboarding_local<R: tauri::Runtime>(
Expand Down Expand Up @@ -129,37 +112,3 @@ pub async fn set_tinybase_values<R: tauri::Runtime>(
) -> Result<(), String> {
app.set_tinybase_values(v)
}

#[tauri::command]
#[specta::specta]
pub async fn get_pinned_tabs<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
) -> Result<Option<String>, String> {
app.get_pinned_tabs()
}

#[tauri::command]
#[specta::specta]
pub async fn set_pinned_tabs<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
v: String,
) -> Result<(), String> {
app.set_pinned_tabs(v)
}

#[tauri::command]
#[specta::specta]
pub async fn get_recently_opened_sessions<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
) -> Result<Option<String>, String> {
app.get_recently_opened_sessions()
}

#[tauri::command]
#[specta::specta]
pub async fn set_recently_opened_sessions<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
v: String,
) -> Result<(), String> {
app.set_recently_opened_sessions(v)
}
59 changes: 0 additions & 59 deletions apps/desktop/src-tauri/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,11 @@ pub trait AppExt<R: tauri::Runtime> {
fn get_onboarding_needed(&self) -> Result<bool, String>;
fn set_onboarding_needed(&self, v: bool) -> Result<(), String>;

fn get_dismissed_toasts(&self) -> Result<Vec<String>, String>;
fn set_dismissed_toasts(&self, v: Vec<String>) -> Result<(), String>;

fn get_onboarding_local(&self) -> Result<bool, String>;
fn set_onboarding_local(&self, v: bool) -> Result<(), String>;

fn get_tinybase_values(&self) -> Result<Option<String>, String>;
fn set_tinybase_values(&self, v: String) -> Result<(), String>;

fn get_pinned_tabs(&self) -> Result<Option<String>, String>;
fn set_pinned_tabs(&self, v: String) -> Result<(), String>;

fn get_recently_opened_sessions(&self) -> Result<Option<String>, String>;
fn set_recently_opened_sessions(&self, v: String) -> Result<(), String>;
}

impl<R: tauri::Runtime, T: tauri::Manager<R>> AppExt<R> for T {
Expand Down Expand Up @@ -48,24 +39,6 @@ impl<R: tauri::Runtime, T: tauri::Manager<R>> AppExt<R> for T {
store.save().map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn get_dismissed_toasts(&self) -> Result<Vec<String>, String> {
let store = self.desktop_store()?;
store
.get(StoreKey::DismissedToasts)
.map(|opt| opt.unwrap_or_default())
.map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn set_dismissed_toasts(&self, v: Vec<String>) -> Result<(), String> {
let store = self.desktop_store()?;
store
.set(StoreKey::DismissedToasts, v)
.map_err(|e| e.to_string())?;
store.save().map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn get_onboarding_local(&self) -> Result<bool, String> {
let store = self.desktop_store()?;
Expand Down Expand Up @@ -100,36 +73,4 @@ impl<R: tauri::Runtime, T: tauri::Manager<R>> AppExt<R> for T {
.map_err(|e| e.to_string())?;
store.save().map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn get_pinned_tabs(&self) -> Result<Option<String>, String> {
let store = self.desktop_store()?;
store.get(StoreKey::PinnedTabs).map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn set_pinned_tabs(&self, v: String) -> Result<(), String> {
let store = self.desktop_store()?;
store
.set(StoreKey::PinnedTabs, v)
.map_err(|e| e.to_string())?;
store.save().map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn get_recently_opened_sessions(&self) -> Result<Option<String>, String> {
let store = self.desktop_store()?;
store
.get(StoreKey::RecentlyOpenedSessions)
.map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn set_recently_opened_sessions(&self, v: String) -> Result<(), String> {
let store = self.desktop_store()?;
store
.set(StoreKey::RecentlyOpenedSessions, v)
.map_err(|e| e.to_string())?;
store.save().map_err(|e| e.to_string())
}
}
6 changes: 0 additions & 6 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use ext::*;
use store::*;

use tauri::Manager;

Check warning on line 11 in apps/desktop/src-tauri/src/lib.rs

View workflow job for this annotation

GitHub Actions / desktop_ci (linux, depot-ubuntu-22.04-8)

unused import: `tauri::Manager`

Check warning on line 11 in apps/desktop/src-tauri/src/lib.rs

View workflow job for this annotation

GitHub Actions / desktop_ci (linux, depot-ubuntu-22.04-8)

unused import: `tauri::Manager`
use tauri_plugin_permissions::{Permission, PermissionsPluginExt};
use tauri_plugin_windows::{AppWindow, WindowsPluginExt};

Expand Down Expand Up @@ -328,8 +328,6 @@
.commands(tauri_specta::collect_commands![
commands::get_onboarding_needed::<tauri::Wry>,
commands::set_onboarding_needed::<tauri::Wry>,
commands::get_dismissed_toasts::<tauri::Wry>,
commands::set_dismissed_toasts::<tauri::Wry>,
commands::get_onboarding_local::<tauri::Wry>,
commands::set_onboarding_local::<tauri::Wry>,
commands::get_env::<tauri::Wry>,
Expand All @@ -338,10 +336,6 @@
commands::resize_window_for_sidebar::<tauri::Wry>,
commands::get_tinybase_values::<tauri::Wry>,
commands::set_tinybase_values::<tauri::Wry>,
commands::get_pinned_tabs::<tauri::Wry>,
commands::set_pinned_tabs::<tauri::Wry>,
commands::get_recently_opened_sessions::<tauri::Wry>,
commands::set_recently_opened_sessions::<tauri::Wry>,
])
.error_handling(tauri_specta::ErrorHandlingMode::Result)
}
Expand Down
5 changes: 2 additions & 3 deletions apps/desktop/src-tauri/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ use tauri_plugin_store2::ScopedStoreKey;

#[derive(serde::Deserialize, specta::Type, PartialEq, Eq, Hash, strum::Display)]
pub enum StoreKey {
// Also accessed from Rust
OnboardingNeeded2,
DismissedToasts,
OnboardingLocal,
// For frontend-only values, use TinybaseValues instead
TinybaseValues,
PinnedTabs,
RecentlyOpenedSessions,
}

impl ScopedStoreKey for StoreKey {}
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback, useMemo } from "react";

import { commands } from "../../../../types/tauri.gen";
import * as main from "../../../../store/tinybase/store/main";

export function useDismissedToasts(): {
dismissedToasts: string[];
dismissToast: (id: string) => void;
isDismissed: (id: string) => boolean;
} {
const queryClient = useQueryClient();

const { data: dismissedToasts = [] } = useQuery({
queryKey: ["dismissed_toasts"],
queryFn: async () => {
const result = await commands.getDismissedToasts();
if (result.status === "ok") {
return result.data;
const store = main.UI.useStore(main.STORE_ID);
const dismissedToastsValue = main.UI.useValue("dismissed_toasts", store);

const dismissedToasts = useMemo(() => {
if (typeof dismissedToastsValue === "string") {
try {
const parsed = JSON.parse(dismissedToastsValue);
if (Array.isArray(parsed)) {
return parsed.filter((id) => typeof id === "string") as string[];
}
} catch {
return [];
}
return [];
},
});
}
return [];
}, [dismissedToastsValue]);

const dismissedSet = useMemo(
() => new Set(dismissedToasts),
Expand All @@ -28,16 +31,14 @@ export function useDismissedToasts(): {

const dismissToast = useCallback(
(id: string) => {
if (dismissedSet.has(id)) {
if (!store || dismissedSet.has(id)) {
return;
}

const updated = [...dismissedToasts, id];
commands.setDismissedToasts(updated).then(() => {
queryClient.invalidateQueries({ queryKey: ["dismissed_toasts"] });
});
store.setValue("dismissed_toasts", JSON.stringify(updated));
},
[dismissedToasts, dismissedSet, queryClient],
[store, dismissedToasts, dismissedSet],
);

const isDismissed = useCallback(
Expand Down
43 changes: 22 additions & 21 deletions apps/desktop/src/routes/app/main/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import { ShellProvider } from "../../../contexts/shell";
import { useRegisterTools } from "../../../contexts/tool";
import { ToolRegistryProvider } from "../../../contexts/tool";
import { useDeeplinkHandler } from "../../../hooks/useDeeplinkHandler";
import * as main from "../../../store/tinybase/store/main";
import {
restorePinnedTabsToStore,
restoreRecentlyOpenedToStore,
usePinnedTabsSync,
useRecentlyOpenedSync,
useTabs,
} from "../../../store/zustand/tabs";

Expand All @@ -37,34 +38,34 @@ function Component() {
const liveStatus = useListener((state) => state.live.status);
const prevLiveStatus = usePrevious(liveStatus);

const store = main.UI.useStore(main.STORE_ID) as main.Store | undefined;
const pinnedTabsValue = main.UI.useValue("pinned_tabs", store);

useDeeplinkHandler();
usePinnedTabsSync();
useRecentlyOpenedSync();

const openDefaultEmptyTab = useCallback(() => {
openNew({ type: "empty" });
}, [openNew]);

useEffect(() => {
const initializeTabs = async () => {
if (!hasOpenedInitialTab.current) {
hasOpenedInitialTab.current = true;
await restorePinnedTabsToStore(
openNew,
pin,
() => useTabs.getState().tabs,
);
await restoreRecentlyOpenedToStore((ids) => {
useTabs.setState({ recentlyOpenedSessionIds: ids });
});
const currentTabs = useTabs.getState().tabs;
if (currentTabs.length === 0) {
openDefaultEmptyTab();
}
if (!hasOpenedInitialTab.current) {
// Wait for TinyBase pinned_tabs value to be loaded before checking if we need to open
// an empty tab. The sync hooks (usePinnedTabsSync, useRecentlyOpenedSync) restore tabs
// asynchronously via useEffect. Without this check, we would open an empty tab before
// the pinned tabs are restored, causing a flash of empty content on startup.
if (pinnedTabsValue === undefined) {
return;
}
};

initializeTabs();
hasOpenedInitialTab.current = true;
const currentTabs = useTabs.getState().tabs;
if (currentTabs.length === 0) {
openDefaultEmptyTab();
}
}
registerOnEmpty(openDefaultEmptyTab);
}, [openNew, pin, openDefaultEmptyTab, registerOnEmpty]);
}, [pinnedTabsValue, openDefaultEmptyTab, registerOnEmpty]);

useEffect(() => {
const justStartedListening =
Expand Down
43 changes: 14 additions & 29 deletions apps/desktop/src/store/zustand/tabs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,10 @@ import {
navigationMiddleware,
type NavigationState,
} from "./navigation";
import {
pinnedPersistenceMiddleware,
restorePinnedTabsToStore,
} from "./pinned-persistence";
import {
createRecentlyOpenedSlice,
type RecentlyOpenedActions,
recentlyOpenedMiddleware,
type RecentlyOpenedState,
restoreRecentlyOpenedToStore,
} from "./recently-opened";
import {
createRestoreSlice,
Expand All @@ -35,7 +29,8 @@ import { createStateUpdaterSlice, type StateBasicActions } from "./state";

export type { SettingsState, SettingsTab, Tab, TabInput } from "./schema";
export { isSameTab, rowIdfromTab, uniqueIdfromTab } from "./schema";
export { restorePinnedTabsToStore, restoreRecentlyOpenedToStore };
export { usePinnedTabsSync } from "./usePinnedTabsSync";
export { useRecentlyOpenedSync } from "./useRecentlyOpenedSync";

type State = BasicState &
NavigationState &
Expand All @@ -51,29 +46,19 @@ type Actions = BasicActions &
type Store = State & Actions;

export const useTabs = create<Store>()(
recentlyOpenedMiddleware(
pinnedPersistenceMiddleware(
restoreMiddleware(
lifecycleMiddleware(
navigationMiddleware((set, get) => ({
...wrapSliceWithLogging("basic", createBasicSlice(set, get)),
...wrapSliceWithLogging("state", createStateUpdaterSlice(set, get)),
...wrapSliceWithLogging(
"navigation",
createNavigationSlice(set, get),
),
...wrapSliceWithLogging(
"lifecycle",
createLifecycleSlice(set, get),
),
...wrapSliceWithLogging("restore", createRestoreSlice(set, get)),
...wrapSliceWithLogging(
"recentlyOpened",
createRecentlyOpenedSlice(set, get),
),
})),
restoreMiddleware(
lifecycleMiddleware(
navigationMiddleware((set, get) => ({
...wrapSliceWithLogging("basic", createBasicSlice(set, get)),
...wrapSliceWithLogging("state", createStateUpdaterSlice(set, get)),
...wrapSliceWithLogging("navigation", createNavigationSlice(set, get)),
...wrapSliceWithLogging("lifecycle", createLifecycleSlice(set, get)),
...wrapSliceWithLogging("restore", createRestoreSlice(set, get)),
...wrapSliceWithLogging(
"recentlyOpened",
createRecentlyOpenedSlice(set, get),
),
),
})),
),
),
);
Loading
Loading