diff --git a/app/models/index-card.ts b/app/models/index-card.ts index 15b179cd90d..3f64d81888a 100644 --- a/app/models/index-card.ts +++ b/app/models/index-card.ts @@ -1,9 +1,15 @@ +import { getOwner } from '@ember/application'; import { inject as service } from '@ember/service'; +import { waitFor } from '@ember/test-waiters'; import Model, { AsyncHasMany, attr, hasMany } from '@ember-data/model'; +import { dropTask } from 'ember-concurrency'; import IntlService from 'ember-intl/services/intl'; import GetLocalizedPropertyHelper from 'ember-osf-web/helpers/get-localized-property'; -import { getOwner } from '@ember/application'; +import config from 'ember-osf-web/config/environment'; +import OsfModel from 'ember-osf-web/models/osf-model'; +import { tracked } from 'tracked-built-ins'; +const osfUrl = config.OSF.url; export interface LanguageText { '@language': string; @@ -23,10 +29,28 @@ export default class IndexCardModel extends Model { getLocalizedString = new GetLocalizedPropertyHelper(getOwner(this)); + @tracked osfModel?: OsfModel; + get resourceId() { return this.resourceIdentifier[0]; } + get osfModelType() { + const types = this.resourceMetadata.resourceType.map( (item: any) => item['@id']); + if (types.includes('Project') || types.includes('ProjectComponent')) { + return 'node'; + } else if (types.includes('Registration') || types.includes('RegistrationComponent')) { + return 'registration'; + } else if (types.includes('Preprint')) { + return 'preprint'; + } else if (types.includes('Person') || types.includes('Agent')) { + return 'user'; + } else if(types.includes('File')) { + return 'file'; + } + return null; + } + get label() { const possibleLabelKeys = ['displayLabel', 'name', 'title']; for (const key of possibleLabelKeys) { @@ -44,6 +68,31 @@ export default class IndexCardModel extends Model { } return ''; } + + @dropTask + @waitFor + async getOsfModel(options?: object) { + const identifier = this.resourceIdentifier; + if (identifier && this.osfModelType) { + const guid = this.guidFromIdentifierList(identifier); + if (guid) { + const osfModel = await this.store.findRecord(this.osfModelType, guid, options); + this.osfModel = osfModel; + } + } + } + + guidFromIdentifierList() { + for (const iri of this.resourceIdentifier) { + if (iri && iri.startsWith(osfUrl)) { + const pathSegments = iri.slice(osfUrl.length).split('/').filter(Boolean); + if (pathSegments.length === 1) { + return pathSegments[0]; // one path segment; looks like osf-id + } + } + } + return null; + } } declare module 'ember-data/types/registries/model' { diff --git a/app/models/search-result.ts b/app/models/search-result.ts index d3a5fa9412e..c21c51f1a57 100644 --- a/app/models/search-result.ts +++ b/app/models/search-result.ts @@ -73,11 +73,15 @@ export default class SearchResultModel extends Model { return this.resourceMetadata['@id']; } + // returns list of affilated institutions for users // returns list of contributors for osf objects // returns list of affiliated institutions for osf users get affiliatedEntities() { if (this.resourceType === 'user') { - // return something + if (this.resourceMetadata.affiliation) { + return this.resourceMetadata.affiliation.map((item: any) => + ({ name: item.name[0]['@value'], absoluteUrl: item['@id'] })); + } } else if (this.resourceMetadata.creator) { return this.resourceMetadata.creator?.map((item: any) => ({ name: item.name[0]['@value'], absoluteUrl: item['@id'] })); diff --git a/app/models/user.ts b/app/models/user.ts index 1c7e2f945f1..b02912bacfe 100644 --- a/app/models/user.ts +++ b/app/models/user.ts @@ -4,6 +4,7 @@ import { buildValidations, validator } from 'ember-cp-validations'; import config from 'ember-osf-web/config/environment'; import { Link } from 'jsonapi-typescript'; +import PreprintModel from 'ember-osf-web/models/preprint'; import SparseNodeModel from 'ember-osf-web/models/sparse-node'; import ContributorModel from './contributor'; import DraftRegistrationModel from './draft-registration'; @@ -114,6 +115,9 @@ export default class UserModel extends OsfModel.extend(Validations) { @hasMany('draft-registration') draftRegistrations!: AsyncHasMany; + @hasMany('preprint') + preprints!: AsyncHasMany; + @hasMany('institution', { inverse: 'users' }) institutions!: AsyncHasMany; diff --git a/lib/osf-components/addon/components/search-page/template.hbs b/lib/osf-components/addon/components/search-page/template.hbs index 047377bbdba..1c69cadb328 100644 --- a/lib/osf-components/addon/components/search-page/template.hbs +++ b/lib/osf-components/addon/components/search-page/template.hbs @@ -230,7 +230,7 @@ as |layout|> {{else}}
-

{{t 'search.no-results'}}

+

{{t 'search.no-results'}}

{{/each}} {{/if}} diff --git a/lib/osf-components/addon/components/search-result-card/component.ts b/lib/osf-components/addon/components/search-result-card/component.ts index d487ff36a03..a0af4a4941d 100644 --- a/lib/osf-components/addon/components/search-result-card/component.ts +++ b/lib/osf-components/addon/components/search-result-card/component.ts @@ -1,17 +1,23 @@ import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; +import Store from '@ember-data/store'; import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; -import SearchResultModel from 'ember-osf-web/models/search-result'; -import { inject as service } from '@ember/service'; import Intl from 'ember-intl/services/intl'; +import SearchResultModel from 'ember-osf-web/models/search-result'; +import UserModel from 'ember-osf-web/models/user'; + interface Args { result: SearchResultModel; } export default class SearchResultCard extends Component { @service intl!: Intl; + @service store!: Store; + @tracked isOpenSecondaryMetadata = false; + @tracked osfUser?: UserModel; @action toggleSecondaryMetadata() { @@ -22,10 +28,23 @@ export default class SearchResultCard extends Component { return this.intl.t(`osf-components.search-result-card.${this.args.result.resourceType}`); } - // not sure if this is the best way, as there was a resourceType of "unknown" out in the wild get secondaryMetadataComponent() { const { resourceType } = this.args.result; - - return `search-result-card/${resourceType.replace('_component', '')}-secondary-metadata`; + switch (resourceType) { + case 'project': + case 'project_component': + return 'search-result-card/project-secondary-metadata'; + case 'registration': + case 'registration_component': + return 'search-result-card/registration-secondary-metadata'; + case 'preprint': + return 'search-result-card/preprint-secondary-metadata'; + case 'file': + return 'search-result-card/file-secondary-metadata'; + case 'user': + return 'search-result-card/user-secondary-metadata'; + default: + return null; + } } } diff --git a/lib/osf-components/addon/components/search-result-card/preprint-secondary-metadata/template.hbs b/lib/osf-components/addon/components/search-result-card/preprint-secondary-metadata/template.hbs index 5ed73f65c5d..d67af09a99f 100644 --- a/lib/osf-components/addon/components/search-result-card/preprint-secondary-metadata/template.hbs +++ b/lib/osf-components/addon/components/search-result-card/preprint-secondary-metadata/template.hbs @@ -90,4 +90,3 @@ {{/if}} - diff --git a/lib/osf-components/addon/components/search-result-card/project-secondary-metadata/template.hbs b/lib/osf-components/addon/components/search-result-card/project-secondary-metadata/template.hbs index 87a2d0a1b6c..f0b29589063 100644 --- a/lib/osf-components/addon/components/search-result-card/project-secondary-metadata/template.hbs +++ b/lib/osf-components/addon/components/search-result-card/project-secondary-metadata/template.hbs @@ -57,4 +57,4 @@ {{/if}} - \ No newline at end of file + diff --git a/lib/osf-components/addon/components/search-result-card/styles.scss b/lib/osf-components/addon/components/search-result-card/styles.scss index 2a53b47ab83..8787c6c5670 100644 --- a/lib/osf-components/addon/components/search-result-card/styles.scss +++ b/lib/osf-components/addon/components/search-result-card/styles.scss @@ -80,8 +80,11 @@ padding-top: 10px; } - dt, - dd { + dt { margin: 15px 0 0 5px; } + + dd { + margin-left: 5px; + } } diff --git a/lib/osf-components/addon/components/search-result-card/template.hbs b/lib/osf-components/addon/components/search-result-card/template.hbs index ea6ba3e1f93..65911c42eba 100644 --- a/lib/osf-components/addon/components/search-result-card/template.hbs +++ b/lib/osf-components/addon/components/search-result-card/template.hbs @@ -5,27 +5,25 @@
{{this.cardTypeLabel}}
- {{#if (not-eq @result.resourceType 'user')}} - - {{/if}} +

{{@result.displayTitle}} @@ -104,4 +102,4 @@ {{/let}} - \ No newline at end of file + diff --git a/lib/osf-components/addon/components/search-result-card/user-secondary-metadata/component.ts b/lib/osf-components/addon/components/search-result-card/user-secondary-metadata/component.ts new file mode 100644 index 00000000000..b03c0a09f3a --- /dev/null +++ b/lib/osf-components/addon/components/search-result-card/user-secondary-metadata/component.ts @@ -0,0 +1,43 @@ +import { inject as service } from '@ember/service'; +import Store from '@ember-data/store'; +import Component from '@glimmer/component'; +import Intl from 'ember-intl/services/intl'; +import { taskFor } from 'ember-concurrency-ts'; + +import SearchResultModel from 'ember-osf-web/models/search-result'; +import UserModel from 'ember-osf-web/models/user'; +import { alias } from '@ember/object/computed'; +import { task } from 'ember-concurrency'; +import { waitFor } from '@ember/test-waiters'; + +interface Args { + result: SearchResultModel; +} + +export default class UserSecondaryMetadata extends Component { + @service intl!: Intl; + @service store!: Store; + + @alias('args.result.indexCard.osfModel') user?: UserModel; + + constructor(owner: unknown, args: Args) { + super(owner, args); + if (!this.user) { + taskFor(this.getOsfUserModel).perform(); + } + } + + @task + @waitFor + async getOsfUserModel() { + const options = { + adapterOptions: { + query: { + related_counts: 'nodes,registrations,preprints', + }, + }, + reload: true, + }; + await taskFor(this.args.result.indexCard.get('getOsfModel')).perform(options); + } +} diff --git a/lib/osf-components/addon/components/search-result-card/user-secondary-metadata/template.hbs b/lib/osf-components/addon/components/search-result-card/user-secondary-metadata/template.hbs new file mode 100644 index 00000000000..3b29069da0a --- /dev/null +++ b/lib/osf-components/addon/components/search-result-card/user-secondary-metadata/template.hbs @@ -0,0 +1,24 @@ +{{#if this.getOsfUserModel.isRunning}} + +{{else if this.getOsfUserModel.isError}} + {{t 'osf-components.search-result-card.fetch_user_error'}} +{{else if (not this.user)}} + {{t 'osf-components.search-result-card.no_user'}} +{{else}} +
+ {{#if this.user.employment.length}} +
{{t 'osf-components.search-result-card.employment'}}
+
{{this.user.employment.[0].institution}}
+ {{/if}} + {{#if this.user.education.length}} +
{{t 'osf-components.search-result-card.education'}}
+
{{this.user.education.[0].institution}}
+ {{/if}} +
{{t 'osf-components.search-result-card.public_projects'}}
+
{{this.user.relatedCounts.nodes}}
+
{{t 'osf-components.search-result-card.public_registrations'}}
+
{{this.user.relatedCounts.registrations}}
+
{{t 'osf-components.search-result-card.public_preprints'}}
+
{{this.user.relatedCounts.preprints}}
+
+{{/if}} diff --git a/lib/osf-components/app/components/search-result-card/user-secondary-metadata/component.js b/lib/osf-components/app/components/search-result-card/user-secondary-metadata/component.js new file mode 100644 index 00000000000..ba01e369bdf --- /dev/null +++ b/lib/osf-components/app/components/search-result-card/user-secondary-metadata/component.js @@ -0,0 +1 @@ +export { default } from 'osf-components/components/search-result-card/user-secondary-metadata/component'; diff --git a/lib/osf-components/app/components/search-result-card/user-secondary-metadata/template.js b/lib/osf-components/app/components/search-result-card/user-secondary-metadata/template.js new file mode 100644 index 00000000000..342377bcb97 --- /dev/null +++ b/lib/osf-components/app/components/search-result-card/user-secondary-metadata/template.js @@ -0,0 +1 @@ +export { default } from 'osf-components/components/search-result-card/user-secondary-metadata/template'; diff --git a/mirage/serializers/user.ts b/mirage/serializers/user.ts index 4036c9df063..fef5af248b8 100644 --- a/mirage/serializers/user.ts +++ b/mirage/serializers/user.ts @@ -58,6 +58,14 @@ export default class UserSerializer extends ApplicationSerializer { }, }, }, + preprints: { + links: { + related: { + href: `${apiUrl}/v2/users/${model.id}/preprints/`, + meta: this.buildRelatedLinkMeta(model, 'preprints'), + }, + }, + }, }; if (model.defaultRegion) { serializedRelationships.defaultRegion = { diff --git a/translations/en-us.yml b/translations/en-us.yml index 99c02c3ec78..d6a4b1533e5 100644 --- a/translations/en-us.yml +++ b/translations/en-us.yml @@ -1896,6 +1896,13 @@ osf-components: withdrawn: Withdrawn unknown: Unknown remaining_count: '{count} more' + fetch_user_error: 'Unable to fetch user information from OSF' + no_user: 'No user' + employment: Employment + education: Education + public_projects: Public projects + public_registrations: Public registrations + public_preprints: Public preprints resources-list: add_instructions: 'Link a DOI from a repository to your registration by clicking the green “+” button.' add_instructions_adhere: 'Contributors affirmed to adhere to the criteria for each badge.'