Merge pull request #348 from forumi0721/20250223/ImageRefresh

Add Refresh Button for Image List
This commit is contained in:
wenjie
2025-03-05 14:05:51 +08:00
committed by GitHub
4 changed files with 51 additions and 18 deletions

View File

@@ -96,6 +96,7 @@ const en = {
mountFailed: 'Mount Failed',
mountDesc:
"In some systems, it's necessary to eject the virtual disk on the remote host before mounting the image.",
refresh: 'Refresh the image list',
tips: {
title: 'How to upload',
usb1: 'Connect the NanoKVM to your computer via USB.',

View File

@@ -14,8 +14,7 @@ const ko = {
placeholderPassword2: '비밀번호를 다시 입력하세요.',
noEmptyUsername: '사용자 이름은 비어있을 수 없습니다.',
noEmptyPassword: '비밀번호는 비어있을 수 없습니다.',
noAccount:
'사용자 정보를 불러오는 데 실패했습니다. 페이지를 새로고침하거나 비밀번호를 초기화하세요.',
noAccount: '사용자 정보를 불러오는 데 실패했습니다. 페이지를 새로고침하거나 비밀번호를 초기화하세요.',
invalidUser: '사용자 이름이나 비밀번호가 틀렸습니다.',
error: '알 수 없는 오류',
changePassword: '비밀번호 변경',
@@ -28,7 +27,8 @@ const ko = {
cancel: '취소',
loginButtonText: '로그인',
tips: {
reset1: '비밀번호를 재설정하려면 NanoKVM의 BOOT 버튼을 10초 동안 누르고 계세요.',
reset1:
'비밀번호를 재설정하려면 NanoKVM의 BOOT 버튼을 10초 동안 누르고 계세요.',
reset2: '자세한 절차는 이 문서를 참조하세요:',
reset3: '웹 기본 계정:',
reset4: 'SSH 기본 계정:',
@@ -89,9 +89,11 @@ const ko = {
title: '이미지',
loading: '불러오는 중...',
empty: '아무것도 없습니다.',
cdrom: 'CD-ROM 모드로 이미지 마운트',
mountFailed: '이미지 마운트 실패',
mountDesc:
'일부 시스템에서는 이미지를 마운트하기 전에 원격 호스트에서 가상 디스크를 제거해야 합니다.',
refresh: '이미지 목록 새로고침',
tips: {
title: '업로드 방법',
usb1: 'USB를 통해 NanoKVM을 컴퓨터에 연결하세요.',
@@ -99,7 +101,7 @@ const ko = {
usb3: '컴퓨터에서 가상 디스크를 열고 이미지 파일을 가상 디스크의 루트 디렉토리로 복사하세요.',
scp1: 'NanoKVM과 컴퓨터가 동일한 로컬 네트워크에 있는지 확인하세요.',
scp2: '컴퓨터에서 터미널을 열고 SCP 명령을 사용하여 이미지 파일을 NanoKVM의 /data 디렉터리에 업로드하세요.',
scp3: '예: scp [이미지 파일 경로] root@[NanoKVM IP 주소]:/data',
scp3: '예: scp [이미지 파일 경로] root@[NanoKVM IP 주소]:/data',
tfCard: 'TF 카드',
tf1: '이 방법은 Linux 시스템에서 지원됩니다',
tf2: 'NanoKVM에서 TF 카드를 가져옵니다(전체 버전의 경우 먼저 케이스를 분해하세요).',
@@ -116,7 +118,7 @@ const ko = {
runFailed: '실행 실패',
attention: '주의',
delDesc: '이 파일을 정말로 삭제합니까?',
confirm: '',
confirm: '',
cancel: '아니오',
delete: '삭제',
close: '닫기'
@@ -222,7 +224,8 @@ const ko = {
upTailscale: 'tailscale을 NanoKVM 의 다음 경로에 업로드 했습니다. : /usr/bin/',
upTailscaled: 'tailscaled을 NanoKVM 의 다음 경로에 업로드 했습니다. : /usr/sbin/',
refresh: '현재 페이지 새로고침',
notLogin: '이 기기는 현재 연동 되지 않았습니다. 로그인해서 계정에 이 장치를 연동하세요.',
notLogin:
'이 기기는 현재 연동 되지 않았습니다. 로그인해서 계정에 이 장치를 연동하세요.',
urlPeriod: '이 주소는 10분간 유효합니다.',
login: '로그인',
loginSuccess: '로그인 성공',
@@ -233,7 +236,7 @@ const ko = {
logout: '로그아웃',
logout2: '정말로 로그아웃 합니까?',
uninstall: 'Tailscale 제거',
okBtn: '',
okBtn: '',
cancelBtn: '아니오'
},
update: {
@@ -253,6 +256,10 @@ const ko = {
updateBtn: '업데이트',
logoutBtn: '로그아웃'
}
},
error: {
title: '문제가 발생했습니다.',
refresh: '새로고침'
}
}
};

View File

@@ -16,9 +16,10 @@ import { client } from '@/lib/websocket.ts';
type ImagesProps = {
cdrom: boolean;
setIsMounted: (isMounted: boolean) => void;
onRefresh?: (callback: () => void) => void;
};
export const Images = ({ cdrom, setIsMounted }: ImagesProps) => {
export const Images = ({ cdrom, setIsMounted, onRefresh }: ImagesProps) => {
const { t } = useTranslation();
const [notify, contextHolder] = notification.useNotification();
@@ -31,6 +32,12 @@ export const Images = ({ cdrom, setIsMounted }: ImagesProps) => {
getImages();
}, []);
useEffect(() => {
if (onRefresh) {
onRefresh(getImages);
}
}, [onRefresh]);
function getImages() {
if (isLoading) return;
setIsLoading(true);

View File

@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { Divider, Popover, Switch, Tooltip } from 'antd';
import { useEffect, useRef, useState } from 'react';
import { Button, Divider, Popover, Switch, Tooltip } from 'antd';
import clsx from 'clsx';
import { DiscIcon } from 'lucide-react';
import { DiscIcon, RefreshCwIcon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useMediaQuery } from 'react-responsive';
@@ -18,9 +18,13 @@ export const Image = () => {
const [isMounted, setIsMounted] = useState(false);
const [cdrom, setCdrom] = useState(false);
const getImagesRef = useRef<() => void>(() => {});
const tooltip = t('image.title');
const [tooltipValue, setTooltipValue] = useState(tooltip);
useEffect(() => {
getMountedImage().then((rsp) => {
if (rsp.code !== 0) return;
@@ -33,6 +37,12 @@ export const Image = () => {
});
}, []);
const refreshImages = () => {
if (getImagesRef.current) {
getImagesRef.current();
}
};
const content = (
<div className="min-w-[300px]">
<div className="flex items-center justify-between px-1">
@@ -41,18 +51,26 @@ export const Image = () => {
<Tips />
</div>
<Tooltip title={t('image.cdrom')} placement="bottom">
<div className="flex items-center space-x-2">
<span className="text-xs text-neutral-400">CD-ROM</span>
<div className="flex items-center space-x-2">
<Tooltip title={t('image.cdrom')} placement="bottom">
<div className="flex items-center space-x-2">
<span className="text-xs text-neutral-400">CD-ROM</span>
<Switch size="small" checked={cdrom} onChange={(checked) => setCdrom(checked)}></Switch>
</div>
</Tooltip>
<Switch size="small" checked={cdrom} onChange={(checked) => setCdrom(checked)}></Switch>
</div>
</Tooltip>
<Tooltip title={t('image.refresh')} placement="bottom">
<Button type="default" icon={<RefreshCwIcon size={16} />} onClick={refreshImages} />
</Tooltip>
</div>
</div>
<Divider style={{ margin: '10px 0 15px 0' }} />
{isPopoverOpen && <Images cdrom={cdrom} setIsMounted={setIsMounted} />}
{isPopoverOpen && (
<Images cdrom={cdrom} setIsMounted={setIsMounted} onRefresh={(callback) => (getImagesRef.current = callback)} />
)}
</div>
);