improve: Immich cloud filter and optimize

This commit is contained in:
2026-10-11 20:18:04 +07:00
parent f37a093a23
commit 0665796e37
15 changed files with 4699 additions and 87 deletions
+22 -10
View File
@@ -13,6 +13,7 @@ import { ExportModal } from './ui/ExportModal';
import { CameraView } from './ui/CameraView';
import { RecipeCreatePanel, type RecipeDraft } from './ui/RecipeCreatePanel';
import { useI18n } from './i18n/I18nProvider';
import { useIsMobile } from './ui/useIsMobile';
import { loadEngine, MARK_FACES } from './engine/assets';
import { renderPhoto, type RenderAssets } from './engine/exportEngine';
import { download, fileToBytes, readCapturedAt, readGps, readIso, readSpecs, resizedJpeg, scaledJpeg, type ShotSpecs } from './engine/imageOps';
@@ -347,6 +348,11 @@ interface Look {
export function Workspace() {
const { t } = useI18n();
const [forceView] = useState<'auto' | 'mobile' | 'desktop'>(() => {
return (localStorage.getItem('rc_force_view') as 'auto' | 'mobile' | 'desktop') || 'auto';
});
const detectedMobile = useIsMobile(860);
const isMobile = forceView === 'mobile' || (forceView === 'auto' && detectedMobile);
// Session restore: read once, synchronously, so the first paint already holds
// the user's own settings instead of a flash of the defaults. The photo comes
@@ -3728,7 +3734,7 @@ export function Workspace() {
</div>
) : null}
<div className={`workspace${stripOpen ? ' strip-open' : ''}`}>
<div className={`workspace${isMobile ? ' is-mobile' : ''}${stripOpen ? ' strip-open' : ''}`}>
{/* The tab opens its own strip in the bottom bar's place; the bar holds
one strip at a time, so a pick both switches the tab and hands the
bar over to it (see .strip-open in app.css). */}
@@ -3747,7 +3753,7 @@ export function Workspace() {
<div className="chips">
{/* column 1 — the tab's own chips, RESET ruled off at the foot */}
<div
className={`col col-main${tab === 'create' || tab === 'save' ? ' col-create' : ''}${
className={`col col-main${tab === 'create' ? ' col-create' : ''}${
tab === 'light' ? ' col-dev' : ''
}`}
data-col="main"
@@ -3773,39 +3779,43 @@ export function Workspace() {
light, FX and frame — as a recipe of this account's own. */}
<button
type="button"
className="btn primary"
className="chip btn-save-action"
data-key="save-recent"
onClick={() => void saveRecipe()}
>
{t('save.recent')}
+ {t('save.recent')}
</button>
<p className="hint">{t('save.recentHint')}</p>
<p className="hint save-hint">{t('save.recentHint')}</p>
{saved.length === 0 ? (
<p className="hint">{t('save.recentEmpty')}</p>
<p className="hint save-hint">{t('save.recentEmpty')}</p>
) : (
<div className="saved-list">
{saved.map((entry) => (
<div className="saved-row" key={entry.id}>
<button
type="button"
className="chip"
className={`chip link-chip${recipe.name === entry.name ? ' on' : ''}`}
data-key={`saved-${entry.id}`}
aria-pressed={recipe.name === entry.name}
onClick={() => applyRecipe({ ...(entry.recipe as Recipe), name: entry.name })}
>
<span className="chip-glyph" aria-hidden="true">◉</span>
<span className="chip-label">{entry.name}</span>
</button>
{/* Only this account can delete its own file — the API
scopes the delete by user, the button mirrors it. */}
<button
type="button"
className="chip"
className="saved-del-btn"
data-key={`saved-del-${entry.id}`}
title={t('save.delete')}
aria-label={t('save.delete')}
onClick={() => void deleteRecipeEntry(entry)}
onClick={(e) => {
e.stopPropagation();
void deleteRecipeEntry(entry);
}}
>
<span className="chip-label" aria-hidden="true">✕</span>
<span aria-hidden="true">✕</span>
</button>
</div>
))}
@@ -4037,6 +4047,8 @@ export function Workspace() {
},
})),
]}
list
glyph="◉"
/>
) : (
<p className="hint">{t('myp.historyEmpty')}</p>