Show error when enabling encryption fails

This commit is contained in:
Gregory Schier
2025-11-03 14:34:43 -08:00
parent bf97ea1659
commit 7e1eb90d29
3 changed files with 110 additions and 70 deletions

View File

@@ -3,7 +3,7 @@ use crate::window_menu::app_menu;
use log::{info, warn};
use rand::random;
use tauri::{
AppHandle, Emitter, LogicalSize, Manager, Runtime, WebviewUrl, WebviewWindow, WindowEvent,
AppHandle, Emitter, LogicalSize, Manager, PhysicalSize, Runtime, WebviewUrl, WebviewWindow, WindowEvent
};
use tauri_plugin_opener::OpenerExt;
use tokio::sync::mpsc;
@@ -160,6 +160,11 @@ pub(crate) fn create_window<R: Runtime>(
"dev.reset_size" => webview_window
.set_size(LogicalSize::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT))
.unwrap(),
"dev.reset_size_record" => {
let width = webview_window.outer_size().unwrap().width;
let height = width * 9 / 16;
webview_window.set_size(PhysicalSize::new(width, height)).unwrap()
}
"dev.refresh" => webview_window.eval("location.reload()").unwrap(),
"dev.generate_theme_css" => {
w.emit("generate_theme_css", true).unwrap();

View File

@@ -143,6 +143,8 @@ pub fn app_menu<R: Runtime>(app_handle: &AppHandle<R>) -> tauri::Result<Menu<R>>
.build(app_handle)?,
&MenuItemBuilder::with_id("dev.reset_size".to_string(), "Reset Size")
.build(app_handle)?,
&MenuItemBuilder::with_id("dev.reset_size_record".to_string(), "Reset Size 16x9")
.build(app_handle)?,
&MenuItemBuilder::with_id(
"dev.generate_theme_css".to_string(),
"Generate Theme CSS",

View File

@@ -1,35 +1,49 @@
import { enableEncryption, revealWorkspaceKey, setWorkspaceKey } from '@yaakapp-internal/crypto';
import type { WorkspaceMeta } from '@yaakapp-internal/models';
import classNames from 'classnames';
import { useAtomValue } from 'jotai';
import { useEffect, useState } from 'react';
import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from '../hooks/useActiveWorkspace';
import { createFastMutation } from '../hooks/useFastMutation';
import { useStateWithDeps } from '../hooks/useStateWithDeps';
import { CopyIconButton } from './CopyIconButton';
import { Banner } from './core/Banner';
import type { ButtonProps } from './core/Button';
import { Button } from './core/Button';
import { IconButton } from './core/IconButton';
import { IconTooltip } from './core/IconTooltip';
import { Label } from './core/Label';
import { PlainInput } from './core/PlainInput';
import { HStack, VStack } from './core/Stacks';
import { EncryptionHelp } from './EncryptionHelp';
import {
enableEncryption,
revealWorkspaceKey,
setWorkspaceKey,
} from "@yaakapp-internal/crypto";
import type { WorkspaceMeta } from "@yaakapp-internal/models";
import classNames from "classnames";
import { useAtomValue } from "jotai";
import { useEffect, useState } from "react";
import {
activeWorkspaceAtom,
activeWorkspaceMetaAtom,
} from "../hooks/useActiveWorkspace";
import { createFastMutation } from "../hooks/useFastMutation";
import { useStateWithDeps } from "../hooks/useStateWithDeps";
import { CopyIconButton } from "./CopyIconButton";
import { Banner } from "./core/Banner";
import type { ButtonProps } from "./core/Button";
import { Button } from "./core/Button";
import { IconButton } from "./core/IconButton";
import { IconTooltip } from "./core/IconTooltip";
import { Label } from "./core/Label";
import { PlainInput } from "./core/PlainInput";
import { HStack, VStack } from "./core/Stacks";
import { EncryptionHelp } from "./EncryptionHelp";
interface Props {
size?: ButtonProps['size'];
size?: ButtonProps["size"];
expanded?: boolean;
onDone?: () => void;
onEnabledEncryption?: () => void;
}
export function WorkspaceEncryptionSetting({ size, expanded, onDone, onEnabledEncryption }: Props) {
const [justEnabledEncryption, setJustEnabledEncryption] = useState<boolean>(false);
export function WorkspaceEncryptionSetting(
{ size, expanded, onDone, onEnabledEncryption }: Props,
) {
const [justEnabledEncryption, setJustEnabledEncryption] = useState<boolean>(
false,
);
const [error, setError] = useState<string | null>(null);
const workspace = useAtomValue(activeWorkspaceAtom);
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);
const [key, setKey] = useState<{ key: string | null; error: string | null } | null>(null);
const [key, setKey] = useState<
{ key: string | null; error: string | null } | null
>(null);
useEffect(() => {
if (workspaceMeta == null) {
@@ -108,30 +122,39 @@ export function WorkspaceEncryptionSetting({ size, expanded, onDone, onEnabledEn
return (
<div className="mb-auto flex flex-col-reverse">
<Button
color={expanded ? 'info' : 'secondary'}
color={expanded ? "info" : "secondary"}
size={size}
onClick={async () => {
setJustEnabledEncryption(true);
await enableEncryption(workspaceMeta.workspaceId);
setError(null);
try {
throw new Error("Platform secure storage error");
await enableEncryption(workspaceMeta.workspaceId);
setJustEnabledEncryption(true);
} catch (err) {
setError("Failed to enable encryption: " + err);
}
}}
>
Enable Encryption
</Button>
{expanded ? (
<Banner color="info" className="mb-6">
<EncryptionHelp />
</Banner>
) : (
<Label htmlFor={null} help={<EncryptionHelp />}>
Workspace encryption
</Label>
)}
{error && <Banner color="danger" className="mb-2">{error}</Banner>}
{expanded
? (
<Banner color="info" className="mb-6">
<EncryptionHelp />
</Banner>
)
: (
<Label htmlFor={null} help={<EncryptionHelp />}>
Workspace encryption
</Label>
)}
</div>
);
}
const setWorkspaceKeyMut = createFastMutation({
mutationKey: ['set-workspace-key'],
mutationKey: ["set-workspace-key"],
mutationFn: setWorkspaceKey,
});
@@ -144,15 +167,13 @@ function EnterWorkspaceKey({
onEnabled?: () => void;
error?: string | null;
}) {
const [key, setKey] = useState<string>('');
const [key, setKey] = useState<string>("");
return (
<VStack space={4} className="w-full">
{error ? (
<Banner color="danger">{error}</Banner>
) : (
{error ? <Banner color="danger">{error}</Banner> : (
<Banner color="info">
This workspace contains encrypted values but no key is configured. Please enter the
workspace key to access the encrypted data.
This workspace contains encrypted values but no key is configured.
Please enter the workspace key to access the encrypted data.
</Banner>
)}
<HStack
@@ -199,24 +220,35 @@ function KeyRevealer({
return (
<div
className={classNames(
'w-full border border-border rounded-md pl-3 py-2 p-1',
'grid gap-1 grid-cols-[minmax(0,1fr)_auto] items-center',
"w-full border border-border rounded-md pl-3 py-2 p-1",
"grid gap-1 grid-cols-[minmax(0,1fr)_auto] items-center",
)}
>
<VStack space={0.5}>
{!disableLabel && (
<span className="text-sm text-primary flex items-center gap-1">
Workspace encryption key{' '}
<IconTooltip iconSize="sm" size="lg" content={helpAfterEncryption} />
Workspace encryption key{" "}
<IconTooltip
iconSize="sm"
size="lg"
content={helpAfterEncryption}
/>
</span>
)}
{encryptionKey && <HighlightedKey keyText={encryptionKey} show={show} />}
{encryptionKey && (
<HighlightedKey
keyText={encryptionKey}
show={show}
/>
)}
</VStack>
<HStack>
{encryptionKey && <CopyIconButton text={encryptionKey} title="Copy workspace key" />}
{encryptionKey && (
<CopyIconButton text={encryptionKey} title="Copy workspace key" />
)}
<IconButton
title={show ? 'Hide' : 'Reveal' + 'workspace key'}
icon={show ? 'eye_closed' : 'eye'}
title={show ? "Hide" : "Reveal" + "workspace key"}
icon={show ? "eye_closed" : "eye"}
onClick={() => setShow((v) => !v)}
/>
</HStack>
@@ -227,31 +259,32 @@ function KeyRevealer({
function HighlightedKey({ keyText, show }: { keyText: string; show: boolean }) {
return (
<span className="text-xs font-mono [&_*]:cursor-auto [&_*]:select-text">
{show ? (
keyText.split('').map((c, i) => {
return (
<span
key={i}
className={classNames(
c.match(/[0-9]/) && 'text-info',
c == '-' && 'text-text-subtle',
)}
>
{c}
</span>
);
})
) : (
<div className="text-text-subtle"></div>
)}
{show
? (
keyText.split("").map((c, i) => {
return (
<span
key={i}
className={classNames(
c.match(/[0-9]/) && "text-info",
c == "-" && "text-text-subtle",
)}
>
{c}
</span>
);
})
)
: <div className="text-text-subtle"></div>}
</span>
);
}
const helpAfterEncryption = (
<p>
The following key is used for encryption operations within this workspace. It is stored securely
using your OS keychain, but it is recommended to back it up. If you share this workspace with
others, you&apos;ll need to send them this key to access any encrypted values.
The following key is used for encryption operations within this workspace.
It is stored securely using your OS keychain, but it is recommended to back
it up. If you share this workspace with others, you&apos;ll need to send
them this key to access any encrypted values.
</p>
);