diff --git a/app/adapters/node-mapcore-group.ts b/app/adapters/node-mapcore-group.ts new file mode 100644 index 000000000..4645e384c --- /dev/null +++ b/app/adapters/node-mapcore-group.ts @@ -0,0 +1,17 @@ +import OsfAdapter from './osf-adapter'; // Import OsfAdapter + +export default class NodeMapcoreGroupAdapter extends OsfAdapter { + urlForQuery(query: any, modelName: string) { + const { nodeId } = query; + if (nodeId) { + return `${this.host || ''}/v2/nodes/${nodeId}/map_core/groups`; + } + return super.urlForQuery(query, modelName as any); + } +} + +declare module 'ember-data/types/registries/adapter' { + export default interface AdapterRegistry { + 'node-mapcore-group': NodeMapcoreGroupAdapter; + } // eslint-disable-line semi +} diff --git a/app/dashboard/template.hbs b/app/dashboard/template.hbs index d7a3809e9..4718d979a 100644 --- a/app/dashboard/template.hbs +++ b/app/dashboard/template.hbs @@ -19,7 +19,7 @@
-
+

{{t 'dashboard.title'}}

@@ -59,7 +59,7 @@

{{t 'dashboard.quicksearch.other_links' htmlSafe=true useSearch=this.useSearch}}

-
+
{{t 'general.title'}} {{t 'general.contributors'}}
-
+
{{t 'general.modified'}} {{t 'general.quota_notice'}}
+
+
+ {{t 'general.groups'}} +
+
diff --git a/app/models/node-mapcore-group.ts b/app/models/node-mapcore-group.ts new file mode 100644 index 000000000..04f354f41 --- /dev/null +++ b/app/models/node-mapcore-group.ts @@ -0,0 +1,25 @@ +import { buildValidations } from 'ember-cp-validations'; +import DS from 'ember-data'; +import OsfModel, { Permission } from './osf-model'; + +const { attr } = DS; + +const Validations = buildValidations({}); +export const permissions = Object.freeze(Object.values(Permission)); + +export default class NodeMapcoreGroupModel extends OsfModel.extend(Validations) { + @attr('number') nodeGroupId!: number; + @attr('number') creatorId!: number; + @attr('fixstring') creator!: string; + @attr('fixstring') permission!: Permission; + @attr('number') mapcoreGroupId!: number; + @attr('fixstring') name!: string; + @attr('fixstring') created!: string; + @attr('fixstring') modified!: string; +} + +declare module 'ember-data/types/registries/model' { + export default interface ModelRegistry { + 'node-mapcore-group': NodeMapcoreGroupModel; + } // eslint-disable-line semi +} diff --git a/lib/handbook/addon/routes.js b/lib/handbook/addon/routes.js index 9e7268271..e1a56d8ba 100644 --- a/lib/handbook/addon/routes.js +++ b/lib/handbook/addon/routes.js @@ -50,6 +50,7 @@ export default buildRoutes(function() { this.route('schema-block-group-renderer'); this.route('tags-widget'); this.route('validated-model-form'); + this.route('group-list'); }); this.route('helpers', function() { diff --git a/lib/osf-components/addon/components/dashboard-item/template.hbs b/lib/osf-components/addon/components/dashboard-item/template.hbs index 836a12997..247d4cc7c 100644 --- a/lib/osf-components/addon/components/dashboard-item/template.hbs +++ b/lib/osf-components/addon/components/dashboard-item/template.hbs @@ -5,7 +5,7 @@
-
+
{{@node.title}} @@ -16,7 +16,7 @@
-
+
{{this.date}}
@@ -25,6 +25,11 @@
{{this.quotaNotice}}
+
+
+ +
+
diff --git a/lib/osf-components/addon/components/group-list/component.ts b/lib/osf-components/addon/components/group-list/component.ts new file mode 100644 index 000000000..0c8b0d410 --- /dev/null +++ b/lib/osf-components/addon/components/group-list/component.ts @@ -0,0 +1,75 @@ +import { tagName } from '@ember-decorators/component'; +import Component from '@ember/component'; +import { computed } from '@ember/object'; +import { alias } from '@ember/object/computed'; +import { inject as service } from '@ember/service'; +import { task } from 'ember-concurrency-decorators'; +import DS from 'ember-data'; + +import { layout } from 'ember-osf-web/decorators/component'; +import Node from 'ember-osf-web/models/node'; +import NodeMapcoreGroup from 'ember-osf-web/models/node-mapcore-group'; +import Ready from 'ember-osf-web/services/ready'; +import captureException from 'ember-osf-web/utils/capture-exception'; +import defaultTo from 'ember-osf-web/utils/default-to'; + +import styles from './styles'; +import template from './template'; + +@layout(template, styles) +@tagName('span') +export default class GroupList extends Component { + // Required arguments + node?: Node; + + // Optional arguments + shouldTruncate: boolean = defaultTo(this.shouldTruncate, true); + + // Private properties + @service store!: DS.Store; + @service ready!: Ready; + + displayedGroups: NodeMapcoreGroup[] = []; + totalGroups?: number; + + @alias('loadGroups.isRunning') + isLoading!: boolean; + + @task({ restartable: true, on: 'didReceiveAttrs' }) + loadGroups = task(function *(this: GroupList) { + try { + if (!this.node || this.node.isAnonymous) { + return; + } + const blocker = this.ready.getBlocker(); + const itemsFromResult = (res: any) => { + const arr = (res && typeof res.toArray === 'function') ? res.toArray() : res || []; + return arr.map((item: any) => { + if (item && typeof item === 'object' && item.__data) { + return item.__data; + } + return item; + }); + }; + + const query = { nodeId: this.node.id, page: 1, visible: true }; + const result = yield this.store.query('node-mapcore-group', query); + const groups = itemsFromResult(result) as NodeMapcoreGroup[]; + const meta = (result as any).meta || {}; + this.setProperties({ + displayedGroups: groups, + totalGroups: meta.total || groups.length, + }); + + blocker.done(); + } catch (e) { + captureException(e, { errorMessage: 'loadGroups task failed synchronously' }); + throw e; + } + }); + + @computed('truncated') + get truncateCount() { + return this.shouldTruncate ? 3 : undefined; + } +} diff --git a/lib/osf-components/addon/components/group-list/group/component.ts b/lib/osf-components/addon/components/group-list/group/component.ts new file mode 100644 index 000000000..ef4cdbeba --- /dev/null +++ b/lib/osf-components/addon/components/group-list/group/component.ts @@ -0,0 +1,28 @@ +import { tagName } from '@ember-decorators/component'; +import Component from '@ember/component'; +import { task } from 'ember-concurrency-decorators'; + +import { layout } from 'ember-osf-web/decorators/component'; +import NodeMapcoreGroup from 'ember-osf-web/models/node-mapcore-group'; +import defaultTo from 'ember-osf-web/utils/default-to'; +import template from './template'; + +@layout(template) +@tagName('') +export default class NodeMapcoreGroupListGroup extends Component { + group!: NodeMapcoreGroup; + shouldShortenName: boolean = defaultTo(this.shouldShortenName, false); + + groupName?: string; + + @task({ restartable: true, on: 'didReceiveAttrs' }) + loadGroup = task(function *(this: NodeMapcoreGroupListGroup) { + yield Promise.resolve(); + this.set( + 'groupName', + this.shouldShortenName + ? this.group && this.group.name + : this.group.name, + ); + }); +} diff --git a/lib/osf-components/addon/components/group-list/group/template.hbs b/lib/osf-components/addon/components/group-list/group/template.hbs new file mode 100644 index 000000000..26b5bb1bd --- /dev/null +++ b/lib/osf-components/addon/components/group-list/group/template.hbs @@ -0,0 +1,3 @@ + + {{~this.groupName~}} + \ No newline at end of file diff --git a/lib/osf-components/addon/components/group-list/styles.scss b/lib/osf-components/addon/components/group-list/styles.scss new file mode 100644 index 000000000..4cf5bb451 --- /dev/null +++ b/lib/osf-components/addon/components/group-list/styles.scss @@ -0,0 +1,3 @@ +:global(.btn).load-groups { + padding: 0 0 3px; +} diff --git a/lib/osf-components/addon/components/group-list/template.hbs b/lib/osf-components/addon/components/group-list/template.hbs new file mode 100644 index 000000000..19d033076 --- /dev/null +++ b/lib/osf-components/addon/components/group-list/template.hbs @@ -0,0 +1,23 @@ +{{#if this.node.isAnonymous}} + {{t 'group_list.anonymous'}} +{{else}} + + {{~#if list.item}} + {{~group-list/group + group=list.item + shouldShortenName=this.shouldTruncate + ~}} + {{else if list.remainingCount}} + {{#if this.isLoading }} + + {{else}} + {{t 'group_list.x_more' x=list.remainingCount}} + {{/if}} + {{/if~}} + +{{/if}} diff --git a/lib/osf-components/app/components/group-list/component.js b/lib/osf-components/app/components/group-list/component.js new file mode 100644 index 000000000..f255aea3a --- /dev/null +++ b/lib/osf-components/app/components/group-list/component.js @@ -0,0 +1 @@ +export { default } from 'osf-components/components/group-list/component'; diff --git a/lib/osf-components/app/components/group-list/group/component.js b/lib/osf-components/app/components/group-list/group/component.js new file mode 100644 index 000000000..d9cac0b79 --- /dev/null +++ b/lib/osf-components/app/components/group-list/group/component.js @@ -0,0 +1 @@ +export { default } from 'osf-components/components/group-list/group/component'; diff --git a/mirage/config.ts b/mirage/config.ts index 1607ac66b..019effa5b 100644 --- a/mirage/config.ts +++ b/mirage/config.ts @@ -86,6 +86,15 @@ export default function(this: Server) { defaultSortKey: 'index', onCreate: createBibliographicContributor, }); + // Added handler for node map_core groups so adapters querying + // Returns an empty paginated response by default. + this.get('/nodes/:parentID/map_core/groups', () => ({ + data: [], + meta: { + total: 0, + per_page: 10, + }, + })); this.get('/nodes/:parentID/files', nodeFileProviderList); // Node file providers list this.get('/nodes/:parentID/files/:fileProviderId', nodeFilesListForProvider); // Node files list for file provider diff --git a/translations/en-us.yml b/translations/en-us.yml index 6120993d9..e57e67416 100644 --- a/translations/en-us.yml +++ b/translations/en-us.yml @@ -95,6 +95,7 @@ general: preprints: Preprints registries: Registries other: Other + groups: Groups node_categories: analysis: Analysis communication: Communication @@ -709,6 +710,8 @@ list: contributor_list: x_more: '{x} more' anonymous: 'Anonymous contributors' +group_list: + x_more: '{x} more' app_components: branded_navbar: my_osf_projects: 'My GakuNin RDM Projects' diff --git a/translations/ja.yml b/translations/ja.yml index cd7debe6a..acf9bda09 100644 --- a/translations/ja.yml +++ b/translations/ja.yml @@ -95,6 +95,7 @@ general: preprints: プレプリント registries: 登録 other: Other + groups: グループ node_categories: analysis: 分析 communication: コミュニケーション @@ -709,6 +710,8 @@ list: contributor_list: x_more: '他{x}名' anonymous: 匿名のメンバー +group_list: + x_more: '他{x}名' app_components: branded_navbar: my_osf_projects: 'My GakuNin RDM Projects'