@checkstack/theme-frontend 0.0.6 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # @checkstack/theme-frontend
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [4eed42d]
8
+ - @checkstack/frontend-api@0.3.0
9
+ - @checkstack/auth-frontend@0.3.1
10
+ - @checkstack/ui@0.2.2
11
+
12
+ ## 0.1.0
13
+
14
+ ### Minor Changes
15
+
16
+ - 7a23261: ## TanStack Query Integration
17
+
18
+ Migrated all frontend components to use `usePluginClient` hook with TanStack Query integration, replacing the legacy `forPlugin()` pattern.
19
+
20
+ ### New Features
21
+
22
+ - **`usePluginClient` hook**: Provides type-safe access to plugin APIs with `.useQuery()` and `.useMutation()` methods
23
+ - **Automatic request deduplication**: Multiple components requesting the same data share a single network request
24
+ - **Built-in caching**: Configurable stale time and cache duration per query
25
+ - **Loading/error states**: TanStack Query provides `isLoading`, `error`, `isRefetching` states automatically
26
+ - **Background refetching**: Stale data is automatically refreshed when components mount
27
+
28
+ ### Contract Changes
29
+
30
+ All RPC contracts now require `operationType: "query"` or `operationType: "mutation"` metadata:
31
+
32
+ ```typescript
33
+ const getItems = proc()
34
+ .meta({ operationType: "query", access: [access.read] })
35
+ .output(z.array(itemSchema))
36
+ .query();
37
+
38
+ const createItem = proc()
39
+ .meta({ operationType: "mutation", access: [access.manage] })
40
+ .input(createItemSchema)
41
+ .output(itemSchema)
42
+ .mutation();
43
+ ```
44
+
45
+ ### Migration
46
+
47
+ ```typescript
48
+ // Before (forPlugin pattern)
49
+ const api = useApi(myPluginApiRef);
50
+ const [items, setItems] = useState<Item[]>([]);
51
+ useEffect(() => {
52
+ api.getItems().then(setItems);
53
+ }, [api]);
54
+
55
+ // After (usePluginClient pattern)
56
+ const client = usePluginClient(MyPluginApi);
57
+ const { data: items, isLoading } = client.getItems.useQuery({});
58
+ ```
59
+
60
+ ### Bug Fixes
61
+
62
+ - Fixed `rpc.test.ts` test setup for middleware type inference
63
+ - Fixed `SearchDialog` to use `setQuery` instead of deprecated `search` method
64
+ - Fixed null→undefined warnings in notification and queue frontends
65
+
66
+ ### Patch Changes
67
+
68
+ - Updated dependencies [7a23261]
69
+ - @checkstack/frontend-api@0.2.0
70
+ - @checkstack/common@0.3.0
71
+ - @checkstack/auth-frontend@0.3.0
72
+ - @checkstack/theme-common@0.1.0
73
+ - @checkstack/ui@0.2.1
74
+
3
75
  ## 0.0.6
4
76
 
5
77
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/theme-frontend",
3
- "version": "0.0.6",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "scripts": {
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef } from "react";
2
- import { useApi, rpcApiRef } from "@checkstack/frontend-api";
2
+ import { useApi, usePluginClient } from "@checkstack/frontend-api";
3
3
  import { authApiRef } from "@checkstack/auth-frontend/api";
4
4
  import { useTheme } from "@checkstack/ui";
5
5
  import { ThemeApi } from "@checkstack/theme-common";
@@ -17,14 +17,22 @@ import { ThemeApi } from "@checkstack/theme-common";
17
17
  export const ThemeSynchronizer = () => {
18
18
  const { setTheme } = useTheme();
19
19
  const authApi = useApi(authApiRef);
20
- const rpcApi = useApi(rpcApiRef);
21
- const themeClient = rpcApi.forPlugin(ThemeApi);
20
+ const themeClient = usePluginClient(ThemeApi);
22
21
  const { data: session, isPending } = authApi.useSession();
23
22
 
24
23
  // Track if we've already synced for this session to avoid repeated API calls
25
24
  const hasSyncedRef = useRef(false);
26
25
  const lastUserIdRef = useRef<string | undefined>(undefined);
27
26
 
27
+ // Fetch theme from backend - only enabled when user is logged in
28
+ const { data: themeData, isSuccess } = themeClient.getTheme.useQuery(
29
+ undefined,
30
+ {
31
+ enabled: !!session?.user && !isPending && !hasSyncedRef.current,
32
+ staleTime: Infinity, // Don't refetch automatically
33
+ }
34
+ );
35
+
28
36
  useEffect(() => {
29
37
  // Wait for session to load
30
38
  if (isPending) {
@@ -44,24 +52,16 @@ export const ThemeSynchronizer = () => {
44
52
  return;
45
53
  }
46
54
 
47
- // For logged-in users, fetch theme from backend
48
- if (session?.user) {
49
- themeClient
50
- .getTheme()
51
- .then(({ theme }) => {
52
- setTheme(theme);
53
- hasSyncedRef.current = true;
54
- })
55
- .catch((error) => {
56
- console.error("Failed to sync theme from backend:", error);
57
- hasSyncedRef.current = true; // Still mark as synced to prevent retry loops
58
- });
59
- } else {
55
+ // For logged-in users, apply theme from backend when query succeeds
56
+ if (session?.user && isSuccess && themeData) {
57
+ setTheme(themeData.theme);
58
+ hasSyncedRef.current = true;
59
+ } else if (!session?.user) {
60
60
  // For non-logged-in users, local storage theme is already applied
61
61
  // by ThemeProvider, so nothing to do
62
62
  hasSyncedRef.current = true;
63
63
  }
64
- }, [session, isPending, setTheme, themeClient]);
64
+ }, [session, isPending, setTheme, themeData, isSuccess]);
65
65
 
66
66
  // Headless component - renders nothing
67
67
  return <></>;
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { Moon, Sun } from "lucide-react";
3
3
  import { Toggle, useTheme, useToast } from "@checkstack/ui";
4
- import { useApi, rpcApiRef } from "@checkstack/frontend-api";
4
+ import { usePluginClient } from "@checkstack/frontend-api";
5
5
  import { ThemeApi } from "@checkstack/theme-common";
6
6
 
7
7
  /**
@@ -14,10 +14,9 @@ import { ThemeApi } from "@checkstack/theme-common";
14
14
  */
15
15
  export const ThemeToggleMenuItem = () => {
16
16
  const { theme, setTheme } = useTheme();
17
- const rpcApi = useApi(rpcApiRef);
18
- const themeClient = rpcApi.forPlugin(ThemeApi);
17
+ const themeClient = usePluginClient(ThemeApi);
18
+ const setThemeMutation = themeClient.setTheme.useMutation();
19
19
 
20
- const [saving, setSaving] = useState(false);
21
20
  const [isDark, setIsDark] = useState(theme === "dark");
22
21
  const toast = useToast();
23
22
 
@@ -34,9 +33,8 @@ export const ThemeToggleMenuItem = () => {
34
33
  setTheme(newTheme); // Also updates local storage via ThemeProvider
35
34
 
36
35
  // Save to backend
37
- setSaving(true);
38
36
  try {
39
- await themeClient.setTheme({ theme: newTheme });
37
+ await setThemeMutation.mutateAsync({ theme: newTheme });
40
38
  } catch (error) {
41
39
  const message =
42
40
  error instanceof Error
@@ -47,8 +45,6 @@ export const ThemeToggleMenuItem = () => {
47
45
  // Revert on error
48
46
  setIsDark(!checked);
49
47
  setTheme(checked ? "light" : "dark");
50
- } finally {
51
- setSaving(false);
52
48
  }
53
49
  };
54
50
 
@@ -61,7 +57,7 @@ export const ThemeToggleMenuItem = () => {
61
57
  <Toggle
62
58
  checked={isDark}
63
59
  onCheckedChange={handleToggle}
64
- disabled={saving}
60
+ disabled={setThemeMutation.isPending}
65
61
  aria-label="Toggle dark mode"
66
62
  />
67
63
  </div>