Skip to content

Commit

Permalink
[ENG-4760] User secondary metadata (#2031)
Browse files Browse the repository at this point in the history
* Add preprint relation to users

* Update result card for user info

* Group dt and dl a little closer

* CR feedback; Refactor secondary metatadata components

* CR feedback; Add test selector

* Better refactor

* Remove leftover arg
  • Loading branch information
futa-ikeda authored Oct 20, 2023
1 parent a9b403d commit bfb3f12
Show file tree
Hide file tree
Showing 15 changed files with 194 additions and 34 deletions.
51 changes: 50 additions & 1 deletion app/models/index-card.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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) {
Expand All @@ -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' {
Expand Down
6 changes: 5 additions & 1 deletion app/models/search-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'] }));
Expand Down
4 changes: 4 additions & 0 deletions app/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -114,6 +115,9 @@ export default class UserModel extends OsfModel.extend(Validations) {
@hasMany('draft-registration')
draftRegistrations!: AsyncHasMany<DraftRegistrationModel>;

@hasMany('preprint')
preprints!: AsyncHasMany<PreprintModel>;

@hasMany('institution', { inverse: 'users' })
institutions!: AsyncHasMany<InstitutionModel>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ as |layout|>
<SearchResultCard @result={{item}} />
{{else}}
<div local-class='no-results'>
<p>{{t 'search.no-results'}}</p>
<p data-test-search-page-no-results>{{t 'search.no-results'}}</p>
</div>
{{/each}}
{{/if}}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Args> {
@service intl!: Intl;
@service store!: Store;

@tracked isOpenSecondaryMetadata = false;
@tracked osfUser?: UserModel;

@action
toggleSecondaryMetadata() {
Expand All @@ -22,10 +28,23 @@ export default class SearchResultCard extends Component<Args> {
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,3 @@
</dd>
{{/if}}
</dl>

Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@
</InlineList>
</dd>
{{/if}}
</dl>
</dl>
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,11 @@
padding-top: 10px;
}

dt,
dd {
dt {
margin: 15px 0 0 5px;
}

dd {
margin-left: 5px;
}
}
42 changes: 20 additions & 22 deletions lib/osf-components/addon/components/search-result-card/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,25 @@
<div local-class='type-label'>
{{this.cardTypeLabel}}
</div>
{{#if (not-eq @result.resourceType 'user')}}
<Button
{{on 'click' this.toggleSecondaryMetadata}}
aria-label={{if this.isOpenSecondaryMetadata
(t 'osf-components.search-result-card.hide_additional_metadata')
(t 'osf-components.search-result-card.show_additional_metadata')
}}
aria-controls={{secondaryMetadataPanelId}}
aria-expanded={{this.isOpenSecondaryMetadata}}
>
{{#if this.isOpenSecondaryMetadata}}
<FaIcon
@icon={{'angle-up'}}
/>
{{else}}
<FaIcon
@icon={{'angle-down'}}
/>
{{/if}}
</Button>
{{/if}}
<Button
{{on 'click' this.toggleSecondaryMetadata}}
aria-label={{if this.isOpenSecondaryMetadata
(t 'osf-components.search-result-card.hide_additional_metadata')
(t 'osf-components.search-result-card.show_additional_metadata')
}}
aria-controls={{secondaryMetadataPanelId}}
aria-expanded={{this.isOpenSecondaryMetadata}}
>
{{#if this.isOpenSecondaryMetadata}}
<FaIcon
@icon={{'angle-up'}}
/>
{{else}}
<FaIcon
@icon={{'angle-down'}}
/>
{{/if}}
</Button>
</div>
<h4>
<a data-test-search-result-card-title href={{@result.absoluteUrl}} target='_blank' rel='noopener noreferrer'> {{@result.displayTitle}} </a>
Expand Down Expand Up @@ -104,4 +102,4 @@
</panel.body>
</CpPanel>
{{/let}}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -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<Args> {
@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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{{#if this.getOsfUserModel.isRunning}}
<LoadingIndicator @dark={{true}}/>
{{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}}
<dl>
{{#if this.user.employment.length}}
<dt>{{t 'osf-components.search-result-card.employment'}}</dt>
<dd>{{this.user.employment.[0].institution}}</dd>
{{/if}}
{{#if this.user.education.length}}
<dt>{{t 'osf-components.search-result-card.education'}}</dt>
<dd>{{this.user.education.[0].institution}}</dd>
{{/if}}
<dt>{{t 'osf-components.search-result-card.public_projects'}}</dt>
<dd>{{this.user.relatedCounts.nodes}}</dd>
<dt>{{t 'osf-components.search-result-card.public_registrations'}}</dt>
<dd>{{this.user.relatedCounts.registrations}}</dd>
<dt>{{t 'osf-components.search-result-card.public_preprints'}}</dt>
<dd>{{this.user.relatedCounts.preprints}}</dd>
</dl>
{{/if}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'osf-components/components/search-result-card/user-secondary-metadata/component';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'osf-components/components/search-result-card/user-secondary-metadata/template';
8 changes: 8 additions & 0 deletions mirage/serializers/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ export default class UserSerializer extends ApplicationSerializer<User> {
},
},
},
preprints: {
links: {
related: {
href: `${apiUrl}/v2/users/${model.id}/preprints/`,
meta: this.buildRelatedLinkMeta(model, 'preprints'),
},
},
},
};
if (model.defaultRegion) {
serializedRelationships.defaultRegion = {
Expand Down
7 changes: 7 additions & 0 deletions translations/en-us.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.'
Expand Down

0 comments on commit bfb3f12

Please sign in to comment.