Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENG-4760] User secondary metadata #2031

Merged
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
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'] }));
aaxelb marked this conversation as resolved.
Show resolved Hide resolved
}
} 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