mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(admin): keep a parallel addon toggle when a photo provider toggle fails on the phone
The phone rolled a failed photo provider toggle back by restoring the whole addon list it saw at the click, which also undid any switch flipped while the request was in flight. Photo providers now go through the same toggle as every other addon, with a per row rollback, as on desktop.
This commit is contained in:
@@ -111,7 +111,8 @@ export default function MAdminAddonManager({ bagTrackingEnabled, onToggleBagTrac
|
||||
|
||||
const handleToggle = async (addon: Addon) => {
|
||||
const newEnabled = !addon.enabled
|
||||
// Optimistic update
|
||||
// Optimistic flip, per-row rollback on failure. Rolling the whole addons snapshot
|
||||
// back instead would undo a toggle the user hit in parallel.
|
||||
setAddons(prev => prev.map(a => a.id === addon.id ? { ...a, enabled: newEnabled } : a))
|
||||
try {
|
||||
await adminApi.updateAddon(addon.id, { enabled: newEnabled })
|
||||
@@ -139,22 +140,6 @@ export default function MAdminAddonManager({ bagTrackingEnabled, onToggleBagTrac
|
||||
return addon.type === 'trip' && (addon.icon === 'Image' || haystack.includes('photo') || haystack.includes('memories'))
|
||||
}
|
||||
|
||||
const handleTogglePhotoProvider = async (providerAddon: Addon) => {
|
||||
const enableProvider = !providerAddon.enabled
|
||||
const prev = addons
|
||||
|
||||
setAddons(current => current.map(a => a.id === providerAddon.id ? { ...a, enabled: enableProvider } : a))
|
||||
|
||||
try {
|
||||
await adminApi.updateAddon(providerAddon.id, { enabled: enableProvider })
|
||||
refreshGlobalAddons()
|
||||
toast.success(t('admin.addons.toast.updated'))
|
||||
} catch {
|
||||
setAddons(prev)
|
||||
toast.error(t('admin.addons.toast.error'))
|
||||
}
|
||||
}
|
||||
|
||||
const photoProviderAddons = addons.filter(isPhotoProviderAddon)
|
||||
const documentProviderAddons = addons.filter(a => a.type === 'document_provider')
|
||||
const tripAddons = addons.filter(a => a.type === 'trip' && !isPhotosAddon(a))
|
||||
@@ -165,7 +150,7 @@ export default function MAdminAddonManager({ bagTrackingEnabled, onToggleBagTrac
|
||||
label: provider.name,
|
||||
description: provider.description,
|
||||
enabled: provider.enabled,
|
||||
toggle: () => handleTogglePhotoProvider(provider),
|
||||
toggle: () => handleToggle(provider),
|
||||
}))
|
||||
// No credential form under these, unlike the photo providers: a document
|
||||
// connection belongs to a trip and is entered there. The admin only decides
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// FE-MOB-AADD-001 to FE-MOB-AADD-033
|
||||
// FE-MOB-AADD-001 to FE-MOB-AADD-034
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { delay, http, HttpResponse } from 'msw';
|
||||
@@ -316,6 +316,33 @@ describe('MAdminAddonManager', () => {
|
||||
await waitFor(() => expect(screen.getByRole('switch', { name: 'Unsplash' })).toHaveAttribute('aria-checked', 'false'));
|
||||
});
|
||||
|
||||
it('FE-MOB-AADD-034: a failing provider toggle keeps a provider toggled while it was in flight', async () => {
|
||||
const user = userEvent.setup();
|
||||
server.use(
|
||||
addonsRoute([
|
||||
buildAddon({ id: 'journey', name: 'Journey', type: 'global', icon: 'Compass', enabled: true }),
|
||||
buildAddon({ id: 'immich', name: 'Immich', description: 'Self-hosted photos', type: 'photo_provider', enabled: false }),
|
||||
buildAddon({ id: 'unsplash', name: 'Unsplash', description: 'Stock photos', type: 'photo_provider', enabled: false }),
|
||||
]),
|
||||
http.put('/api/admin/addons/immich', () => HttpResponse.json({ success: true })),
|
||||
http.put('/api/admin/addons/unsplash', async () => {
|
||||
await delay(150);
|
||||
return HttpResponse.error();
|
||||
}),
|
||||
);
|
||||
render(<><ToastContainer /><MAdminAddonManager /></>);
|
||||
await screen.findByText('Immich');
|
||||
|
||||
// Unsplash is still saving when Immich goes on and through.
|
||||
await user.click(screen.getByRole('switch', { name: 'Unsplash' }));
|
||||
await user.click(screen.getByRole('switch', { name: 'Immich' }));
|
||||
await screen.findByText('Addon updated');
|
||||
|
||||
await screen.findByText('Failed to update addon');
|
||||
await waitFor(() => expect(screen.getByRole('switch', { name: 'Unsplash' })).toHaveAttribute('aria-checked', 'false'));
|
||||
expect(screen.getByRole('switch', { name: 'Immich' })).toHaveAttribute('aria-checked', 'true');
|
||||
});
|
||||
|
||||
it('FE-MOB-AADD-026: switching the journey addon off and on again shows the cascaded providers as off', async () => {
|
||||
const user = userEvent.setup();
|
||||
let loads = 0;
|
||||
|
||||
Reference in New Issue
Block a user