mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 20:00:29 +01:00
Show error when enabling encryption fails
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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'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'll need to send
|
||||
them this key to access any encrypted values.
|
||||
</p>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user