Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web Photo Editor #9575

Closed
wants to merge 111 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
4743320
image editor
faupau03 Jul 12, 2023
2a2de39
fix import
faupau03 Jul 14, 2023
d5ed321
editor ui initial commit - not functional
faupau03 Jul 14, 2023
4f03882
add simple close functionality
faupau03 Jul 14, 2023
4fd9c3d
add simple crop, flip and rotate functionality
faupau03 Jul 15, 2023
136300c
fix cropping
faupau03 Jul 16, 2023
4a1207f
add angle selection
faupau03 Jul 16, 2023
fa7fa3a
Merge branch 'main' into feat-web-photo-editor
faupau03 Jul 24, 2023
b34ac2e
fix package.json package-lock.json
faupau03 Jul 24, 2023
bc96db1
fix angle selection
faupau03 Jul 24, 2023
1dda536
some changes not functional
faupau03 Jul 24, 2023
949cf13
fix rotate
faupau03 Jul 25, 2023
d56d26f
Merge branch 'main' into feat-web-photo-editor
faupau03 Aug 13, 2023
d3a78e4
add shadow to overflow, add handles
faupau03 Aug 13, 2023
f0cec94
fix aspect ratio selection
faupau03 Aug 20, 2023
c5ca118
dont allow selection of angle slider
faupau03 Aug 20, 2023
a5b48ff
fix correct original aspect ratio
faupau03 Aug 20, 2023
e782874
remove unnecesarry dep
faupau03 Aug 20, 2023
46f3142
fix img stretching
faupau03 Aug 20, 2023
ceb8f02
fix rotate also cropzone
faupau03 Aug 20, 2023
569b46a
rotate opt param
faupau03 Aug 20, 2023
7eb5028
add touch support for angle slider
faupau03 Aug 26, 2023
9f50b8b
add asset movement in x and y direction
faupau03 Aug 28, 2023
6fdd82b
fix aspect ratio selection
faupau03 Aug 28, 2023
0c8c652
fix aspect ratio
faupau03 Aug 30, 2023
0611537
simple render for crop and rotate, fix angle slide
faupau03 Aug 31, 2023
3d55019
fix rotation by 90 deg
faupau03 Aug 31, 2023
1385a22
fix types
faupau03 Aug 31, 2023
81d881a
fix assetdrag direction
faupau03 Aug 31, 2023
f6fce2d
add adjustment tab functions, fix loading
faupau03 Sep 5, 2023
7153f22
highlight active adjustments and add reset func
faupau03 Sep 5, 2023
0085a44
add css filter to render, with polyfill for safari
faupau03 Sep 6, 2023
37a1813
fix filter
faupau03 Sep 14, 2023
45b660f
use original image
faupau03 Sep 15, 2023
d0a88b9
load thumb data
faupau03 Sep 15, 2023
5db1da4
use thumbnail for preview
faupau03 Sep 15, 2023
5418bac
load in different function
faupau03 Sep 15, 2023
d891b3f
add preview to preset cards
faupau03 Sep 18, 2023
af17d2b
fix rendering (wrong ratio)
faupau03 Sep 18, 2023
13ec9c8
add save animation
faupau03 Sep 18, 2023
f46809f
fix type
faupau03 Sep 18, 2023
09fe26e
Merge branch 'main' into feat-web-photo-editor
faupau03 Oct 5, 2023
8549800
add simple zoom
faupau03 Oct 5, 2023
1732139
Merge remote-tracking branch 'refs/remotes/origin/feat-web-photo-edit…
Oct 5, 2023
5daebc0
fix package-lock.json
faupau03 Oct 6, 2023
0391b51
fix package-lock.json
faupau03 Oct 6, 2023
74f05fc
add zoom feature
faupau03 Oct 7, 2023
5aa85bb
dev config
faupau03 Oct 7, 2023
510a44b
Delete server/immich.config
faupau03 Oct 7, 2023
14e383a
Merge remote-tracking branch 'upstream/main' into feat-web-photo-editor
faupau03 Nov 28, 2023
70e78df
Fix import statements and update filter icons
faupau03 Nov 29, 2023
eacae2e
Add icon component to filter card and fix merge
faupau03 Nov 29, 2023
815fd67
remove unused vars
faupau03 Nov 29, 2023
d81c790
fix test
faupau03 Nov 29, 2023
6ebc49d
fix lint
faupau03 Nov 29, 2023
2db8b2f
fix prettier
faupau03 Nov 29, 2023
7dbde95
fix prettier
faupau03 Nov 29, 2023
376a64a
Merge branch 'immich-app:main' into feat-web-photo-editor
faupau03 Nov 29, 2023
6b8db38
Merge branch 'immich-app:main' into feat-web-photo-editor
faupau03 Nov 30, 2023
f5c7231
Merge branch 'immich-app:main' into feat-web-photo-editor
faupau03 Dec 1, 2023
6a100eb
fix adjust slider
faupau03 Dec 1, 2023
671a305
add zoom to render
faupau03 Dec 1, 2023
975040a
add loading animation, remove console logs
faupau03 Dec 5, 2023
278e697
Fix aspect ratio bug in photo editor
faupau03 Dec 7, 2023
a75ce86
copy exif to edited file
faupau03 Dec 16, 2023
50173c7
Merge branch 'immich-app:main' into feat-web-photo-editor
faupau03 Dec 16, 2023
c36a35f
fix: check for image types before, only copy exif when supported
faupau03 Dec 17, 2023
fe421c8
pinch test
faupau03 Dec 17, 2023
7ece293
add touch zoom, touch angle selection, fix flip not rendered
faupau03 Dec 17, 2023
563aac2
clean up
faupau03 Dec 17, 2023
a7e53e7
fix test
faupau03 Dec 18, 2023
407954b
add touch controls
faupau03 Dec 19, 2023
1b7874b
Merge branch 'immich-app:main' into feat-web-photo-editor
faupau03 Dec 19, 2023
6c52372
fix tests
faupau03 Dec 23, 2023
e7549b1
Merge branch 'immich-app:main' into feat-web-photo-editor
faupau03 Dec 23, 2023
4bf649a
fix typing
faupau03 Dec 23, 2023
351ce49
fix type
faupau03 Dec 23, 2023
59d0642
fix format
faupau03 Dec 23, 2023
bcaf170
add basic editing history
faupau03 Dec 23, 2023
c26fb4b
add new editor and render class
faupau03 Dec 26, 2023
6a32848
Merge branch 'immich-app:main' into feat-web-photo-editor
faupau03 Dec 31, 2023
6a3c2d9
clear old code, fix typing
faupau03 Jan 1, 2024
f309016
Merge branch 'feat-web-photo-editor' of https://github.com/faupau03/i…
faupau03 Jan 1, 2024
13066c5
fix ts, formatting, loading spinner
faupau03 Jan 1, 2024
6cf7e6a
remove old dependencies
faupau03 Jan 2, 2024
ca12427
fix merge conflicts
faupau03 Jan 2, 2024
c1efd8a
fix merge issue
faupau03 Jan 2, 2024
2ad6583
Merge branch 'main' of https://github.com/faupau03/immich into feat-w…
faupau03 Jan 2, 2024
bd53d0b
add upload/linking functionality
faupau03 Jan 2, 2024
98087a0
fix formatting
faupau03 Jan 2, 2024
7520bcb
package-lock.json
faupau03 Jan 2, 2024
1809226
fix active preset
faupau03 Jan 2, 2024
54d559a
Merge branch 'main' into feat-web-photo-editor
faupau03 Jan 9, 2024
3592661
fix paning, rotating
faupau03 Jan 16, 2024
6b581e2
Merge branch 'immich-app:main' into feat-web-photo-editor
faupau03 Jan 31, 2024
5b5bbbb
Merge branch 'immich-app:main' into feat-web-photo-editor
faupau03 Jan 31, 2024
cac19b6
Merge remote-tracking branch 'origin/main' into feat-web-photo-editor
faupau03 Feb 28, 2024
46df7c1
fix git remains
faupau03 Mar 26, 2024
bea6c4e
Merge branch 'main' into feat-web-photo-editor
faupau03 Mar 26, 2024
f14e2a6
fix linting
faupau03 Mar 26, 2024
f412251
fix lint
faupau03 Mar 26, 2024
2221e5f
fix formattinh
faupau03 Mar 26, 2024
d672a74
fix linting - number litaral casing
faupau03 Apr 1, 2024
3350048
Merge branch 'main' into feat-web-photo-editor
faupau03 Apr 1, 2024
1c49b7e
fix merge
faupau03 Apr 1, 2024
8f33e80
fix format
faupau03 Apr 8, 2024
44c023f
Merge branch 'main' into feat-web-photo-editor
faupau03 Apr 8, 2024
b832033
Merge remote-tracking branch 'origin' into feat-web-photo-editor
May 14, 2024
c32be1f
feat: add editor history module
May 17, 2024
07e0f5d
refactor: update adjustment slider to use idiomatic svelte code
May 18, 2024
7f36af1
refactor: remove auto-fix UI for now
May 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
4,713 changes: 1,616 additions & 3,097 deletions web/package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@types/justified-layout": "^4.1.4",
"@types/lodash-es": "^4.17.12",
"@types/luxon": "^3.4.2",
"@types/node": "^20.11.30",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@vitest/coverage-v8": "^1.3.1",
Expand All @@ -44,6 +45,7 @@
"eslint-plugin-svelte": "^2.35.1",
"eslint-plugin-unicorn": "^52.0.0",
"factory.ts": "^1.4.1",
"jsdom": "^24.0.0",
"postcss": "^8.4.35",
"prettier": "^3.2.5",
"prettier-plugin-organize-imports": "^3.2.4",
Expand All @@ -66,13 +68,15 @@
"@photo-sphere-viewer/video-plugin": "^5.7.2",
"@zoom-image/svelte": "^0.2.6",
"buffer": "^6.0.3",
"context-filter-polyfill": "^0.3.6",
"copy-image-clipboard": "^2.1.2",
"dom-to-image": "^2.6.0",
"handlebars": "^4.7.8",
"justified-layout": "^4.1.0",
"lodash-es": "^4.17.21",
"luxon": "^3.4.4",
"socket.io-client": "^4.7.4",
"svelte-hammer": "^0.1.7",
"svelte-local-storage-store": "^0.6.4",
"svelte-maplibre": "^0.9.0",
"thumbhash": "^0.1.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
mdiMagnifyPlusOutline,
mdiMotionPauseOutline,
mdiPlaySpeed,
mdiTune,
mdiPresentationPlay,
mdiShareVariantOutline,
} from '@mdi/js';
Expand All @@ -44,6 +45,7 @@
export let showMotionPlayButton: boolean;
export let isMotionPhotoPlaying = false;
export let showDownloadButton: boolean;
export let showEditButton = true;
export let showDetailButton: boolean;
export let showShareButton: boolean;
export let showSlideshow = false;
Expand Down Expand Up @@ -79,6 +81,7 @@
runJob: AssetJobName;
playSlideShow: void;
unstack: void;
edit: void;
showShareModal: void;
}>();

Expand Down Expand Up @@ -108,6 +111,9 @@
<CircleIconButton color="opaque" icon={mdiArrowLeft} title="Go back" on:click={() => dispatch('back')} />
</div>
<div class="flex w-[calc(100%-3rem)] justify-end gap-2 overflow-hidden text-white">
{#if showEditButton}
<CircleIconButton isOpacity={true} icon={mdiTune} title="Edit" on:click={() => dispatch('edit')} />
{/if}
{#if showShareButton}
<CircleIconButton
color="opaque"
Expand Down
17 changes: 13 additions & 4 deletions web/src/lib/components/asset-viewer/asset-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,21 @@
type AlbumResponseDto,
type AssetResponseDto,
} from '@immich/sdk';
import { mdiChevronLeft, mdiChevronRight, mdiImageBrokenVariant } from '@mdi/js';
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
import { fly } from 'svelte/transition';
import Thumbnail from '../assets/thumbnail/thumbnail.svelte';
import DeleteAssetDialog from '../photos-page/delete-asset-dialog.svelte';
import AlbumSelectionModal from '../shared-components/album-selection-modal.svelte';
import { NotificationType, notificationController } from '../shared-components/notification/notification';
import ProfileImageCropper from '../shared-components/profile-image-cropper.svelte';
import ActivityStatus from './activity-status.svelte';
import ActivityViewer from './activity-viewer.svelte';
import AssetViewerNavBar from './asset-viewer-nav-bar.svelte';
import DetailPanel from './detail-panel.svelte';
import NavigationArea from './navigation-area.svelte';

import PhotoEditor from './photo-editor/photo-editor.svelte';
import { mdiChevronLeft, mdiChevronRight, mdiImageBrokenVariant } from '@mdi/js';
import Thumbnail from '../assets/thumbnail/thumbnail.svelte';
import ActivityViewer from './activity-viewer.svelte';
import ActivityStatus from './activity-status.svelte';
import PanoramaViewer from './panorama-viewer.svelte';
import PhotoViewer from './photo-viewer.svelte';
import SlideshowBar from './slideshow-bar.svelte';
Expand Down Expand Up @@ -190,6 +192,7 @@
handlePromiseError(getNumberOfComments());
}
}
let shouldShowPhotoEditor = false;

onMount(async () => {
await navigate({ targetRoute: 'current', assetId: asset.id });
Expand Down Expand Up @@ -448,6 +451,7 @@
}
};

$: console.log(shouldShowPhotoEditor);
const handleRunJob = async (name: AssetJobName) => {
try {
await runAssetJobs({ assetJobsDto: { assetIds: [asset.id], name } });
Expand Down Expand Up @@ -594,6 +598,7 @@
on:runJob={({ detail: job }) => handleRunJob(job)}
on:playSlideShow={() => ($slideshowState = SlideshowState.PlaySlideshow)}
on:unstack={handleUnstack}
on:edit={() => (shouldShowPhotoEditor = true)}
on:showShareModal={() => (isShowShareModal = true)}
/>
</div>
Expand Down Expand Up @@ -802,6 +807,10 @@
{#if isShowShareModal}
<CreateSharedLinkModal assetIds={[asset.id]} onClose={() => (isShowShareModal = false)} />
{/if}

{#if shouldShowPhotoEditor}
<PhotoEditor {asset} on:close={() => (shouldShowPhotoEditor = false)} />
{/if}
</section>
</FocusTrap>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { createHistory } from '$lib/components/asset-viewer/photo-editor/history';

describe('Editor history', () => {
it('reports undo is unavailable when history is empty', () => {
const h = createHistory();

expect(h.canUndo()).toBe(false);
});

it('reports redo is unavailable when history is empty', () => {
const h = createHistory();

expect(h.canRedo()).toBe(false);
});

it('cannot undo after only one change', () => {
const h = createHistory();

h.push({type: 'foo', properties: {}});
expect(h.canUndo()).toBe(false);
});

it('can undo after more than one change', () => {
const h = createHistory();

h.push({type: 'foo', properties: {}});
h.push({type: 'foo', properties: {}});
expect(h.canUndo()).toBe(true);
});

it('cannot redo if undo has not been called', () => {
const h = createHistory();

h.push({type: 'foo', properties: {}});
expect(h.canRedo()).toBe(false);
});

it('can redo after undo', () => {
const h = createHistory();

h.push({type: 'foo', properties: {}});
h.push({type: 'foo', properties: {}});
h.undo();
expect(h.canRedo()).toBe(true);
});

it('returns the current state based on resolving the change history', () => {
const h = createHistory();

h.push({type: 'foo', properties: {a: 1, b: 2}});
h.push({type: 'foo', properties: {a: 2}});
expect(h.currentState()).toEqual({
foo: {a: 2}
});
});

it('returns the current state based on resolving the change history after undo', () => {
const h = createHistory();

h.push({type: 'foo', properties: {a: 1, b: 2}});
h.push({type: 'foo', properties: {a: 2}});
expect(h.undo()).toEqual({
foo: {a: 1, b: 2}
});
});

it('cannot redo after pushing a new change after undo', () => {
const h = createHistory();

h.push({type: 'foo', properties: {a: 1, b: 2}});
h.push({type: 'foo', properties: {a: 2}});
h.undo();
h.push({type: 'foo', properties: {a: 3}});
expect(h.canRedo()).toBe(false);
});

it('removes all undone changes when a new change is pushed', () => {
const h = createHistory();

h.push({type: 'foo', properties: {a: 1, b: 2}});
h.push({type: 'foo', properties: {a: 2}});
h.undo();
h.push({type: 'bar', properties: {a: 3}});
expect(h.currentState()).toEqual({
foo: {a: 1, b: 2},
bar: {a: 3}
});
});
});
Loading