mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 14:21:46 +00:00
fix journey bugs reported by roel-de-vries (#722-#736)
Mobile UI: - #722 timeline carousel no longer cut off by BottomNav (uses --bottom-nav-h var) - #723 scroll-snap-type relaxed to proximity so small swipes no longer skip entries - #724 defensive padding-bottom fix in JourneySettingsDialog for iOS PWA - #725 add back/settings buttons + journey title subtitle to mobile activity view - #726 active entry re-centers after scroll settle; tap inactive card activates it (does not jump straight into editor) Entry editor flow: - #727 photo uploads queue locally until Save for existing entries too (previously fired upload immediately; Cancel silently kept the new photo) - #728 Cancel/Close with unsaved changes now requires confirm (window.confirm) - #729 linking a Gallery photo into an entry now copies the row (old MOVE behavior meant Remove-from-Entry also nuked the Gallery original) - #731 addPhoto / addProviderPhoto / linkPhotoToEntry promote skeleton entries to concrete 'entry' type when content is added Permissions: - #732 updateJourney switched from canEdit to isOwner — editors can still edit entries and photos, just not the journey shell (title, cover, status) - #733 Contributors list gains a per-row remove (X) control with confirm - #734 my_role is computed server-side and returned with the journey; UI gates Settings/Add/Edit/Delete controls based on role - #736 createOrUpdateJourneyShareLink + deleteJourneyShareLink now require isOwner (previously NO permission check at all — anyone authenticated could publish or unpublish a journey) Immich upload (#730): - migration 111: add users.immich_auto_upload (default 0) - migration 112: seed provider_field for the toggle (idempotent, FK-safe) - journey photo upload only mirrors to Immich when the user has opted in - Settings UI gets a "Mirror journey photos to Immich on upload" checkbox Test updates: - JOURNEY-SVC-019 inverted to assert editor cannot update journey settings - JOURNEY-SHARE-007 now passes userId (owner) to deleteJourneyShareLink - FE-PAGE-JOURNEYDETAIL-148 inverted to assert photos stay pending until Save - client/tests still green (2676/2676) Also fixed en route: gallery entry title is now the literal 'Gallery' on the wire (used to send the translated label, which broke server-side title === 'Gallery' checks in non-English locales); confirm interpolation uses {username} single braces matching the existing i18n runtime; Settings footer uses icon-only delete/archive buttons on mobile so the row doesn't wrap.
This commit is contained in:
@@ -6,6 +6,7 @@ import crypto from 'node:crypto';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { AuthRequest } from '../types';
|
||||
import * as svc from '../services/journeyService';
|
||||
import { db } from '../db/database';
|
||||
import { createOrUpdateJourneyShareLink, getJourneyShareLink, deleteJourneyShareLink, getPublicJourney } from '../services/journeyShareService';
|
||||
import { uploadToImmich } from '../services/memories/immichService';
|
||||
|
||||
@@ -95,16 +96,21 @@ router.post('/entries/:entryId/photos', authenticate, upload.array('photos', 10)
|
||||
req.body?.caption
|
||||
);
|
||||
if (photo) {
|
||||
// sync to Immich if connected — update the same photo record
|
||||
try {
|
||||
const immichId = await uploadToImmich(authReq.user.id, relativePath, file.originalname);
|
||||
if (immichId) {
|
||||
svc.setPhotoProvider(photo.id, 'immich', immichId, authReq.user.id);
|
||||
photo.provider = 'immich' as any;
|
||||
photo.asset_id = immichId;
|
||||
photo.owner_id = authReq.user.id;
|
||||
}
|
||||
} catch {}
|
||||
// Mirror to Immich only when the user has explicitly opted in via the
|
||||
// Immich integration settings. Avoids the "surprise upload" in #730
|
||||
// where a write-capable API key implicitly enabled mirroring.
|
||||
const prefs = db.prepare('SELECT immich_auto_upload FROM users WHERE id = ?').get(authReq.user.id) as { immich_auto_upload?: number } | undefined;
|
||||
if (prefs?.immich_auto_upload) {
|
||||
try {
|
||||
const immichId = await uploadToImmich(authReq.user.id, relativePath, file.originalname);
|
||||
if (immichId) {
|
||||
svc.setPhotoProvider(photo.id, 'immich', immichId, authReq.user.id);
|
||||
photo.provider = 'immich' as any;
|
||||
photo.asset_id = immichId;
|
||||
photo.owner_id = authReq.user.id;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
results.push(photo);
|
||||
}
|
||||
}
|
||||
@@ -301,11 +307,15 @@ router.post('/:id/share-link', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { share_timeline, share_gallery, share_map } = req.body || {};
|
||||
const result = createOrUpdateJourneyShareLink(Number(req.params.id), authReq.user.id, { share_timeline, share_gallery, share_map });
|
||||
if (!result) return res.status(403).json({ error: 'Not allowed' });
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
router.delete('/:id/share-link', authenticate, (req: Request, res: Response) => {
|
||||
deleteJourneyShareLink(Number(req.params.id));
|
||||
const authReq = req as AuthRequest;
|
||||
if (!deleteJourneyShareLink(Number(req.params.id), authReq.user.id)) {
|
||||
return res.status(403).json({ error: 'Not allowed' });
|
||||
}
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { getClientIp } from '../../services/auditLog';
|
||||
import {
|
||||
getConnectionSettings,
|
||||
saveImmichSettings,
|
||||
setImmichAutoUpload,
|
||||
testConnection,
|
||||
getConnectionStatus,
|
||||
browseTimeline,
|
||||
@@ -31,9 +32,12 @@ router.get('/settings', authenticate, (req: Request, res: Response) => {
|
||||
|
||||
router.put('/settings', authenticate, async (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { immich_url, immich_api_key } = req.body;
|
||||
const { immich_url, immich_api_key, auto_upload } = req.body;
|
||||
const result = await saveImmichSettings(authReq.user.id, immich_url, immich_api_key, getClientIp(req));
|
||||
if (!result.success) return res.status(400).json({ error: result.error });
|
||||
if (typeof auto_upload === 'boolean') {
|
||||
setImmichAutoUpload(authReq.user.id, auto_upload);
|
||||
}
|
||||
if (result.warning) return res.json({ success: true, warning: result.warning });
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user