-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
feat(web): View all duplicates page #15856
base: main
Are you sure you want to change the base?
feat(web): View all duplicates page #15856
Conversation
Can you add some screenshots to the PR description? |
There you go :) @bo0tzz |
What happens if you have 10_000 or 20_000 duplicates? Will it crash the page? |
Seems to run just fine, at least for me. I adjusted the load function of export const load = (async ({ params }) => {
await authenticate();
const asset = await getAssetInfoFromParam(params);
const duplicates = await getAssetDuplicates();
const $t = await getFormatter();
const mockedDuplicates = [...duplicates];
for (let i = 0; i < 10_000; i++) {
mockedDuplicates.push(...duplicates);
}
return {
asset,
duplicates: mockedDuplicates,
meta: {
title: $t('duplicates'),
},
};
}) satisfies PageLoad; |
How about 20,000? usually, with these types of rendering, the load can be stressed on both the server load and the web browser crashing. I am very positive we will run into issues since the query isn't lazy paginated as well as the view doesn't have lazy loading mechanism I chose to go with single duplicate because of the aforementioned limitation |
Alright, so we'd need to implement a client side pagination to merge this? I assume only client side, since the current single duplicate implementation also already calls the entire duplicate list from the server, so this shouldn't be a problem |
Let's take a few steps and understand the problem we are trying to solve. Below are some of my feed back, and thoughts
Heck, perhaps we can replace the current view if the above points are implemented. What do you think? |
Make sure to consider #13851 before you get too far. |
web/src/lib/components/utilities-page/duplicates/duplicate-thumbnail.svelte
Outdated
Show resolved
Hide resolved
href="{AppRoute.DUPLICATES}/{asset.duplicateId}" | ||
class="hover:dark:text-immich-dark-primary hover:text-immich-primary" | ||
> | ||
{$t('has_duplicates')} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel this doesn't give a good indication that it's a button that takes you to a different view.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this feature to see the duplicate info on the image details. I agree with mert about it not being clear that it is clickable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried to improve this in the latest iteration :D
|
||
export const load = (async ({ params }) => { | ||
await authenticate(); | ||
const duplicates = await getAssetDuplicates(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This view only needs to know the number of each duplicate group, not fetch all their members. It can fetch much less data with a different endpoint that only returns the data it actually needs (one asset to display per group, and the number of duplicates in the group).
@@ -103,7 +116,17 @@ | |||
await stackAssets(assets, false); | |||
const duplicateAssetIds = assets.map((asset) => asset.id); | |||
await updateAssets({ assetBulkUpdateDto: { ids: duplicateAssetIds, duplicateId: null } }); | |||
const currentDuplicateIndex = duplicates.findIndex((duplicate) => duplicate.duplicateId === duplicateId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you just track the index instead of doing this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't think I really understand by "tracking" in this context.
Where would you calculate the index? In the API endpoint?
const duplicates = await getAssetDuplicates(); | ||
const $t = await getFormatter(); | ||
|
||
const activeDuplicate = duplicates.find((duplicate) => duplicate.duplicateId === params.duplicateId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here as well.
Alright, thanks for all the feedback! I'll give it a try in the coming days/weeks My idea for combining the 2 views would be something like this: Is that something you had in mind? |
I think it is worth to draft a wireframe design so we don't misunderstand how the page will look like |
I'm not a huge fan of the explicit numbered pages. Maybe there's a more subtle way to paginate this, or to use virtual scroll like the timeline? |
I would create a I don't think that should be too hard to implement, without the need for any additional libraries. |
I happened to see this while searching for search improvements so just leaving a note to consider (or disregard), that all the pages (timeline, people, albums, search etc) in the app have a consistent theme/pattern. A vertically scrolling/paginated on scroll approach but now with the above screenshot we seem to be introducing a brand new way/pattern of horizontal scrolling on click that not only goes against the existing pattern but makes scrolling through 1000s of duplicates a tedious click through. (as opposed to the usual vertically scrolling/paginated on scroll approach in the app). Apologies, if not relevant please disregard. |
I agree a vertical scroll oriented design would be more intuitive, easier to use and more consistent with other pages. That being said, I'm not a web dev and not sure how difficult it is to make it a virtual scroll. It's at least easy to do it the way the search result page works (load more results on scroll without removing earlier entries from the DOM). It would start having issues once you scroll enough to have >5000 groups loaded, but that might be fine tbh. |
Just a bit ago I ran into the work done for the timeline and I almost fainted. (The timeline has inconsistent sizing that has to be estimated and fixed on the scrollbar and stuff, so that's why it's so complicated.) |
Well tbh, this was more of a ui problem than a technical one. I'm sure we can make virtual scroll work horizontally or vertically. I just didn't have a good idea on how to make a vertically scrolling list/grid while also having the dedupe mechanism on the same page. If someone's got ideas or suggestions, I'm open :D |
Is that truly necessary? What about clicking on an image pops up the current page (to the specific image you click)? And if you wanna be fancy, it could be a big (almost full-screen) popup. |
Well, that is the current implementation, basically. List of duplicate groups, you click on it and land on the known duplicate page. But Alex suggested to merge those into one: #15856 (comment) |
Oh yeah, I just tried it. It's pretty good but yeah, some stuff could be addressed. Also, you should try rebasing when you merge main and then force push. |
So, a little update on what I've done now:
In wanting to implement Mert's comments, and improve effiency in general, I also added 2 new endpoints for duplicates:
However I didn't end up actually using those:
|
Feature Description
Add a new intermediate page, that displays all the found duplicates alongside how many duplicate assets each duplicate has.
![image](https://private-user-images.githubusercontent.com/46051866/408881757-70443f98-fda0-4ff7-aaaa-83ea4d8c0f74.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk2Nzg3NDQsIm5iZiI6MTczOTY3ODQ0NCwicGF0aCI6Ii80NjA1MTg2Ni80MDg4ODE3NTctNzA0NDNmOTgtZmRhMC00ZmY3LWFhYWEtODNlYTRkOGMwZjc0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTYlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjE2VDA0MDA0NFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTA4MGQyNmFkYWFmNTQ2MWFjNDAyMDE4YjdhZTYyOGE1YzMyNzYwZDEzMWYzYTBiOWFlMWYxYWRhNzBkY2IyYmQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.xNQwd1FpBxwWUW3H9_jfMdhx9KSPbcwn9baUL1mU4JI)
From there, you can click on any duplicate to arrive at the "old" duplicate selection mechanism for that specific stack.
After resolving, it should move on to the next duplicate in the list
With this change, we can now also link from "Viewing an asset" straight to the duplication page for that duplicate stack.
Implementation
duplicateId
[[photos=photos]]
param, I assume it's not relevant anymore with these changes, but I wanted confirmation before removing itutilities/duplicates
duplicateId