diff --git a/package.json b/package.json index e5aeb5d..a1e5b5c 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@headlessui/react": "^2.1.2", "@patternfly/react-log-viewer": "^5.2.0", + "@sweetalert2/theme-dark": "^5.0.18", "@tauri-apps/api": "^1", "chart.js": "^4.4.4", "react": "^18.2.0", @@ -21,7 +22,6 @@ "react-router-dom": "^6.25.1", "react-syntax-highlighter": "^15.5.0", "react-table": "^7.8.0", - "react-toastify": "^10.0.5", "sweetalert2": "^11.14.0", "tauri-plugin-store-api": "https://github.com/tauri-apps/tauri-plugin-store#v1" }, diff --git a/src/App.jsx b/src/App.jsx index b678e74..5ffafa6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,8 +2,6 @@ import React, {useEffect, useRef} from 'react'; import {BrowserRouter as Router, Route, Routes, useNavigate, useLocation} from 'react-router-dom'; import Sidebar from './components/Sidebar'; import ContainersScreen from './components/Screens/ContainersScreen'; -import {ToastContainer} from 'react-toastify'; -import 'react-toastify/dist/ReactToastify.css'; import ImagesScreen from './components/Screens/ImagesScreen'; import VolumesScreen from './components/Screens/VolumesScreen'; import NetworkScreen from './components/Screens/NetworkScreen'; @@ -55,11 +53,6 @@ function App() {
-
}/> diff --git a/src/components/Containers/ContainerDetails.jsx b/src/components/Containers/ContainerDetails.jsx index c82a3c4..5749e3f 100644 --- a/src/components/Containers/ContainerDetails.jsx +++ b/src/components/Containers/ContainerDetails.jsx @@ -5,12 +5,13 @@ import {listen} from '@tauri-apps/api/event'; import LogsViewer from '../LogsViewer'; import {IconBxTerminal, IconBxTrashAlt, IconCircleStop, IconPlayCircle, IconRestart, IconWeb} from '../../Icons'; -import {toast} from 'react-toastify'; import {useContainers} from '../../state/ContainerContext'; import LogoScreen from '../LogoScreen'; import ContainerStats from './ContainerStats'; import JSONSyntaxHighlighter from "../JSONSyntaxHighlighter.jsx"; import ContainerNameWidget from "./ContainerNameWidget.jsx"; +import Swal from "sweetalert2"; +import toast from "../../utils/toast.js"; function ContainerDetails() { @@ -93,6 +94,30 @@ function ContainerDetails() { function containerOperation(actionType) { + + let unsafeActions = ["delete"] + + if (unsafeActions.includes(actionType)) { + Swal.fire({ + title: 'Are you sure?', + showDenyButton: true, + showCancelButton: true, + confirmButtonText: 'Yes', + denyButtonText: 'No', + icon: 'warning' + }).then((result) => { + if (result.isConfirmed) { + Swal.fire('Saved!', '', 'success') + } else if (result.isDenied) { + Swal.fire('Changes are not saved', '', 'info') + } + }) + toast.success('Operation completed successfully!'); + + return + } + + setLoadingButton(actionType) invoke('container_operation', { containerName: selectedContainer.Names[0].replace("/", ""), @@ -161,13 +186,13 @@ function ContainerDetails() { : } @@ -178,7 +203,7 @@ function ContainerDetails() { disabled={!isContainerRunning} onClick={() => containerOperation("restart")} > - {loadingButton == 'restart' ? : + {loadingButton === 'restart' ? : }
@@ -187,7 +212,7 @@ function ContainerDetails() {
diff --git a/src/components/Containers/ContainerNameWidget.jsx b/src/components/Containers/ContainerNameWidget.jsx index 7f59846..be8c47b 100644 --- a/src/components/Containers/ContainerNameWidget.jsx +++ b/src/components/Containers/ContainerNameWidget.jsx @@ -2,7 +2,7 @@ import {IconCancel, IconEdit, IconTick} from "../../Icons/index.jsx"; import React, {useState} from "react"; import {invoke} from "@tauri-apps/api"; import {useContainers} from "../../state/ContainerContext.jsx"; -import {toast} from "react-toastify"; +import toast from "../../utils/toast.js"; export default function ContainerNameWidget() { const {selectedContainer, refreshSelectedContainer} = useContainers(); diff --git a/src/components/Images/ImageDetails.jsx b/src/components/Images/ImageDetails.jsx index 55dacb4..6e7680e 100644 --- a/src/components/Images/ImageDetails.jsx +++ b/src/components/Images/ImageDetails.jsx @@ -1,6 +1,6 @@ import {invoke} from '@tauri-apps/api'; import React, {useEffect, useState} from 'react'; -import {toast} from 'react-toastify'; +import toast from '../../utils/toast.js'; import {useImages} from "../../state/ImagesContext"; import {IconBxExport, IconBxTrashAlt, IconCopy} from '../../Icons'; import ImageHistory from './ImageHistory'; @@ -54,7 +54,7 @@ function ImageDetails() { force: forceDelete, noPrune: noPrune }).then((res) => { - toast.success(res); + toast.success('Image successfully deleted!'); setSelectedImage(null); loadImages(); }).catch((err) => { diff --git a/src/components/Images/ImageHistory.jsx b/src/components/Images/ImageHistory.jsx index 8f0d0a0..442c497 100644 --- a/src/components/Images/ImageHistory.jsx +++ b/src/components/Images/ImageHistory.jsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { useImages } from '../../state/ImagesContext' import { formatSize, formatDate } from '../../utils'; import { invoke } from '@tauri-apps/api'; -import { toast } from 'react-toastify'; +import toast from '../../utils/toast.js'; diff --git a/src/components/Screens/SettingsScreen.jsx b/src/components/Screens/SettingsScreen.jsx index e330e59..f8d909a 100644 --- a/src/components/Screens/SettingsScreen.jsx +++ b/src/components/Screens/SettingsScreen.jsx @@ -1,14 +1,15 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { capitalizeFirstLetter } from '../../utils'; -import { ALL_THEMES, DEFAULT_THEME, DOCKER_TERMINAL } from '../../constants'; -import { useSettings } from '../../state/SettingsContext'; -import { getVersion } from "@tauri-apps/api/app"; -import { checkUpdate, installUpdate } from "@tauri-apps/api/updater"; -import { relaunch } from "@tauri-apps/api/process"; -import { IconGithub } from "../../Icons/index.jsx"; +import React, {useCallback, useEffect, useState} from 'react'; +import {capitalizeFirstLetter} from '../../utils'; +import {ALL_THEMES, DEFAULT_THEME, DOCKER_TERMINAL} from '../../constants'; +import {useSettings} from '../../state/SettingsContext'; +import {getVersion} from "@tauri-apps/api/app"; +import {checkUpdate, installUpdate} from "@tauri-apps/api/updater"; +import {relaunch} from "@tauri-apps/api/process"; +import {IconGithub} from "../../Icons/index.jsx"; import Swal from "sweetalert2"; -import { invoke } from '@tauri-apps/api'; -import { reteriveValue, storeValue } from "../../utils/storage.js"; +import {invoke} from '@tauri-apps/api'; +import {reteriveValue, storeValue} from "../../utils/storage.js"; +import toast from "../../utils/toast.js"; const SettingsScreen = () => { const [theme, setTheme] = useState(DEFAULT_THEME); @@ -42,14 +43,7 @@ const SettingsScreen = () => { try { const { shouldUpdate, manifest } = await checkUpdate(); if (!shouldUpdate) { - Swal.fire({ - position: "bottom-right", - icon: "success", - title: "Your app is up to date.", - showConfirmButton: false, - timer: 1500, - toast: true - }); + toast.success("Your app is up to date.") return; } console.log(`Installing update ${manifest?.version}, ${manifest?.date}, ${manifest?.body}`); @@ -60,7 +54,7 @@ const SettingsScreen = () => { Swal.fire({ icon: "error", title: "Oops...", - text: error.message, + text: error.message ? error.message : "Failed to check for updates.", footer: 'Visit Latest Release Page' }); } finally { diff --git a/src/utils/clipboard.js b/src/utils/clipboard.js index 8341d81..df1a76c 100644 --- a/src/utils/clipboard.js +++ b/src/utils/clipboard.js @@ -1,4 +1,4 @@ -import { toast } from "react-toastify"; +import toast from "./toast.js"; export const copyToClipboard = (text) => { diff --git a/src/utils/toast.js b/src/utils/toast.js new file mode 100644 index 0000000..4b196e1 --- /dev/null +++ b/src/utils/toast.js @@ -0,0 +1,53 @@ +import Swal from 'sweetalert2'; +import 'sweetalert2/dist/sweetalert2.min.css'; +import '@sweetalert2/theme-dark/dark.css'; + +const toast = (() => { + let currentLibrary = 'sweetalert2'; + + + const showToast = (message, type = 'info', duration = 3000) => { + const timerProgressColors = { + success: "bg-success", + info: "bg-info", + warning: "bg-warning", + error: "bg-error" + }; + if (currentLibrary === 'sweetalert2') { + Swal.fire({ + toast: true, + position: 'bottom-right', + showConfirmButton: false, + timer: duration, + timerProgressBar: true, + icon: type, + title: message, + background: 'oklch(var(--b2))', + customClass: { + popup: 'text-base-content', + timerProgressBar: timerProgressColors[type] + }, + }); + } else if (currentLibrary === 'react-toast') { + console.log(`React-toast: ${type} - ${message}`); + } + }; + + const setLibrary = (library) => { + if (['sweetalert2', 'react-toast'].includes(library)) { + currentLibrary = library; + } else { + console.error('Unsupported library'); + } + }; + + return { + success: (message, duration) => showToast(message, 'success', duration), + error: (message, duration) => showToast(message, 'error', duration), + warning: (message, duration) => showToast(message, 'warning', duration), + info: (message, duration) => showToast(message, 'info', duration), + setLibrary + }; +})(); + +export default toast; diff --git a/yarn.lock b/yarn.lock index 8adec59..69af384 100644 --- a/yarn.lock +++ b/yarn.lock @@ -609,6 +609,11 @@ dependencies: tslib "^2.4.0" +"@sweetalert2/theme-dark@^5.0.18": + version "5.0.18" + resolved "https://registry.yarnpkg.com/@sweetalert2/theme-dark/-/theme-dark-5.0.18.tgz#e070da21b74eec8ba3420c26cf5bd64ed480dd05" + integrity sha512-Fdt8OQHQcbJy6i+rvA49h3OAzQevMwDgfsHPdR2kNwT5M7AtG5rAaBBo0StlvNbcTx/AQ5xhEdMyJdnM05CNoQ== + "@tanstack/react-virtual@^3.8.1": version "3.10.8" resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.10.8.tgz#bf4b06f157ed298644a96ab7efc1a2b01ab36e3c" @@ -907,7 +912,7 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -clsx@^2.0.0, clsx@^2.1.0: +clsx@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== @@ -1628,13 +1633,6 @@ react-table@^7.8.0: resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.8.0.tgz#07858c01c1718c09f7f1aed7034fcfd7bda907d2" integrity sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA== -react-toastify@^10.0.5: - version "10.0.5" - resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-10.0.5.tgz#6b8f8386060c5c856239f3036d1e76874ce3bd1e" - integrity sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw== - dependencies: - clsx "^2.1.0" - react@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"