Skip to content

Commit 3433ed7

Browse files
authored
Merge pull request #1129 from fabmiz/ENG-2083/drafts-contrib-list
[ENG-2083] Allow contributors to remove themselves from draft registrations
2 parents 7b023d7 + c35f583 commit 3433ed7

File tree

12 files changed

+251
-20
lines changed

12 files changed

+251
-20
lines changed

app/adapters/contributor.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { assert } from '@ember/debug';
12
import { inject as service } from '@ember/service';
23
import DS from 'ember-data';
34

@@ -15,18 +16,27 @@ export default class ContributorAdapter extends OsfAdapter {
1516
requestType: string,
1617
) {
1718
if (requestType === 'createRecord' || requestType === 'findRecord') {
18-
const [nId, uId] = (id || '').split('-');
19-
const nodeId = snapshot ? snapshot.record.get('nodeId') : nId;
19+
let userId;
20+
let nodeId;
21+
22+
if (requestType === 'findRecord') {
23+
[nodeId, userId] = (id || '').split('-');
24+
assert(`"contributorId" must be "nodeId-userId": got ${nodeId}-${userId}`, Boolean(nodeId && userId));
25+
} else {
26+
nodeId = snapshot.record.get('nodeId');
27+
assert(`"nodeId" is required to create a contributor; got ${nodeId}`, Boolean(nodeId));
28+
}
29+
2030
const node = this.store.peekRecord('node', nodeId);
2131

2232
if (!node) {
2333
throw new Error('Trying to add a contributor to a Node that hasn\'t been loaded into the store');
2434
}
2535

26-
const base = this.buildRelationshipURL((node as any)._internalModel.createSnapshot(), 'contributors');
36+
const baseUrl = this.buildRelationshipURL((node as any)._internalModel.createSnapshot(), 'contributors');
2737

2838
if (requestType === 'findRecord') {
29-
return `${base}${uId}/`;
39+
return `${baseUrl}${userId}/`;
3040
}
3141

3242
const params = {
@@ -35,7 +45,7 @@ export default class ContributorAdapter extends OsfAdapter {
3545
send_email: snapshot ? (snapshot.record.get('sendEmail') || false) : true,
3646
};
3747

38-
return `${base}?${param(params)}`;
48+
return `${baseUrl}?${param(params)}`;
3949
}
4050

4151
return super.buildURL(modelName, id, snapshot, requestType);

lib/osf-components/addon/components/contributor-list/component.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import { tagName } from '@ember-decorators/component';
22
import Component from '@ember/component';
3-
import { computed } from '@ember/object';
3+
import { action, computed } from '@ember/object';
44
import { alias } from '@ember/object/computed';
55
import { inject as service } from '@ember/service';
6-
import { task } from 'ember-concurrency-decorators';
6+
import { dropTask, task } from 'ember-concurrency-decorators';
77
import DS from 'ember-data';
8+
import config from 'ember-get-config';
9+
import Intl from 'ember-intl/services/intl';
10+
import Toast from 'ember-toastr/services/toast';
811

12+
import RouterService from '@ember/routing/router-service';
913
import { layout } from 'ember-osf-web/decorators/component';
1014
import Contributor from 'ember-osf-web/models/contributor';
1115
import Node from 'ember-osf-web/models/node';
1216
import { QueryHasManyResult } from 'ember-osf-web/models/osf-model';
17+
import CurrentUser from 'ember-osf-web/services/current-user';
1318
import Ready from 'ember-osf-web/services/ready';
19+
import captureException from 'ember-osf-web/utils/capture-exception';
1420
import defaultTo from 'ember-osf-web/utils/default-to';
1521

1622
import styles from './styles';
@@ -26,14 +32,19 @@ export default class ContributorList extends Component {
2632
shouldTruncate: boolean = defaultTo(this.shouldTruncate, true);
2733
shouldLinkUsers: boolean = defaultTo(this.shouldLinkUsers, false);
2834
shouldEnableClaimUser: boolean = false;
35+
allowRemoveMe: boolean = false;
2936

3037
// Private properties
3138
@service store!: DS.Store;
3239
@service ready!: Ready;
40+
@service toast!: Toast;
41+
@service intl!: Intl;
42+
@service currentUser!: CurrentUser;
43+
@service router!: RouterService;
3344

3445
page = 1;
3546
displayedContributors: Contributor[] = [];
36-
totalContributors?: number;
47+
totalContributors = 0;
3748
shouldLoadAll: boolean = navigator.userAgent.includes('Prerender');
3849

3950
@alias('loadContributors.isRunning')
@@ -71,6 +82,46 @@ export default class ContributorList extends Component {
7182
blocker.done();
7283
});
7384

85+
@dropTask({ withTestWaiter: true })
86+
removeMeTask = task(function *(this: ContributorList) {
87+
if (!this.node || this.node.isAnonymous || !this.currentUser.currentUserId) {
88+
return;
89+
}
90+
91+
const userID = this.currentUser.currentUserId;
92+
let contributor = this.displayedContributors
93+
.find(contrib => contrib.users.get('id') === this.currentUser.currentUserId);
94+
95+
if (!contributor) {
96+
contributor = yield this.store.findRecord('contributor', `${this.node.id}-${userID}`);
97+
this.setProperties({
98+
displayedContributors: [...this.displayedContributors, contributor],
99+
});
100+
}
101+
102+
try {
103+
yield contributor!.destroyRecord();
104+
this.toast.success(this.intl.t('contributor_list.remove_contributor.success'));
105+
this.router.transitionTo('home');
106+
} catch (e) {
107+
const { supportEmail } = config.support;
108+
const errorMessage = this.intl
109+
.t('contributor_list.remove_contributor.error', { supportEmail, htmlSafe: true });
110+
captureException(e, { errorMessage });
111+
this.toast.error(errorMessage);
112+
}
113+
});
114+
115+
@action
116+
removeMe() {
117+
this.removeMeTask.perform();
118+
}
119+
120+
@computed('allowRemoveMe', 'currentUser.currentUserId', 'totalContributors')
121+
get shouldShowRemoveMeButton() {
122+
return this.allowRemoveMe && this.currentUser.currentUserId && this.totalContributors > 1;
123+
}
124+
74125
@computed('truncated')
75126
get truncateCount() {
76127
return this.shouldTruncate ? 3 : undefined;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
:global(.btn).load-contribs {
22
padding: 0 0 3px;
33
}
4+
5+
.Button__removeMe {
6+
margin-bottom: 5px;
7+
}

lib/osf-components/addon/components/contributor-list/template.hbs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
{{#if this.node.isAnonymous}}
22
{{t 'contributor_list.anonymous'}}
33
{{else}}
4+
{{#if this.shouldShowRemoveMeButton}}
5+
<div local-class='Button__removeMe'>
6+
<DeleteButton
7+
data-test-contributor-remove-me
8+
local-class='RemoveContributorButton'
9+
@buttonLabel={{t 'contributor_list.remove_contributor.button_label'}}
10+
@delete={{this.removeMe}}
11+
@smallSecondary={{true}}
12+
@modalTitle={{t 'contributor_list.remove_contributor.confirm_remove.title'}}
13+
@modalBody={{t 'contributor_list.remove_contributor.confirm_remove.body'}}
14+
@confirmButtonText={{t 'contributor_list.remove_contributor.confirm_remove.button'}}
15+
/>
16+
</div>
17+
{{/if}}
418
<InlineList
519
@items={{this.displayedContributors}}
620
@total={{this.totalContributors}}

lib/osf-components/addon/components/delete-button/component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default class DeleteButton extends Component {
2727

2828
// Optional arguments
2929
small: boolean = defaultTo(this.small, false);
30+
smallSecondary: boolean = defaultTo(this.smallSecondary, false);
3031
noBackground: boolean = defaultTo(this.noBackground, false);
3132
hardConfirm: boolean = defaultTo(this.hardConfirm, false);
3233
disabled: boolean = defaultTo(this.disabled, false);

lib/osf-components/addon/components/delete-button/styles.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
}
1111
}
1212

13+
.Button__secondaryDestroy {
14+
color: $brand-danger;
15+
}
16+
1317
.Modal__confirmBlock {
1418
display: block;
1519
font-weight: normal;

lib/osf-components/addon/components/delete-button/template.hbs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@
1010
>
1111
{{fa-icon 'times' size='lg'}}
1212
</Button>
13+
{{else if this.smallSecondary}}
14+
<Button
15+
data-analytics-name='Delete button'
16+
data-test-delete-button-secondary-destroy
17+
local-class='Button__secondaryDestroy'
18+
disabled={{this.disabled}}
19+
@type='secondary'
20+
@layout='small'
21+
{{on 'click' this._show}}
22+
>
23+
{{this.buttonLabel}}
24+
</Button>
1325
{{else}}
1426
<Button
1527
data-analytics-name='Delete button'

lib/osf-components/addon/components/registries/review-metadata-renderer/template.hbs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{{assert 'Registries::ReviewMetadataRenderer requires a draftRegistration' @draftRegistration}}
22
<h2 id='MetadataPageLabel'>{{t 'registries.drafts.draft.metadata.page_label'}}</h2>
33

4-
<Registries::ReviewMetadataRenderer::LabelDisplay
4+
<Registries::ReviewMetadataRenderer::LabelDisplay
55
@draftRegistration={{@draftRegistration}}
66
@changeset={{@metadataChangeset}}
77
@field='title'
@@ -10,7 +10,7 @@
1010
{{~if @draftRegistration.title @draftRegistration.title (t 'registries.registration_metadata.no_title')~}}
1111
</Registries::ReviewMetadataRenderer::LabelDisplay>
1212

13-
<Registries::ReviewMetadataRenderer::LabelDisplay
13+
<Registries::ReviewMetadataRenderer::LabelDisplay
1414
@draftRegistration={{@draftRegistration}}
1515
@changeset={{@metadataChangeset}}
1616
@field='description'
@@ -19,7 +19,20 @@
1919
{{~if @draftRegistration.description @draftRegistration.description (t 'registries.registration_metadata.no_description')~}}
2020
</Registries::ReviewMetadataRenderer::LabelDisplay>
2121

22-
<Registries::ReviewMetadataRenderer::LabelDisplay
22+
<Registries::ReviewMetadataRenderer::LabelDisplay
23+
@draftRegistration={{@draftRegistration}}
24+
@changeset={{@metadataChangeset}}
25+
@field='contributors'
26+
>
27+
<ContributorList
28+
@node={{get-model @draftRegistration.branchedFrom}}
29+
@shouldLinkUsers={{true}}
30+
@shouldTruncate={{false}}
31+
@allowRemoveMe={{true}}
32+
/>
33+
</Registries::ReviewMetadataRenderer::LabelDisplay>
34+
35+
<Registries::ReviewMetadataRenderer::LabelDisplay
2336
@draftRegistration={{@draftRegistration}}
2437
@changeset={{@metadataChangeset}}
2538
@field='category'
@@ -34,25 +47,25 @@
3447
{{/if}}
3548
</Registries::ReviewMetadataRenderer::LabelDisplay>
3649

37-
<Registries::ReviewMetadataRenderer::LabelDisplay
50+
<Registries::ReviewMetadataRenderer::LabelDisplay
3851
@draftRegistration={{@draftRegistration}}
3952
@changeset={{@metadataChangeset}}
4053
@field='affiliated_institutions'
4154
@fieldText='affiliated institutions'
4255
local-class='{{unless @draftRegistration.affiliatedInstitutions 'NoResponse'}}'
4356
>
44-
{{~if @draftRegistration.affiliatedInstitutions
57+
{{~if @draftRegistration.affiliatedInstitutions
4558
(join ', ' (map-by 'name' @draftRegistration.affiliatedInstitutions))
4659
(t 'registries.registration_metadata.no_affiliated_institutions')~}}
4760
</Registries::ReviewMetadataRenderer::LabelDisplay>
4861

49-
<Registries::ReviewMetadataRenderer::LabelDisplay
62+
<Registries::ReviewMetadataRenderer::LabelDisplay
5063
@draftRegistration={{@draftRegistration}}
5164
@changeset={{@metadataChangeset}}
5265
@field='license'
5366
local-class='{{unless @draftRegistration.license.name 'NoResponse'}}'
5467
>
55-
{{#if @draftRegistration.license.name}}
68+
{{#if @draftRegistration.license.name}}
5669
<LicenseViewer @registration={{@draftRegistration}} />
5770
{{else}}
5871
{{~t 'registries.form_view.none_selected'~}}
@@ -64,7 +77,7 @@
6477
<div local-class='NestedProperty'>
6578
{{#each fields as |field|}}
6679
{{#let (get @draftRegistration.nodeLicense field) as |nodeLicenseField|}}
67-
<Registries::ReviewMetadataRenderer::LabelDisplay
80+
<Registries::ReviewMetadataRenderer::LabelDisplay
6881
@draftRegistration={{@draftRegistration}}
6982
@changeset={{@metadataChangeset}}
7083
@field='nodeLicense.{{field}}'
@@ -84,21 +97,21 @@
8497
{{/if}}
8598
{{/let}}
8699

87-
<Registries::ReviewMetadataRenderer::LabelDisplay
100+
<Registries::ReviewMetadataRenderer::LabelDisplay
88101
@draftRegistration={{@draftRegistration}}
89102
@changeset={{@metadataChangeset}}
90103
@field='subjects'
91104
local-class='{{unless @draftRegistration.subjects 'NoResponse'}}'
92105
>
93-
{{#if @draftRegistration.subjects}}
106+
{{#if @draftRegistration.subjects}}
94107
<Subjects::Display @subjects={{@draftRegistration.subjects}} />
95108
{{else}}
96109
{{~t 'registries.registration_metadata.no_subjects'~}}
97110
{{/if}}
98111

99112
</Registries::ReviewMetadataRenderer::LabelDisplay>
100113

101-
<Registries::ReviewMetadataRenderer::LabelDisplay
114+
<Registries::ReviewMetadataRenderer::LabelDisplay
102115
@draftRegistration={{@draftRegistration}}
103116
@changeset={{@metadataChangeset}}
104117
@field='tags'

0 commit comments

Comments
 (0)