diff --git a/app/page.js b/app/page.js index 3b218ef..e918743 100644 --- a/app/page.js +++ b/app/page.js @@ -3,16 +3,20 @@ const poppins = Poppins({ subsets: ["latin"], weight: ["800"] }) import SudokuBoard from "@/components/sudoku_board"; +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { Label } from "@/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Separator } from "@/components/ui/separator"; import { Slider } from "@/components/ui/slider"; import { Switch } from "@/components/ui/switch"; import { cn, difficultyDesc } from "@/lib/utils"; +import { getVersion } from "@tauri-apps/api/app"; import { invoke } from "@tauri-apps/api/tauri"; -import { ChevronDown, HelpCircle, Settings2 } from "lucide-react"; +import { checkUpdate, installUpdate } from "@tauri-apps/api/updater"; +import { ChevronDown, HelpCircle, Loader2, Settings2 } from "lucide-react"; import { useTheme } from "next-themes"; import { Poppins } from "next/font/google"; import Link from "next/link"; @@ -21,12 +25,27 @@ import { useEffect, useState } from "react"; export default function Home() { const [difficulty, setDifficulty] = useState(2); const [markingAssist, setMarkingAssist] = useState(false); + const [appVersion, setAppversion] = useState(""); + const [updateDialogOpen, setUpdateDialogOpen] = useState(false); + const [updateManifest, setUpdateManifest] = useState({}); + const [updating, setUpdating] = useState(false); + const [updateFailed, setUpdateFailed] = useState(false); useEffect(() => { invoke('get_difficulty').then((difficulty) => setDifficulty(difficulty)); invoke('get_marking_assist').then((markingAssist) => setMarkingAssist(markingAssist)); }, []); + useEffect(() => { + getVersion().then(setAppversion); + checkUpdate().then(({ shouldUpdate, manifest }) => { + if (shouldUpdate) { + setUpdateManifest(manifest); + setUpdateDialogOpen(true); + } + }) + }, []); + const randomPuzzle = [ [4, 0, 8, 0, 0, 0, 0, 0, 9], [0, 0, 0, 4, 9, 0, 7, 0, 0], @@ -58,6 +77,10 @@ export default function Home() {
+
+ 设置 +
+
@@ -99,7 +122,7 @@ export default function Home() { @@ -115,10 +138,79 @@ export default function Home() {
+
+ +
+ + + + + 版本更新 + + {updateManifest ? + +

发现新版本v{updateManifest.version}。更新内容:

+
+

{updateManifest.body}

+
+

是否立即更新?

+ { + updateFailed ? + <> +
+

+ 更新失败,请前往 + + 手动下载更新。 +

+ + : <> + } +
: <> + } +
+ + { + setUpdateDialogOpen(false); + setUpdateFailed(false); + setUpdating(false); + }} + > + 稍后再说 + + { + setUpdating(true); + installUpdate() + .catch(() => { + setUpdateFailed(true); + }); + }} + > + { + updating ? + <>更新中 : <>立即更新 + } + + +
+
+
diff --git a/components/ui/alert-dialog.jsx b/components/ui/alert-dialog.jsx new file mode 100644 index 0000000..92cac7b --- /dev/null +++ b/components/ui/alert-dialog.jsx @@ -0,0 +1,99 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/components/ui/separator.jsx b/components/ui/separator.jsx new file mode 100644 index 0000000..9c31480 --- /dev/null +++ b/components/ui/separator.jsx @@ -0,0 +1,25 @@ +"use client" + +import * as React from "react" +import * as SeparatorPrimitive from "@radix-ui/react-separator" + +import { cn } from "@/lib/utils" + +const Separator = React.forwardRef(( + { className, orientation = "horizontal", decorative = true, ...props }, + ref +) => ( + +)) +Separator.displayName = SeparatorPrimitive.Root.displayName + +export { Separator } diff --git a/package-lock.json b/package-lock.json index 68f7146..aaef4dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,19 @@ { - "name": "tauri-example", - "version": "0.1.0", + "name": "sudoxide", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "tauri-example", - "version": "0.1.0", + "name": "sudoxide", + "version": "0.2.0", "dependencies": { + "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slider": "^1.1.2", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", @@ -471,6 +473,34 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz", + "integrity": "sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dialog": "1.0.5", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", @@ -956,6 +986,29 @@ } } }, + "node_modules/@radix-ui/react-separator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz", + "integrity": "sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slider": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.1.2.tgz", diff --git a/package.json b/package.json index d7f67cc..012cc42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sudoxide", - "version": "0.2.1", + "version": "0.2.2", "private": true, "scripts": { "dev": "next dev", @@ -10,10 +10,12 @@ "tauri": "tauri" }, "dependencies": { + "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slider": "^1.1.2", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b9b555a..482c699 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "Sudoxide", - "version": "0.2.1" + "version": "0.2.2" }, "tauri": { "allowlist": { @@ -67,12 +67,8 @@ "endpoints": [ "https://nervonment.github.io/sudoxide_versions/update.json" ], - "dialog": true, - "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDQyNEU5RDgyQjQxODE2NDkKUldSSkZoaTBncDFPUW9KdEtMMTRsUVptUG9BUGtDUXdnd0QwdkRoays2YkZLVjdBWG1LODlmNVEK", - "windows": { - "installMode": "passive", - "installerArgs": [] - } + "dialog": false, + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDQyNEU5RDgyQjQxODE2NDkKUldSSkZoaTBncDFPUW9KdEtMMTRsUVptUG9BUGtDUXdnd0QwdkRoays2YkZLVjdBWG1LODlmNVEK" }, "windows": [ {