Skip to content

Commit fdfade8

Browse files
committed
feat: add Nautilus availability check and corresponding UI banner
1 parent 3111eff commit fdfade8

13 files changed

Lines changed: 261 additions & 4 deletions

File tree

src/apps/main/interface.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export interface IElectronAPI {
157157
onBackupProgress(func: (value: number) => void): () => void;
158158
startRemoteSync(): Promise<void>;
159159
getUpdateStatus(): Promise<{ version: string } | null>;
160+
getNautilusAvailability(): Promise<boolean>;
160161
onUpdateAvailable(callback: (info: { version: string }) => void): () => void;
161162
getRemoteSyncStatus(): Promise<import('./remote-sync/helpers').RemoteSyncStatus>;
162163
onRemoteSyncStatusChange(callback: (status: import('./remote-sync/helpers').RemoteSyncStatus) => void): () => void;

src/apps/main/preload.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ declare interface Window {
218218
getDiskSpace: () => Promise<number>;
219219
};
220220
getUpdateStatus(): Promise<{ version: string } | null>;
221+
getNautilusAvailability(): Promise<boolean>;
221222
onUpdateAvailable(callback: (info: { version: string }) => void): () => void;
222223
};
223224
}

src/apps/main/preload.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ contextBridge.exposeInMainWorld('electron', {
409409
getUpdateStatus() {
410410
return ipcRenderer.invoke('get-update-status');
411411
},
412+
getNautilusAvailability() {
413+
return ipcRenderer.invoke('get-nautilus-availability');
414+
},
412415
onUpdateAvailable(callback) {
413416
const eventName = 'update-available';
414417
const callbackWrapper = (_, info) => callback(info);

src/apps/renderer/localize/locales/en.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@
108108
"message": "We are having issues mounting your Internxt Drive, try unmounting it manually and starting the app again"
109109
},
110110
"banners": {
111+
"nautilus-unavailable": {
112+
"body": "File explorer not supported. Some features may not work properly.",
113+
"action": "Learn more"
114+
},
111115
"update-available": {
112116
"body": "A new version is available.",
113117
"action": "Download update"
@@ -434,4 +438,4 @@
434438
"ctaUpgrade": "Upgrade plan",
435439
"plan": "{{planName}} -> up to {{planMaxFileSize}}"
436440
}
437-
}
441+
}

src/apps/renderer/localize/locales/es.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@
108108
"message": "Estamos teniendo problemas al montar tu unidad Internxt. Intenta desmontarla manualmente y luego reiniciar la aplicación."
109109
},
110110
"banners": {
111+
"nautilus-unavailable": {
112+
"body": "Explorador de archivos no compatible. Algunas funciones pueden no funcionar correctamente.",
113+
"action": "Más información"
114+
},
111115
"update-available": {
112116
"body": "Hay una nueva versión disponible.",
113117
"action": "Descargar actualización"
@@ -434,4 +438,4 @@
434438
"ctaUpgrade": "Mejorar plan",
435439
"plan": "{{planName}} -> hasta {{planMaxFileSize}}"
436440
}
437-
}
441+
}

src/apps/renderer/localize/locales/fr.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@
108108
"message": "Nous rencontrons des problèmes pour monter votre disque Internxt. Essayez de le démonter manuellement et de relancer l'application. "
109109
},
110110
"banners": {
111+
"nautilus-unavailable": {
112+
"body": "Explorateur de fichiers non pris en charge. Certaines fonctionnalités peuvent ne pas fonctionner correctement.",
113+
"action": "En savoir plus"
114+
},
111115
"update-available": {
112116
"body": "Une nouvelle version est disponible.",
113117
"action": "Télécharger la mise à jour"
@@ -434,4 +438,4 @@
434438
"ctaUpgrade": "Mettre à jour le plan",
435439
"plan": "{{planName}} -> jusqu'à {{planMaxFileSize}}"
436440
}
437-
}
441+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
2+
import { NautilusUnavailable } from './NautilusUnavailable';
3+
4+
describe('NautilusUnavailable', () => {
5+
beforeEach(() => {
6+
vi.mocked(window.electron.getNautilusAvailability).mockResolvedValue(true);
7+
});
8+
9+
it('renders nothing when Nautilus is available', async () => {
10+
const { container } = render(<NautilusUnavailable />);
11+
12+
await waitFor(() => {
13+
expect(window.electron.getNautilusAvailability).toHaveBeenCalledTimes(1);
14+
});
15+
16+
expect(container).toBeEmptyDOMElement();
17+
});
18+
19+
it('renders the warning banner when Nautilus is not available', async () => {
20+
vi.mocked(window.electron.getNautilusAvailability).mockResolvedValue(false);
21+
22+
render(<NautilusUnavailable />);
23+
24+
await waitFor(() => {
25+
expect(screen.getByText('widget.banners.nautilus-unavailable.body')).toBeInTheDocument();
26+
});
27+
});
28+
29+
it('dismisses the warning banner when the X button is clicked', async () => {
30+
vi.mocked(window.electron.getNautilusAvailability).mockResolvedValue(false);
31+
32+
render(<NautilusUnavailable />);
33+
34+
await waitFor(() => {
35+
expect(screen.getByText('widget.banners.nautilus-unavailable.body')).toBeInTheDocument();
36+
});
37+
38+
fireEvent.click(screen.getByLabelText('Dismiss'));
39+
40+
expect(screen.queryByText('widget.banners.nautilus-unavailable.body')).not.toBeInTheDocument();
41+
});
42+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Warning, X } from '@phosphor-icons/react';
2+
import { useEffect, useState } from 'react';
3+
import { useTranslationContext } from '../../../../context/LocalContext';
4+
5+
export function NautilusUnavailable() {
6+
const [isNautilusAvailable, setIsNautilusAvailable] = useState(true);
7+
const [dismissed, setDismissed] = useState(false);
8+
const { translate } = useTranslationContext();
9+
10+
useEffect(() => {
11+
window.electron
12+
.getNautilusAvailability()
13+
.then((isAvailable) => {
14+
setIsNautilusAvailable(isAvailable);
15+
})
16+
.catch(() => {
17+
setIsNautilusAvailable(true);
18+
});
19+
}, []);
20+
21+
if (isNautilusAvailable || dismissed) {
22+
return <></>;
23+
}
24+
25+
return (
26+
<div className="mx-1 mt-3 flex items-center gap-2.5 rounded-xl border-2 border-[#FFF4CC] bg-[#FFF9E5] px-4 py-2 dark:border-[#7F6B19] dark:bg-[#4C400F]">
27+
<Warning size={22} weight="fill" className="shrink-0 text-yellow-dark" />
28+
<div className="flex flex-1 flex-col">
29+
<p className="text-xs text-gray-80">{translate('widget.banners.nautilus-unavailable.body')}</p>
30+
</div>
31+
<button
32+
onClick={() => setDismissed(true)}
33+
className="ml-2 shrink-0 rounded-md p-1 text-gray-50 transition-colors hover:bg-black/5 hover:text-gray-80"
34+
aria-label="Dismiss">
35+
<X size={18} />
36+
</button>
37+
</div>
38+
);
39+
}

src/apps/renderer/pages/Widget/InfoBanners/InfoBanners.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { DiscoverBackups } from './Banners/DiscoverBackups';
2+
import { NautilusUnavailable } from './Banners/NautilusUnavailable';
23
import { UpdateAvailable } from './Banners/UpdateAvailable';
34

45
export function InfoBanners() {
56
return (
67
<>
8+
<NautilusUnavailable />
79
<UpdateAvailable />
810
<DiscoverBackups />
911
</>

src/backend/features/nautilus-extension/install.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { logger } from '@internxt/drive-desktop-core/build/backend';
2-
import { copyNautilusExtensionFile, deleteNautilusExtensionFile, isInstalled, reloadNautilus } from './service';
2+
import {
3+
copyNautilusExtensionFile,
4+
deleteNautilusExtensionFile,
5+
isInstalled,
6+
reloadNautilus,
7+
} from './service';
8+
import { isNautilusAvailable } from './is-nautilus-available';
39

410
import configStore from '../../../apps/main/config';
511
import { LATEST_NAUTILUS_EXTENSION_VERSION } from './version';
@@ -26,6 +32,10 @@ async function install(): Promise<void> {
2632

2733
export async function installNautilusExtension() {
2834
try {
35+
const canInstall = await isNautilusAvailable();
36+
37+
if (!canInstall) return;
38+
2939
const installed = await isInstalled();
3040
const hasLatestsVersion = isUpToDate();
3141

0 commit comments

Comments
 (0)