feat(web): show partners assets on the main timeline (#4933)

This commit is contained in:
Alex
2023-11-11 15:06:19 -06:00
committed by GitHub
parent 3b11854702
commit 35767591d2
59 changed files with 1929 additions and 172 deletions
+250 -14
View File
@@ -2361,6 +2361,103 @@ export interface OAuthConfigResponseDto {
*/
'url'?: string;
}
/**
*
* @export
* @interface PartnerResponseDto
*/
export interface PartnerResponseDto {
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'createdAt': string;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'deletedAt': string | null;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'email': string;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'externalPath': string | null;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'firstName': string;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'id': string;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'inTimeline'?: boolean;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'isAdmin': boolean;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'lastName': string;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'oauthId': string;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'profileImagePath': string;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'shouldChangePassword': boolean;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'storageLabel': string | null;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'updatedAt': string;
}
/**
*
* @export
@@ -4220,6 +4317,19 @@ export interface UpdateLibraryDto {
*/
'name'?: string;
}
/**
*
* @export
* @interface UpdatePartnerDto
*/
export interface UpdatePartnerDto {
/**
*
* @type {boolean}
* @memberof UpdatePartnerDto
*/
'inTimeline': boolean;
}
/**
*
* @export
@@ -7274,11 +7384,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {boolean} [withStacked]
* @param {boolean} [withPartners]
* @param {string} [key]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
getTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, withPartners?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'size' is not null or undefined
assertParamExists('getTimeBucket', 'size', size)
// verify required parameter 'timeBucket' is not null or undefined
@@ -7336,6 +7447,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
localVarQueryParameter['withStacked'] = withStacked;
}
if (withPartners !== undefined) {
localVarQueryParameter['withPartners'] = withPartners;
}
if (timeBucket !== undefined) {
localVarQueryParameter['timeBucket'] = timeBucket;
}
@@ -7365,11 +7480,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {boolean} [withStacked]
* @param {boolean} [withPartners]
* @param {string} [key]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, withPartners?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'size' is not null or undefined
assertParamExists('getTimeBuckets', 'size', size)
const localVarPath = `/asset/time-buckets`;
@@ -7425,6 +7541,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
localVarQueryParameter['withStacked'] = withStacked;
}
if (withPartners !== undefined) {
localVarQueryParameter['withPartners'] = withPartners;
}
if (key !== undefined) {
localVarQueryParameter['key'] = key;
}
@@ -8227,12 +8347,13 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {boolean} [withStacked]
* @param {boolean} [withPartners]
* @param {string} [key]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, key, options);
async getTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, withPartners?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, withPartners, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
@@ -8245,12 +8366,13 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {boolean} [withStacked]
* @param {boolean} [withPartners]
* @param {string} [key]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, key, options);
async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, withPartners?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, withPartners, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
@@ -8547,7 +8669,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError}
*/
getTimeBucket(requestParameters: AssetApiGetTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> {
return localVarFp.getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.key, options).then((request) => request(axios, basePath));
return localVarFp.getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.withPartners, requestParameters.key, options).then((request) => request(axios, basePath));
},
/**
*
@@ -8556,7 +8678,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError}
*/
getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<TimeBucketResponseDto>> {
return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.key, options).then((request) => request(axios, basePath));
return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.withPartners, requestParameters.key, options).then((request) => request(axios, basePath));
},
/**
* Get all asset of a device that are in the database, ID only.
@@ -9043,6 +9165,13 @@ export interface AssetApiGetTimeBucketRequest {
*/
readonly withStacked?: boolean
/**
*
* @type {boolean}
* @memberof AssetApiGetTimeBucket
*/
readonly withPartners?: boolean
/**
*
* @type {string}
@@ -9113,6 +9242,13 @@ export interface AssetApiGetTimeBucketsRequest {
*/
readonly withStacked?: boolean
/**
*
* @type {boolean}
* @memberof AssetApiGetTimeBuckets
*/
readonly withPartners?: boolean
/**
*
* @type {string}
@@ -9592,7 +9728,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi
*/
public getTimeBucket(requestParameters: AssetApiGetTimeBucketRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
return AssetApiFp(this.configuration).getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.withPartners, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
}
/**
@@ -9603,7 +9739,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi
*/
public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.withPartners, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
}
/**
@@ -12312,6 +12448,54 @@ export const PartnerApiAxiosParamCreator = function (configuration?: Configurati
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {string} id
* @param {UpdatePartnerDto} updatePartnerDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
updatePartner: async (id: string, updatePartnerDto: UpdatePartnerDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('updatePartner', 'id', id)
// verify required parameter 'updatePartnerDto' is not null or undefined
assertParamExists('updatePartner', 'updatePartnerDto', updatePartnerDto)
const localVarPath = `/partner/{id}`
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication cookie required
// authentication api_key required
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
// authentication bearer required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
localVarHeaderParameter['Content-Type'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
localVarRequestOptions.data = serializeDataIfNeeded(updatePartnerDto, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
@@ -12333,7 +12517,7 @@ export const PartnerApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async createPartner(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<UserResponseDto>> {
async createPartner(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PartnerResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.createPartner(id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@@ -12343,7 +12527,7 @@ export const PartnerApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getPartners(direction: 'shared-by' | 'shared-with', options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<UserResponseDto>>> {
async getPartners(direction: 'shared-by' | 'shared-with', options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<PartnerResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getPartners(direction, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@@ -12357,6 +12541,17 @@ export const PartnerApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.removePartner(id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @param {string} id
* @param {UpdatePartnerDto} updatePartnerDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async updatePartner(id: string, updatePartnerDto: UpdatePartnerDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PartnerResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.updatePartner(id, updatePartnerDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
}
};
@@ -12373,7 +12568,7 @@ export const PartnerApiFactory = function (configuration?: Configuration, basePa
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
createPartner(requestParameters: PartnerApiCreatePartnerRequest, options?: AxiosRequestConfig): AxiosPromise<UserResponseDto> {
createPartner(requestParameters: PartnerApiCreatePartnerRequest, options?: AxiosRequestConfig): AxiosPromise<PartnerResponseDto> {
return localVarFp.createPartner(requestParameters.id, options).then((request) => request(axios, basePath));
},
/**
@@ -12382,7 +12577,7 @@ export const PartnerApiFactory = function (configuration?: Configuration, basePa
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getPartners(requestParameters: PartnerApiGetPartnersRequest, options?: AxiosRequestConfig): AxiosPromise<Array<UserResponseDto>> {
getPartners(requestParameters: PartnerApiGetPartnersRequest, options?: AxiosRequestConfig): AxiosPromise<Array<PartnerResponseDto>> {
return localVarFp.getPartners(requestParameters.direction, options).then((request) => request(axios, basePath));
},
/**
@@ -12394,6 +12589,15 @@ export const PartnerApiFactory = function (configuration?: Configuration, basePa
removePartner(requestParameters: PartnerApiRemovePartnerRequest, options?: AxiosRequestConfig): AxiosPromise<void> {
return localVarFp.removePartner(requestParameters.id, options).then((request) => request(axios, basePath));
},
/**
*
* @param {PartnerApiUpdatePartnerRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
updatePartner(requestParameters: PartnerApiUpdatePartnerRequest, options?: AxiosRequestConfig): AxiosPromise<PartnerResponseDto> {
return localVarFp.updatePartner(requestParameters.id, requestParameters.updatePartnerDto, options).then((request) => request(axios, basePath));
},
};
};
@@ -12439,6 +12643,27 @@ export interface PartnerApiRemovePartnerRequest {
readonly id: string
}
/**
* Request parameters for updatePartner operation in PartnerApi.
* @export
* @interface PartnerApiUpdatePartnerRequest
*/
export interface PartnerApiUpdatePartnerRequest {
/**
*
* @type {string}
* @memberof PartnerApiUpdatePartner
*/
readonly id: string
/**
*
* @type {UpdatePartnerDto}
* @memberof PartnerApiUpdatePartner
*/
readonly updatePartnerDto: UpdatePartnerDto
}
/**
* PartnerApi - object-oriented interface
* @export
@@ -12478,6 +12703,17 @@ export class PartnerApi extends BaseAPI {
public removePartner(requestParameters: PartnerApiRemovePartnerRequest, options?: AxiosRequestConfig) {
return PartnerApiFp(this.configuration).removePartner(requestParameters.id, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @param {PartnerApiUpdatePartnerRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof PartnerApi
*/
public updatePartner(requestParameters: PartnerApiUpdatePartnerRequest, options?: AxiosRequestConfig) {
return PartnerApiFp(this.configuration).updatePartner(requestParameters.id, requestParameters.updatePartnerDto, options).then((request) => request(this.axios, this.basePath));
}
}
@@ -97,8 +97,12 @@
</script>
<header>
{#if $isMultiSelectState}
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
{#if $isMultiSelectState && user}
<AssetSelectControlBar
ownerId={user.id}
assets={$selectedAssets}
clearSelect={() => assetInteractionStore.clearMultiselect()}
>
<SelectAllAssets {assetStore} {assetInteractionStore} />
{#if sharedLink.allowDownload}
<DownloadAction filename="{album.albumName}.zip" />
@@ -20,14 +20,14 @@
let loading = false;
const { getAssets, clearSelect } = getAssetControlContext();
const { clearSelect, getOwnedAssets } = getAssetControlContext();
const handleArchive = async () => {
const isArchived = !unarchive;
loading = true;
try {
const assets = Array.from(getAssets()).filter((asset) => asset.isArchived !== isArchived);
const assets = Array.from(getOwnedAssets()).filter((asset) => asset.isArchived !== isArchived);
const ids = assets.map(({ id }) => id);
if (ids.length > 0) {
@@ -14,13 +14,13 @@
AssetJobName.TranscodeVideo,
];
const { getAssets, clearSelect } = getAssetControlContext();
const { clearSelect, getOwnedAssets } = getAssetControlContext();
$: isAllVideos = Array.from(getAssets()).every((asset) => asset.type === AssetTypeEnum.Video);
$: isAllVideos = Array.from(getOwnedAssets()).every((asset) => asset.type === AssetTypeEnum.Video);
const handleRunJob = async (name: AssetJobName) => {
try {
const ids = Array.from(getAssets()).map(({ id }) => id);
const ids = Array.from(getOwnedAssets()).map(({ id }) => id);
await api.assetApi.runAssetJobs({ assetJobsDto: { assetIds: ids, name } });
notificationController.show({ message: api.getAssetJobMessage(name), type: NotificationType.Info });
clearSelect();
@@ -17,7 +17,7 @@
export let menuItem = false;
export let force = !$featureFlags.trash;
const { getAssets, clearSelect } = getAssetControlContext();
const { clearSelect, getOwnedAssets } = getAssetControlContext();
const dispatch = createEventDispatcher();
@@ -37,7 +37,7 @@
loading = true;
try {
const ids = Array.from(getAssets())
const ids = Array.from(getOwnedAssets())
.filter((a) => !a.isExternal)
.map((a) => a.id);
await api.assetApi.deleteAssets({ assetBulkDeleteDto: { ids, force } });
@@ -75,7 +75,7 @@
{#if isShowConfirmation}
<ConfirmDialogue
title="Permanently Delete Asset{getAssets().size > 1 ? 's' : ''}"
title="Permanently Delete Asset{getOwnedAssets().size > 1 ? 's' : ''}"
confirmText="Delete"
on:confirm={handleDelete}
on:cancel={() => (isShowConfirmation = false)}
@@ -84,8 +84,8 @@
<svelte:fragment slot="prompt">
<p>
Are you sure you want to permanently delete
{#if getAssets().size > 1}
these <b>{getAssets().size}</b> assets? This will also remove them from their album(s).
{#if getOwnedAssets().size > 1}
these <b>{getOwnedAssets().size}</b> assets? This will also remove them from their album(s).
{:else}
this asset? This will also remove it from its album(s).
{/if}
@@ -20,14 +20,15 @@
let loading = false;
const { getAssets, clearSelect } = getAssetControlContext();
const { clearSelect, getOwnedAssets } = getAssetControlContext();
const handleFavorite = async () => {
const isFavorite = !removeFavorite;
loading = true;
try {
const assets = Array.from(getAssets()).filter((asset) => asset.isFavorite !== isFavorite);
const assets = Array.from(getOwnedAssets()).filter((asset) => asset.isFavorite !== isFavorite);
const ids = assets.map(({ id }) => id);
if (ids.length > 0) {
@@ -10,11 +10,11 @@
export let onStack: OnStack | undefined = undefined;
const { getAssets, clearSelect } = getAssetControlContext();
const { clearSelect, getOwnedAssets } = getAssetControlContext();
const handleStack = async () => {
try {
const assets = Array.from(getAssets());
const assets = Array.from(getOwnedAssets());
const parent = assets.at(0);
if (parent == undefined) {
@@ -9,7 +9,8 @@
export interface AssetControlContext {
// Wrap assets in a function, because context isn't reactive.
getAssets: () => Set<AssetResponseDto>;
getAssets: () => Set<AssetResponseDto>; // All assets includes partners' assets
getOwnedAssets: () => Set<AssetResponseDto>; // Only assets owned by the user
clearSelect: () => void;
}
@@ -25,8 +26,14 @@
export let assets: Set<AssetResponseDto>;
export let clearSelect: () => void;
export let ownerId: string | undefined = undefined;
setContext({ getAssets: () => assets, clearSelect });
setContext({
getAssets: () => assets,
getOwnedAssets: () =>
ownerId !== undefined ? new Set(Array.from(assets).filter((asset) => asset.ownerId === ownerId)) : assets,
clearSelect,
});
</script>
<ControlAppBar on:close-button-click={clearSelect} backIcon={mdiClose} tailwindClasses="bg-white shadow-md">
@@ -1,22 +1,71 @@
<script lang="ts">
import { UserResponseDto, api } from '@api';
import { PartnerResponseDto, UserResponseDto, api } from '@api';
import UserAvatar from '../shared-components/user-avatar.svelte';
import Button from '../elements/buttons/button.svelte';
import PartnerSelectionModal from './partner-selection-modal.svelte';
import { handleError } from '../../utils/handle-error';
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
import { mdiClose } from '@mdi/js';
import { mdiCheck, mdiClose } from '@mdi/js';
import { onMount } from 'svelte';
import Icon from '../elements/icon.svelte';
import SettingSwitch from '../admin-page/settings/setting-switch.svelte';
interface PartnerSharing {
user: UserResponseDto;
sharedByMe: boolean;
sharedWithMe: boolean;
inTimeline: boolean;
}
export let user: UserResponseDto;
export let partners: UserResponseDto[];
let createPartner = false;
let removePartner: UserResponseDto | null = null;
let removePartner: PartnerResponseDto | null = null;
let partners: Array<PartnerSharing> = [];
onMount(() => {
refreshPartners();
});
const refreshPartners = async () => {
const { data } = await api.partnerApi.getPartners({ direction: 'shared-by' });
partners = data;
partners = [];
const [{ data: sharedBy }, { data: sharedWith }] = await Promise.all([
api.partnerApi.getPartners({ direction: 'shared-by' }),
api.partnerApi.getPartners({ direction: 'shared-with' }),
]);
for (const candidate of sharedBy) {
partners = [
...partners,
{
user: candidate,
sharedByMe: true,
sharedWithMe: false,
inTimeline: candidate.inTimeline ?? false,
},
];
}
for (const candidate of sharedWith) {
const existIndex = partners.findIndex((p) => candidate.id === p.user.id);
if (existIndex >= 0) {
partners[existIndex].sharedWithMe = true;
partners[existIndex].inTimeline = candidate.inTimeline ?? false;
} else {
partners = [
...partners,
{
user: candidate,
sharedByMe: false,
sharedWithMe: true,
inTimeline: candidate.inTimeline ?? false,
},
];
}
}
};
const handleRemovePartner = async () => {
@@ -45,34 +94,80 @@
handleError(error, 'Unable to add partners');
}
};
const handleShowOnTimelineChanged = async (partner: PartnerSharing, inTimeline: boolean) => {
try {
await api.partnerApi.updatePartner({ id: partner.user.id, updatePartnerDto: { inTimeline } });
partner.inTimeline = inTimeline;
partners = partners;
} catch (error) {
handleError(error, 'Unable to update timeline display status');
}
};
</script>
<section class="my-4">
{#if partners.length > 0}
<div class="flex flex-row gap-4">
{#each partners as partner (partner.id)}
<div class="flex gap-4 rounded-lg px-5 py-4 transition-all">
<UserAvatar user={partner} size="md" autoColor />
<div class="text-left">
<p class="text-immich-fg dark:text-immich-dark-fg">
{partner.firstName}
{partner.lastName}
</p>
<p class="text-xs text-immich-fg/75 dark:text-immich-dark-fg/75">
{partner.email}
</p>
{#each partners as partner (partner.user.id)}
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 mt-6 bg-slate-50 dark:bg-gray-900 p-5">
<div class="flex gap-4 rounded-lg pb-4 transition-all justify-between">
<div class="flex gap-4">
<UserAvatar user={partner.user} size="md" autoColor />
<div class="text-left">
<p class="text-immich-fg dark:text-immich-dark-fg">
{partner.user.firstName}
{partner.user.lastName}
</p>
<p class="text-xs text-immich-fg/75 dark:text-immich-dark-fg/75">
{partner.user.email}
</p>
</div>
</div>
<CircleIconButton
on:click={() => (removePartner = partner)}
icon={mdiClose}
size={'16'}
title="Remove partner"
/>
{#if partner.sharedByMe}
<CircleIconButton
on:click={() => (removePartner = partner.user)}
icon={mdiClose}
size={'16'}
title="Stop sharing your photos with this user"
/>
{/if}
</div>
{/each}
</div>
<div class="dark:text-gray-200 text-immich-dark-gray">
<!-- I am sharing my assets with this user -->
{#if partner.sharedByMe}
<hr class="my-4 border border-gray-200 dark:border-gray-700" />
<p class="text-xs font-medium my-4">SHARED WITH {partner.user.firstName.toUpperCase()}</p>
<p class="text-md">{partner.user.firstName} can access</p>
<ul class="text-sm">
<li class="flex gap-2 place-items-center py-1 mt-2">
<Icon path={mdiCheck} /> All your photos and videos except those in Archived and Deleted
</li>
<li class="flex gap-2 place-items-center py-1">
<Icon path={mdiCheck} /> The location where your photos were taken
</li>
</ul>
{/if}
<!-- this user is sharing assets with me -->
{#if partner.sharedWithMe}
<hr class="my-4 border border-gray-200 dark:border-gray-700" />
<p class="text-xs font-medium my-4">PHOTOS FROM {partner.user.firstName.toUpperCase()}</p>
<SettingSwitch
title="Show in timeline"
subtitle="Show photos and videos from this user in your timeline"
bind:checked={partner.inTimeline}
on:toggle={({ detail }) => handleShowOnTimelineChanged(partner, detail)}
/>
{/if}
</div>
</div>
{/each}
{/if}
<div class="flex justify-end">
<div class="flex justify-end mt-5">
<Button size="sm" on:click={() => (createPartner = true)}>Add partner</Button>
</div>
</section>
@@ -18,7 +18,6 @@
export let keys: APIKeyResponseDto[] = [];
export let devices: AuthDeviceResponseDto[] = [];
export let partners: UserResponseDto[] = [];
let oauthOpen = false;
if (browser) {
@@ -61,7 +60,7 @@
</SettingAccordion>
<SettingAccordion title="Sharing" subtitle="Manage sharing with partners">
<PartnerSettings {user} bind:partners />
<PartnerSettings {user} />
</SettingAccordion>
<SettingAccordion title="Sidebar" subtitle="Manage sidebar settings">
@@ -16,7 +16,7 @@
export let data: PageData;
const assetStore = new AssetStore({ userId: data.partner.id, isArchived: false });
const assetStore = new AssetStore({ userId: data.partner.id, isArchived: false, withStacked: true });
const assetInteractionStore = createAssetInteractionStore();
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
+6 -2
View File
@@ -26,7 +26,7 @@
let { isViewing: showAssetViewer } = assetViewingStore;
let handleEscapeKey = false;
const assetStore = new AssetStore({ isArchived: false, withStacked: true });
const assetStore = new AssetStore({ isArchived: false, withStacked: true, withPartners: true });
const assetInteractionStore = createAssetInteractionStore();
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
@@ -48,7 +48,11 @@
</script>
{#if $isMultiSelectState}
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
<AssetSelectControlBar
ownerId={data.user.id}
assets={$selectedAssets}
clearSelect={() => assetInteractionStore.clearMultiselect()}
>
<CreateSharedLink on:escape={() => (handleEscapeKey = true)} />
<SelectAllAssets {assetStore} {assetInteractionStore} />
<AssetSelectContextMenu icon={mdiPlus} title="Add">
@@ -10,13 +10,11 @@ export const load = (async ({ parent, locals }) => {
const { data: keys } = await locals.api.keyApi.getApiKeys();
const { data: devices } = await locals.api.authenticationApi.getAuthDevices();
const { data: partners } = await locals.api.partnerApi.getPartners({ direction: 'shared-by' });
return {
user,
keys,
devices,
partners,
meta: {
title: 'Settings',
},
@@ -9,7 +9,7 @@
<UserPageLayout user={data.user} title={data.meta.title}>
<section class="mx-4 flex place-content-center">
<div class="w-full max-w-3xl">
<UserSettingsList user={data.user} keys={data.keys} devices={data.devices} partners={data.partners} />
<UserSettingsList user={data.user} keys={data.keys} devices={data.devices} />
</div>
</section>
</UserPageLayout>