Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ <h2 class="align-self-center">
[currentAddonType]="addonTypeString()"
[supportedResourceTypes]="supportedResourceTypes()"
(operationInvoke)="handleCreateOperationInvocation($event.operationName, $event.itemId)"
(operationInvokeWithCursor)="
handleCreateOperationInvocationWithCursor($event.operationName, $event.itemId, $event.pageCursor)
"
[(selectedStorageItemId)]="selectedStorageItemId"
[(selectedStorageItemUrl)]="selectedStorageItemUrl"
[(selectedResourceType)]="selectedResourceType"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,24 @@ export class ConfigureAddonComponent implements OnInit {
this.actions.createAddonOperationInvocation(payload);
}

handleCreateOperationInvocationWithCursor(
operationName: OperationNames,
folderId: string,
pageCursor?: string
): void {
const addon = this.addon();
if (!addon) return;

const payload = this.operationInvocationService.createOperationInvocationPayload(
addon,
operationName,
folderId,
pageCursor
);

this.actions.createAddonOperationInvocation(payload);
}

ngOnInit(): void {
this.handleCreateOperationInvocation(OperationNames.GET_ITEM_INFO, this.selectedStorageItemId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ <h2 class="pt-2">{{ 'settings.addons.connectAddon.configure' | translate }} {{ a
[(selectedStorageItemUrl)]="selectedStorageItemUrl"
[(selectedResourceType)]="selectedResourceType"
(operationInvoke)="handleCreateOperationInvocation($event.operationName, $event.itemId)"
(operationInvokeWithCursor)="
handleCreateOperationInvocationWithCursor($event.operationName, $event.itemId, $event.pageCursor)
"
(save)="handleCreateConfiguredAddon()"
(cancelSelection)="handleNavigateToAccountSelection()"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,23 @@ export class ConnectConfiguredAddonComponent {
this.actions.createAddonOperationInvocation(payload);
}

handleCreateOperationInvocationWithCursor(operationName: OperationNames, itemId: string, pageCursor?: string): void {
const selectedAccount = this.currentAuthorizedAddonAccounts().find(
(account) => account.id === this.chosenAccountId()
);

if (!selectedAccount) return;

const payload = this.operationInvocationService.createInitialOperationInvocationPayload(
operationName,
selectedAccount,
itemId,
pageCursor
);

this.actions.createAddonOperationInvocation(payload);
}

handleNavigateToAccountSelection(): void {
this.resetConfigurationForm();
this.stepper()?.value.set(ProjectAddonsStepperValue.CHOOSE_ACCOUNT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ <h3>{{ 'settings.addons.configureAddon.selectFolder' | translate }}</h3>
<p>{{ 'settings.addons.configureAddon.noFolders' | translate }}</p>
</div>
}
@if (showLoadMoreButton() && !isOperationInvocationSubmitting()) {
<div class="flex justify-content-center my-3">
<p-button
link
[label]="'common.buttons.loadMore' | translate"
severity="secondary"
(onClick)="handleLoadMore()"
></p-button>
</div>
}
</div>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class StorageItemSelectorComponent implements OnInit {
selectedStorageItemId = model<string>('/');
selectedStorageItemUrl = model<string>('');
operationInvoke = output<OperationInvokeData>();
operationInvokeWithCursor = output<OperationInvokeData>();
save = output<void>();
cancelSelection = output<void>();
readonly OperationNames = OperationNames;
Expand Down Expand Up @@ -109,6 +110,7 @@ export class StorageItemSelectorComponent implements OnInit {
initiallySelectedStorageItem = select(AddonsSelectors.getSelectedStorageItem);
isOperationInvocationSubmitting = select(AddonsSelectors.getOperationInvocationSubmitting);
isSubmitting = select(AddonsSelectors.getCreatedOrUpdatedConfiguredAddonSubmitting);
operationInvocation = select(AddonsSelectors.getOperationInvocation);
readonly homeBreadcrumb: MenuItem = {
id: '/',
label: this.translateService.instant('settings.addons.configureAddon.home'),
Expand Down Expand Up @@ -180,6 +182,14 @@ export class StorageItemSelectorComponent implements OnInit {
return this.hasInputChanged() || this.hasFolderChanged() || this.hasResourceTypeChanged();
});

readonly showLoadMoreButton = computed(() => {
const invocation = this.operationInvocation();
if (!invocation?.nextSampleCursor || !invocation?.thisSampleCursor) {
return false;
}
return invocation.nextSampleCursor > invocation.thisSampleCursor;
});

handleCreateOperationInvocation(
operationName: OperationNames,
itemId: string,
Expand All @@ -196,6 +206,19 @@ export class StorageItemSelectorComponent implements OnInit {
this.trimBreadcrumbs(itemId);
}

handleLoadMore(): void {
const invocation = this.operationInvocation();
if (!invocation?.nextSampleCursor) {
return;
}

this.operationInvokeWithCursor.emit({
operationName: invocation.operationName as OperationNames,
itemId: invocation.operationKwargs.itemId || '/',
pageCursor: invocation.nextSampleCursor,
});
}

handleSave(): void {
this.selectedStorageItemId.set(this.selectedStorageItem()?.itemId || '');
this.selectedStorageItemUrl.set(this.selectedStorageItem()?.itemLink || '');
Expand Down
10 changes: 10 additions & 0 deletions src/app/shared/mappers/addon.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ export class AddonMapper {
mayContainRootCandidates: operationResult.may_contain_root_candidates ?? isLinkAddon,
},
];

const cursors = isOperationResult
? {
...(operationResult.this_sample_cursor && { thisSampleCursor: operationResult.this_sample_cursor }),
...(operationResult.first_sample_cursor && { firstSampleCursor: operationResult.first_sample_cursor }),
...(operationResult.next_sample_cursor && { nextSampleCursor: operationResult.next_sample_cursor }),
}
: {};

return {
type: response.type,
id: response.id,
Expand All @@ -131,6 +140,7 @@ export class AddonMapper {
},
itemCount: isOperationResult ? operationResult.total_count : 0,
operationResult: mappedOperationResult,
...cursors,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export interface StorageItemResponseJsonApi {
export interface OperationResultJsonApi {
items: StorageItemResponseJsonApi[];
total_count: number;
this_sample_cursor?: string;
first_sample_cursor?: string;
next_sample_cursor?: string;
}

export interface OperationInvocationRequestJsonApi {
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/models/addons/addon-utils.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ export interface OAuthCallbacks {
export interface OperationInvokeData {
operationName: OperationNames;
itemId: string;
pageCursor?: string;
}
3 changes: 3 additions & 0 deletions src/app/shared/models/addons/operation-invocation.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ export interface OperationInvocation {
};
operationResult: StorageItem[];
itemCount: number;
thisSampleCursor?: string;
firstSampleCursor?: string;
nextSampleCursor?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ export class AddonOperationInvocationService {
createInitialOperationInvocationPayload(
operationName: OperationNames,
selectedAccount: AuthorizedAccountModel,
itemId?: string
itemId?: string,
pageCursor?: string
): OperationInvocationRequestJsonApi {
const addonSpecificOperationName = this.getAddonSpecificOperationName(operationName, selectedAccount);
const operationKwargs = this.getOperationKwargs(addonSpecificOperationName, itemId);
const operationKwargs = this.getOperationKwargs(addonSpecificOperationName, itemId, pageCursor);

return {
data: {
Expand Down Expand Up @@ -58,10 +59,11 @@ export class AddonOperationInvocationService {
createOperationInvocationPayload(
addon: ConfiguredAddonModel,
operationName: OperationNames,
itemId: string
itemId: string,
pageCursor?: string
): OperationInvocationRequestJsonApi {
const addonSpecificOperationName = this.getAddonSpecificOperationName(operationName, addon);
const operationKwargs = this.getOperationKwargs(addonSpecificOperationName, itemId);
const operationKwargs = this.getOperationKwargs(addonSpecificOperationName, itemId, pageCursor);

return {
data: {
Expand All @@ -86,20 +88,31 @@ export class AddonOperationInvocationService {
};
}

private getOperationKwargs(operationName: OperationNames, itemId?: string): Record<string, unknown> {
private getOperationKwargs(
operationName: OperationNames,
itemId?: string,
pageCursor?: string
): Record<string, unknown> {
const isRootOperation =
operationName === OperationNames.LIST_ROOT_ITEMS || operationName === OperationNames.LIST_ROOT_COLLECTIONS;

if (!itemId || isRootOperation) {
return {};
const baseKwargs: Record<string, unknown> = {};

if (itemId && !isRootOperation) {
baseKwargs['item_id'] = itemId;
}

const isChildOperation =
operationName === OperationNames.LIST_CHILD_ITEMS || operationName === OperationNames.LIST_COLLECTION_ITEMS;

return {
item_id: itemId,
...(isChildOperation && { item_type: 'FOLDER' }),
};
if (isChildOperation) {
baseKwargs['item_type'] = 'FOLDER';
}

if (pageCursor) {
baseKwargs['page_cursor'] = pageCursor;
}

return baseKwargs;
}
}
13 changes: 12 additions & 1 deletion src/app/shared/stores/addons/addons.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,9 +559,20 @@ export class AddonsState {

return this.addonsService.createAddonOperationInvocation(action.payload).pipe(
tap((response) => {
const isLoadMore = !!action.payload.data.attributes.operation_kwargs['page_cursor'];
const existingData = state.operationInvocation.data;
const shouldMerge = isLoadMore && existingData;

const mergedResponse = shouldMerge
? {
...response,
operationResult: [...existingData.operationResult, ...response.operationResult],
}
: response;

ctx.patchState({
operationInvocation: {
data: response,
data: mergedResponse,
isLoading: false,
isSubmitting: false,
error: null,
Expand Down
3 changes: 2 additions & 1 deletion src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
"selectAll": "Select All",
"removeAll": "Remove All",
"accept": "Accept",
"reject": "Reject"
"reject": "Reject",
"loadMore": "Load more"
},
"accessibility": {
"help": "Help",
Expand Down
Loading