diff --git a/app/models/addon-operation-invocation.ts b/app/models/addon-operation-invocation.ts index 8e90627ef8..c6369d8f75 100644 --- a/app/models/addon-operation-invocation.ts +++ b/app/models/addon-operation-invocation.ts @@ -16,15 +16,22 @@ export enum ItemType { File = 'FILE', } -export interface OperationResult{ +export type OperationResult = ListItemsResult | Item; + +export interface ListItemsResult { items: Item[]; - cursor?: string; // TODO: name?? + thisSampleCursor?: string; + nextSampleCursor?: string; + prevSampleCursor?: string; + firstSampleCursor?: string; + totalCount?: number; } + export interface Item { itemId: string; itemName: string; itemType: ItemType; - itemPath: string; + itemPath?: Item[]; } export default class AddonOperationInvocationModel extends Model { diff --git a/lib/osf-components/addon/components/addons-service/configured-addon-edit/template.hbs b/lib/osf-components/addon/components/addons-service/configured-addon-edit/template.hbs index 754df62aeb..994b78c29f 100644 --- a/lib/osf-components/addon/components/addons-service/configured-addon-edit/template.hbs +++ b/lib/osf-components/addon/components/addons-service/configured-addon-edit/template.hbs @@ -27,23 +27,27 @@ as |fileManager| >
- {{#each fileManager.currentPath as |pathItem index|}} - - - + + {{#each fileManager.currentPath as |pathItem|}} + {{/each}}
@@ -54,43 +58,59 @@ - {{#each fileManager.currentItems as |folder|}} - - - - - {{else if fileManager.isLoading}} + {{#if fileManager.isLoading}} {{else if fileManager.isError}} {{else}} - - - - {{/each}} + {{#each fileManager.currentItems as |folder|}} + + + + + {{else}} + + + + {{/each}} + {{#if fileManager.hasMore}} + + + + {{/if}} + {{/if}}
- - - -
{{t 'addons.configure.error-loading-items'}}
{{t 'addons.configure.no-folders'}}
+ + + +
{{t 'addons.configure.no-folders'}}
+ +
diff --git a/lib/osf-components/addon/components/addons-service/file-manager/component.ts b/lib/osf-components/addon/components/addons-service/file-manager/component.ts index b00b283da4..28bafde02d 100644 --- a/lib/osf-components/addon/components/addons-service/file-manager/component.ts +++ b/lib/osf-components/addon/components/addons-service/file-manager/component.ts @@ -8,7 +8,7 @@ import { taskFor } from 'ember-concurrency-ts'; import IntlService from 'ember-intl/services/intl'; import Toast from 'ember-toastr/services/toast'; -import { Item } from 'ember-osf-web/models/addon-operation-invocation'; +import { Item, ListItemsResult } from 'ember-osf-web/models/addon-operation-invocation'; import ConfiguredStorageAddonModel, { OperationKwargs } from 'ember-osf-web/models/configured-storage-addon'; import captureException, { getApiErrorMessage } from 'ember-osf-web/utils/capture-exception'; @@ -24,9 +24,9 @@ export default class FileManager extends Component { @tracked currentPath: Item[] = []; @tracked currentItems: Item[] = []; - @tracked currentFolderId = this.args.startingFolderId; + @tracked currentFolderId?: string; - @tracked cursor = ''; + @tracked cursor?: string; @tracked hasMore = false; private lastInvocation: any = null; @@ -43,22 +43,27 @@ export default class FileManager extends Component { constructor(owner: unknown, args: Args) { super(owner, args); - taskFor(this.getStartingFolder).perform(); - taskFor(this.getItems).perform(); + taskFor(this.initialize).perform(); } + @task + @waitFor + async initialize() { + await taskFor(this.getStartingFolder).perform(); + await taskFor(this.getItems).perform(); + } @action goToRoot() { - this.cursor = ''; - this.currentPath = this.currentPath.slice(0, 1); - this.currentFolderId = this.args.startingFolderId; + this.cursor = undefined; + this.currentPath = this.currentPath = []; + this.currentFolderId = undefined; taskFor(this.getItems).perform(); } @action goToFolder(folder: Item) { - this.cursor = ''; + this.cursor = undefined; this.currentItems = []; if (this.currentPath.includes(folder)) { this.currentPath = this.currentPath.slice(0, this.currentPath.indexOf(folder) + 1); @@ -71,7 +76,7 @@ export default class FileManager extends Component { @action getMore() { - this.cursor = this.lastInvocation?.operationResult.cursor; + this.cursor = this.lastInvocation?.operationResult.nextSampleCursor; taskFor(this.getItems).perform(); } @@ -81,8 +86,12 @@ export default class FileManager extends Component { async getStartingFolder() { const { startingFolderId, configuredStorageAddon } = this.args; try { - const invocation = await taskFor(configuredStorageAddon.getItemInfo).perform(startingFolderId); - this.currentPath = [invocation.operationResult]; + if (startingFolderId) { + const invocation = await taskFor(configuredStorageAddon.getItemInfo).perform(startingFolderId); + const result = invocation.operationResult as Item; + this.currentFolderId = result.itemId; + this.currentPath = result.itemPath ? [...result.itemPath] : []; + } } catch (e) { captureException(e); const errorMessage = this.intl.t('osf-components.addons-service.file-manager.get-item-error'); @@ -98,16 +107,15 @@ export default class FileManager extends Component { kwargs.itemId = this.currentFolderId; kwargs.pageCursor = this.cursor; try { - let invocation; + const getFolderArgs = !this.currentFolderId ? {} : kwargs; + const invocation = await taskFor(this.args.configuredStorageAddon.getFolderItems).perform(getFolderArgs); + this.lastInvocation = invocation; + const operationResult = invocation.operationResult as ListItemsResult; if (!this.currentFolderId) { - invocation = await taskFor(this.args.configuredStorageAddon.getFolderItems).perform(); - } else { - invocation = await taskFor(this.args.configuredStorageAddon.getFolderItems).perform(kwargs); + this.currentFolderId = operationResult.items[0].itemId; } - this.lastInvocation = invocation; - const { operationResult } = invocation; this.currentItems = this.cursor ? [...this.currentItems, ...operationResult.items] : operationResult.items; - this.hasMore = Boolean(invocation.operationResult.cursor); + this.hasMore = Boolean(operationResult.nextSampleCursor); } catch (e) { captureException(e); const errorMessage = this.intl.t('osf-components.addons-service.file-manager.get-items-error'); diff --git a/mirage/views/addons.ts b/mirage/views/addons.ts index bc087aca79..5a1e2019f1 100644 --- a/mirage/views/addons.ts +++ b/mirage/views/addons.ts @@ -260,20 +260,23 @@ export function createAddonOperationInvocation(this: HandlerContext, schema: Sch const invocation = schema.addonOperationInvocations.create(attrs) as ModelInstance; let result: any = null; const folderId = kwargs.item_id || 'root'; + const item_type = kwargs.item_type || ItemType.Folder; + const fakePath = folderId.split('-') + .map((folder: string) => ({ item_id: folder, item_name: folder, item_type: ItemType.Folder })); if (attrs.operationName === ConnectedOperationNames.GetItemInfo) { result = { item_id: folderId, - item_name: `Item with ID ${folderId}`, - item_type: kwargs.item_type || ItemType.Folder, - item_path: folderId, + item_name: `Folder with ID ${folderId}`, + item_type, + item_path: folderId === 'root' ? undefined : fakePath, }; } else { result = { items: '12345'.split('').map(i => ({ item_id: `${folderId}-${i}`, - item_name: `${kwargs.item_type}${i} in ${folderId}`, - item_type: kwargs.item_type, - item_path: folderId, + item_name: `${item_type}${i} in ${folderId}`, + item_type, + item_path: folderId === 'root' ? undefined : fakePath, })), }; } diff --git a/tests/acceptance/guid-node/addons-test.ts b/tests/acceptance/guid-node/addons-test.ts index 40a5130fd7..cab1372d25 100644 --- a/tests/acceptance/guid-node/addons-test.ts +++ b/tests/acceptance/guid-node/addons-test.ts @@ -192,6 +192,7 @@ module('Acceptance | guid-node/addons', hooks => { assert.dom('[data-test-display-name-input]').exists('Name input is present'); assert.dom('[data-test-display-name-input]') .hasValue(s3AccountsDisplayNamesAndRootFolders[0].displayName, 'Name input has correct value'); + assert.dom('[data-test-go-to-root]').exists('Go to root button is present'); assert.dom('[data-test-folder-path-option]').exists({ count: 1 }, 'Folder path shown'); assert.dom('[data-test-root-folder-save]').isEnabled('Save button is enabled when name is present'); assert.dom('[data-test-root-folder-cancel]').exists('Cancel button is present'); @@ -223,7 +224,7 @@ module('Acceptance | guid-node/addons', hooks => { const firstRootFolder = document.querySelectorAll('[data-test-configured-addon-root-folder]')[0]; assert.dom(firstDisplayName) .containsText('New S3 Account Display Name', 'Display name change is saved'); - assert.dom(firstRootFolder).containsText('s3root-1-1', 'Root folder change is saved'); + assert.dom(firstRootFolder).containsText('root-1-1', 'Root folder change is saved'); // Remove other account await click('[data-test-remove-connected-location]:last-child'); diff --git a/translations/en-us.yml b/translations/en-us.yml index 38ed2c0fd5..3240b3719f 100644 --- a/translations/en-us.yml +++ b/translations/en-us.yml @@ -64,6 +64,7 @@ general: hosted_on_the_osf: 'Hosted on OSF' last_modified: 'Last modified' loading: Loading... + load_more: 'Load more' md5: MD5 modified: Modified more: more @@ -351,6 +352,7 @@ addons: configure: heading: 'Configure {providerName}' display-name: 'Display name' + go-to-root: 'Go to home folder' go-to-folder: 'Go to folder {folderName}' select-folder: 'Select {folderName} as root folder' table-headings: