feat(server): read-write external assets (#9235)

* refactor: remove isReadOnly and isExternal usages

* chore: open api

* fix: linting

* remove mobile isReadOnly dependency

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Jason Rasmussen
2024-05-03 15:34:57 -04:00
committed by GitHub
parent d26ac431b8
commit 5b87abb021
57 changed files with 181 additions and 603 deletions

View File

@@ -193,9 +193,7 @@
{/if}
{#if isOwner}
{#if !asset.isReadOnly || !asset.isExternal}
<CircleIconButton color="opaque" icon={mdiDeleteOutline} on:click={() => dispatch('delete')} title="Delete" />
{/if}
<CircleIconButton color="opaque" icon={mdiDeleteOutline} on:click={() => dispatch('delete')} title="Delete" />
<div
use:clickOutside={{
onOutclick: () => (isShowAssetOptions = false),

View File

@@ -308,23 +308,15 @@
{/if}
<div class="px-4 py-4">
{#if !asset.exifInfo && !asset.isExternal}
<p class="text-sm">NO EXIF INFO AVAILABLE</p>
{:else if !asset.exifInfo && asset.isExternal}
<div class="flex gap-4 py-4">
<div>
<p class="break-all">
Metadata not loaded for {asset.originalPath}
</p>
</div>
</div>
{:else}
{#if asset.exifInfo}
<div class="flex h-10 w-full items-center justify-between text-sm">
<h2>DETAILS</h2>
</div>
{:else}
<p class="text-sm">NO EXIF INFO AVAILABLE</p>
{/if}
{#if asset.exifInfo?.dateTimeOriginal && !asset.isReadOnly}
{#if asset.exifInfo?.dateTimeOriginal}
{@const assetDateTimeOriginal = DateTime.fromISO(asset.exifInfo.dateTimeOriginal, {
zone: asset.exifInfo.timeZone ?? undefined,
})}
@@ -374,7 +366,7 @@
</div>
{/if}
</button>
{:else if !asset.exifInfo?.dateTimeOriginal && !asset.isReadOnly && isOwner}
{:else if !asset.exifInfo?.dateTimeOriginal && isOwner}
<div class="flex justify-between place-items-start gap-4 py-4">
<div class="flex gap-4">
<div>
@@ -385,43 +377,6 @@
<Icon path={mdiPencil} size="20" />
</div>
</div>
{:else if asset.exifInfo?.dateTimeOriginal && asset.isReadOnly}
{@const assetDateTimeOriginal = DateTime.fromISO(asset.exifInfo.dateTimeOriginal, {
zone: asset.exifInfo.timeZone ?? undefined,
})}
<div class="flex justify-between place-items-start gap-4 py-4">
<div class="flex gap-4">
<div>
<Icon path={mdiCalendar} size="24" />
</div>
<div>
<p>
{assetDateTimeOriginal.toLocaleString(
{
month: 'short',
day: 'numeric',
year: 'numeric',
},
{ locale: $locale },
)}
</p>
<div class="flex gap-2 text-sm">
<p>
{assetDateTimeOriginal.toLocaleString(
{
weekday: 'short',
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'longOffset',
},
{ locale: $locale },
)}
</p>
</div>
</div>
</div>
</div>
{/if}
{#if isShowChangeDate}
@@ -501,7 +456,7 @@
</div>
{/if}
{#if asset.exifInfo?.city && !asset.isReadOnly}
{#if asset.exifInfo?.city}
<button
type="button"
class="flex w-full text-left justify-between place-items-start gap-4 py-4"
@@ -534,7 +489,7 @@
</div>
{/if}
</button>
{:else if !asset.exifInfo?.city && !asset.isReadOnly && isOwner}
{:else if !asset.exifInfo?.city && isOwner}
<button
type="button"
class="flex w-full text-left justify-between place-items-start gap-4 py-4 rounded-lg hover:dark:text-immich-dark-primary hover:text-immich-primary"
@@ -552,26 +507,6 @@
<Icon path={mdiPencil} size="20" />
</div>
</button>
{:else if asset.exifInfo?.city && asset.isReadOnly}
<div class="flex justify-between place-items-start gap-4 py-4">
<div class="flex gap-4">
<div><Icon path={mdiMapMarkerOutline} size="24" /></div>
<div>
<p>{asset.exifInfo.city}</p>
{#if asset.exifInfo?.state}
<div class="flex gap-2 text-sm">
<p>{asset.exifInfo.state}</p>
</div>
{/if}
{#if asset.exifInfo?.country}
<div class="flex gap-2 text-sm">
<p>{asset.exifInfo.country}</p>
</div>
{/if}
</div>
</div>
</div>
{/if}
{#if isShowChangeLocation}
<ChangeLocation

View File

@@ -34,7 +34,7 @@
const handleDelete = async () => {
loading = true;
const ids = [...getOwnedAssets()].filter((a) => !a.isExternal).map((a) => a.id);
const ids = [...getOwnedAssets()].map((a) => a.id);
await deleteAssets(force, onAssetDelete, ids);
clearSelect();
isShowConfirmation = false;

View File

@@ -47,7 +47,7 @@
$: timelineY = element?.scrollTop || 0;
$: isEmpty = $assetStore.initialized && $assetStore.buckets.length === 0;
$: idsSelectedAssets = [...$selectedAssets].filter((a) => !a.isExternal).map((a) => a.id);
$: idsSelectedAssets = [...$selectedAssets].map(({ id }) => id);
$: {
void assetStore.updateViewport(viewport);

View File

@@ -258,9 +258,9 @@ export const getAssetType = (type: AssetTypeEnum) => {
};
export const getSelectedAssets = (assets: Set<AssetResponseDto>, user: UserResponseDto | null): string[] => {
const ids = [...assets].filter((a) => !a.isExternal && user && a.ownerId === user.id).map((a) => a.id);
const ids = [...assets].filter((a) => user && a.ownerId === user.id).map((a) => a.id);
const numberOfIssues = [...assets].filter((a) => a.isExternal || (user && a.ownerId !== user.id)).length;
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
if (numberOfIssues > 0) {
notificationController.show({
message: `Can't change metadata of ${numberOfIssues} asset${numberOfIssues > 1 ? 's' : ''}`,

View File

@@ -22,9 +22,7 @@ export const assetFactory = Sync.makeFactory<AssetResponseDto>({
isTrashed: Sync.each(() => faker.datatype.boolean()),
duration: '0:00:00.00000',
checksum: Sync.each(() => faker.string.alphanumeric(28)),
isExternal: Sync.each(() => faker.datatype.boolean()),
isOffline: Sync.each(() => faker.datatype.boolean()),
isReadOnly: Sync.each(() => faker.datatype.boolean()),
hasMetadata: Sync.each(() => faker.datatype.boolean()),
stackCount: null,
});