Skip to content
Draft
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
10 changes: 10 additions & 0 deletions apps/desktop/src/lib/trpc/routers/auto-update/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { observable } from "@trpc/server/observable";
import { app } from "electron";
import {
type AutoUpdateStatusEvent,
autoUpdateEmitter,
checkForUpdates,
checkForUpdatesInteractive,
dismissUpdate,
getUpdateStatus,
installUpdate,
Expand Down Expand Up @@ -34,10 +36,18 @@ export const createAutoUpdateRouter = () => {
return getUpdateStatus();
}),

getVersion: publicProcedure.query(() => {
return app.getVersion();
}),

check: publicProcedure.mutation(() => {
checkForUpdates();
}),

checkForUpdates: publicProcedure.mutation(() => {
checkForUpdatesInteractive();
}),

install: publicProcedure.mutation(() => {
installUpdate();
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,42 @@ import { Avatar, AvatarFallback, AvatarImage } from "@superset/ui/avatar";
import { Button } from "@superset/ui/button";
import { Skeleton } from "@superset/ui/skeleton";
import { toast } from "@superset/ui/sonner";
import { HiOutlineClipboardDocument } from "react-icons/hi2";
import { trpc } from "renderer/lib/trpc";
import { AUTO_UPDATE_STATUS } from "shared/auto-update";
import {
isItemVisible,
SETTING_ITEM_ID,
type SettingItemId,
} from "../settings-search";

export function AccountSettings() {
interface AccountSettingsProps {
visibleItems?: SettingItemId[] | null;
}

export function AccountSettings({ visibleItems }: AccountSettingsProps) {
const showProfile = isItemVisible(
SETTING_ITEM_ID.ACCOUNT_PROFILE,
visibleItems,
);
const showVersion = isItemVisible(
SETTING_ITEM_ID.ACCOUNT_VERSION,
visibleItems,
);
const showSignOut = isItemVisible(
SETTING_ITEM_ID.ACCOUNT_SIGNOUT,
visibleItems,
);
const { data: user, isLoading } = trpc.user.me.useQuery();
const { data: version } = trpc.autoUpdate.getVersion.useQuery();
const { data: updateStatus } = trpc.autoUpdate.getStatus.useQuery();
const checkForUpdatesMutation = trpc.autoUpdate.checkForUpdates.useMutation();
const signOutMutation = trpc.auth.signOut.useMutation({
onSuccess: () => toast.success("Signed out"),
});

const signOut = () => signOutMutation.mutate();
const isChecking = updateStatus?.status === AUTO_UPDATE_STATUS.CHECKING;

const initials = user?.name
?.split(" ")
Expand All @@ -20,7 +47,7 @@ export function AccountSettings() {
.slice(0, 2);

return (
<div className="p-6 max-w-4xl">
<div className="p-6 max-w-4xl min-w-[500px]">
<div className="mb-8">
<h2 className="text-xl font-semibold">Account</h2>
<p className="text-sm text-muted-foreground mt-1">
Expand All @@ -30,46 +57,84 @@ export function AccountSettings() {

<div className="space-y-8">
{/* Profile Section */}
<div>
<h3 className="text-sm font-medium mb-4">Profile</h3>
<div className="flex items-center gap-4 p-4 rounded-lg border bg-card">
{isLoading ? (
<>
<Skeleton className="h-16 w-16 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-5 w-32" />
<Skeleton className="h-4 w-48" />
</div>
</>
) : user ? (
<>
<Avatar className="h-16 w-16">
<AvatarImage src={user.avatarUrl ?? undefined} />
<AvatarFallback className="text-lg">
{initials || "?"}
</AvatarFallback>
</Avatar>
<div>
<p className="font-medium text-lg">{user.name}</p>
<p className="text-sm text-muted-foreground">{user.email}</p>
</div>
</>
) : (
<p className="text-muted-foreground">Unable to load user info</p>
)}
{showProfile && (
<div>
<h3 className="text-sm font-medium mb-4">Profile</h3>
<div className="flex items-center gap-4 p-4 rounded-lg border bg-card">
{isLoading ? (
<>
<Skeleton className="h-16 w-16 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-5 w-32" />
<Skeleton className="h-4 w-48" />
</div>
</>
) : user ? (
<>
<Avatar className="h-16 w-16">
<AvatarImage src={user.avatarUrl ?? undefined} />
<AvatarFallback className="text-lg">
{initials || "?"}
</AvatarFallback>
</Avatar>
<div>
<p className="font-medium text-lg">{user.name}</p>
<p className="text-sm text-muted-foreground">
{user.email}
</p>
</div>
</>
) : (
<p className="text-muted-foreground">
Unable to load user info
</p>
)}
</div>
</div>
)}

{/* Version */}
{showVersion && (
<div className="pt-6 border-t">
<div className="flex items-start justify-between">
<div className="space-y-1">
<p className="text-sm text-muted-foreground">Version</p>
<button
type="button"
className="flex items-center gap-2 text-sm font-mono hover:text-foreground text-muted-foreground"
onClick={() => {
navigator.clipboard.writeText(version ?? "");
}}
>
<HiOutlineClipboardDocument className="h-4 w-4" />
{version}
</button>
</div>
<div className="text-right space-y-1">
<button
type="button"
className="text-sm text-primary hover:underline disabled:opacity-50"
onClick={() => checkForUpdatesMutation.mutate()}
disabled={isChecking}
>
Check for updates
</button>
<p className="text-sm text-muted-foreground">
{isChecking ? "Checking..." : "Up to date"}
</p>
Comment on lines +122 to +124
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Status text doesn't reflect actual update state.

The text always shows "Up to date" when not checking, but updateStatus can have other states like READY (update available) or ERROR. This could mislead users.

🔎 Proposed fix
 								<p className="text-sm text-muted-foreground">
-									{isChecking ? "Checking..." : "Up to date"}
+									{updateStatus?.status === AUTO_UPDATE_STATUS.CHECKING
+										? "Checking..."
+										: updateStatus?.status === AUTO_UPDATE_STATUS.READY
+											? "Update available"
+											: updateStatus?.status === AUTO_UPDATE_STATUS.ERROR
+												? "Check failed"
+												: updateStatus?.status === AUTO_UPDATE_STATUS.DOWNLOADING
+													? "Downloading..."
+													: "Up to date"}
 								</p>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p className="text-sm text-muted-foreground">
{isChecking ? "Checking..." : "Up to date"}
</p>
<p className="text-sm text-muted-foreground">
{updateStatus?.status === AUTO_UPDATE_STATUS.CHECKING
? "Checking..."
: updateStatus?.status === AUTO_UPDATE_STATUS.READY
? "Update available"
: updateStatus?.status === AUTO_UPDATE_STATUS.ERROR
? "Check failed"
: updateStatus?.status === AUTO_UPDATE_STATUS.DOWNLOADING
? "Downloading..."
: "Up to date"}
</p>
🤖 Prompt for AI Agents
In
apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx
around lines 109-111, the status text currently shows "Checking..." only when
isChecking is true and otherwise always "Up to date", which ignores updateStatus
values like READY or ERROR; change the render to derive the displayed string
from updateStatus when not checking (e.g., map READY -> "Update available",
ERROR -> "Update error", IDLE or UPDATED -> "Up to date") or use a switch/lookup
to produce the correct message, keeping "Checking..." when isChecking is true
and ensure any unexpected updateStatus falls back to a sensible default.

</div>
</div>
</div>
</div>
)}

{/* Sign Out Section */}
<div className="pt-6 border-t">
<h3 className="text-sm font-medium mb-2">Sign Out</h3>
<p className="text-sm text-muted-foreground mb-4">
Sign out of your Superset account on this device.
</p>
<Button variant="outline" onClick={() => signOut()}>
Sign Out
</Button>
</div>
{/* Sign Out */}
{showSignOut && (
<div className="pt-6 border-t">
<Button variant="outline" onClick={() => signOut()}>
Sign Out
</Button>
</div>
)}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,30 @@ import {
useThemeStore,
} from "renderer/stores";
import { builtInThemes } from "shared/themes";
import {
isItemVisible,
SETTING_ITEM_ID,
type SettingItemId,
} from "./settings-search";
import { ThemeCard } from "./ThemeCard";

export function AppearanceSettings() {
interface AppearanceSettingsProps {
visibleItems?: SettingItemId[] | null;
}

export function AppearanceSettings({ visibleItems }: AppearanceSettingsProps) {
const showTheme = isItemVisible(
SETTING_ITEM_ID.APPEARANCE_THEME,
visibleItems,
);
const showMarkdown = isItemVisible(
SETTING_ITEM_ID.APPEARANCE_MARKDOWN,
visibleItems,
);
const showCustomThemes = isItemVisible(
SETTING_ITEM_ID.APPEARANCE_CUSTOM_THEMES,
visibleItems,
);
const activeThemeId = useThemeId();
const setTheme = useSetTheme();
const customThemes = useThemeStore((state) => state.customThemes);
Expand All @@ -36,50 +57,58 @@ export function AppearanceSettings() {

<div className="space-y-8">
{/* Theme Section */}
<div>
<h3 className="text-sm font-medium mb-4">Theme</h3>
<div className="grid grid-cols-2 lg:grid-cols-3 gap-4">
{allThemes.map((theme) => (
<ThemeCard
key={theme.id}
theme={theme}
isSelected={activeThemeId === theme.id}
onSelect={() => setTheme(theme.id)}
/>
))}
{showTheme && (
<div>
<h3 className="text-sm font-medium mb-4">Theme</h3>
<div className="grid grid-cols-2 lg:grid-cols-3 gap-4">
{allThemes.map((theme) => (
<ThemeCard
key={theme.id}
theme={theme}
isSelected={activeThemeId === theme.id}
onSelect={() => setTheme(theme.id)}
/>
))}
</div>
</div>
</div>
)}

<div className="pt-6 border-t">
<h3 className="text-sm font-medium mb-2">Markdown Style</h3>
<p className="text-sm text-muted-foreground mb-4">
Rendering style for markdown files when viewing rendered content
</p>
<Select
value={markdownStyle}
onValueChange={(value) => setMarkdownStyle(value as MarkdownStyle)}
>
<SelectTrigger className="w-[200px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="default">Default</SelectItem>
<SelectItem value="tufte">Tufte</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground mt-2">
Tufte style uses elegant serif typography inspired by Edward Tufte's
books
</p>
</div>
{showMarkdown && (
<div className="pt-6 border-t">
<h3 className="text-sm font-medium mb-2">Markdown Style</h3>
<p className="text-sm text-muted-foreground mb-4">
Rendering style for markdown files when viewing rendered content
</p>
<Select
value={markdownStyle}
onValueChange={(value) =>
setMarkdownStyle(value as MarkdownStyle)
}
>
<SelectTrigger className="w-[200px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="default">Default</SelectItem>
<SelectItem value="tufte">Tufte</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground mt-2">
Tufte style uses elegant serif typography inspired by Edward
Tufte's books
</p>
</div>
)}

<div className="pt-6 border-t">
<h3 className="text-sm font-medium mb-2">Custom Themes</h3>
<p className="text-sm text-muted-foreground">
Custom theme import coming soon. You'll be able to import JSON theme
files to create your own themes.
</p>
</div>
{showCustomThemes && (
<div className="pt-6 border-t">
<h3 className="text-sm font-medium mb-2">Custom Themes</h3>
<p className="text-sm text-muted-foreground">
Custom theme import coming soon. You'll be able to import JSON
theme files to create your own themes.
</p>
</div>
)}
</div>
</div>
);
Expand Down
Loading