Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4026407
Update develop from main (#322)
bp-cos Sep 4, 2025
52c5896
Feat(8653): Implement view tracking for registrations and preprints (…
opaduchak Sep 4, 2025
6ff10ff
[ENG-8624] feat(registries): add context to registration submission +…
adlius Sep 4, 2025
0fed748
[ENG-8504] Show Osf introduction video and Collections,Institutions, …
mkovalua Sep 4, 2025
5bbf4b8
Fix/develop conflicts (#332)
nsemets Sep 5, 2025
c75d7b7
[ENG-8639] add <meta> tags to files detail (#324)
aaxelb Sep 9, 2025
bde80ff
Add TOS Banner to angular-osf
mkovalua Sep 10, 2025
16bfc29
implement tos banner component
mkovalua Sep 11, 2025
508832e
use accepted_terms_of_service for models
mkovalua Sep 11, 2025
1692455
update tos component
mkovalua Sep 11, 2025
e7cbe50
get acceptedTermsOfService from db on component initialization
mkovalua Sep 11, 2025
48582dd
Update acceptedTermsOfService on checkbox select and continue click
mkovalua Sep 11, 2025
4cac855
fix(tos banner): updating component and updating acceptedTermsOfServi…
mkovalua Sep 11, 2025
be026c2
fix(tos banner): update terms of use
mkovalua Sep 11, 2025
7ef46e1
fix(tos banner): use en.json for text
mkovalua Sep 11, 2025
fee9e8f
fix(tos banner): code updates
mkovalua Sep 11, 2025
47bb32f
fix(tos banner): merge main into feature/ENG-8780 and resolve merge c…
mkovalua Sep 11, 2025
38e064a
fix(tos-banner): merge main to ENG-8780
mkovalua Sep 12, 2025
1f45526
fix(tos-banner): add tos banner to dashboard
mkovalua Sep 12, 2025
6268e6f
fix(tos-banner): remove subscribe for updateUserProfile call
mkovalua Sep 12, 2025
a5207f5
fix(dashboard): fix CR comments
mkovalua Sep 12, 2025
b60bc7e
fix(tos-banner): fix CR comments
mkovalua Sep 12, 2025
1d3b623
fix(banner): fixed banner
nsemets Sep 12, 2025
cbe41f4
Merge remote-tracking branch 'origin/main' into feature/ENG-8780
nsemets Sep 12, 2025
d519a1d
fix(tos-banner): update en.json
mkovalua Sep 12, 2025
26e0fed
fix(tos-banner): Convert acceptedTermsOfService to accepted_terms_of_…
mkovalua Sep 12, 2025
f7c0ca7
fix(tos-banner): convert acceptedTermsOfService to accepted_terms_of_…
mkovalua Sep 12, 2025
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
13 changes: 11 additions & 2 deletions src/app/core/services/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { UserMapper } from '@osf/shared/mappers';
import {
JsonApiResponse,
ProfileSettingsUpdate,
User,
User, UserAcceptedTermsOfServiceJsonApi,
UserData,
UserDataJsonApi,
UserDataResponseJsonApi,
Expand Down Expand Up @@ -53,12 +53,21 @@ export class UserService {
}

updateUserProfile(userId: string, key: string, data: ProfileSettingsUpdate): Observable<User> {
const patchedData = key === ProfileSettingsKey.User ? data : { [key]: data };
let data_formatted = ProfileSettingsKey.User && data.hasOwnProperty('acceptedTermsOfService') ? {accepted_terms_of_service: true} : data;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image Did you have here a lint error?

const patchedData = key === ProfileSettingsKey.User ? data_formatted : { [key]: data_formatted };

return this.jsonApiService
.patch<UserDataJsonApi>(`${this.apiUrl}/users/${userId}/`, {
data: { type: 'users', id: userId, attributes: patchedData },
})
.pipe(map((response) => UserMapper.fromUserGetResponse(response)));
}

updateUserAcceptedTermsOfService(userId: string, data: UserAcceptedTermsOfServiceJsonApi): Observable<User> {
return this.jsonApiService
.patch<UserDataJsonApi>(`${this.apiUrl}/users/${userId}/`, {
data: { type: 'users', id: userId, attributes: data },
})
.pipe(map((response) => UserMapper.fromUserGetResponse(response)));
}
}
4 changes: 4 additions & 0 deletions src/app/core/store/user/user.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export class SetUserAsModerator {
static readonly type = '[User] Set User As Moderator';
}

export class AcceptTermsOfServiceByUser {
static readonly type = '[User] Accept Terms Of Service';
}

export class ClearCurrentUser {
static readonly type = '[User] Clear Current User';
}
36 changes: 35 additions & 1 deletion src/app/core/store/user/user.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import { inject, Injectable } from '@angular/core';
import { ProfileSettingsKey } from '@osf/shared/enums';
import { removeNullable } from '@osf/shared/helpers';
import { UserMapper } from '@osf/shared/mappers';
import { Social } from '@osf/shared/models';
import { Social, User } from '@osf/shared/models';

import { UserService } from '../../services';

import {
AcceptTermsOfServiceByUser,
ClearCurrentUser,
GetCurrentUser,
GetCurrentUserSettings,
Expand Down Expand Up @@ -253,6 +254,39 @@ export class UserState {
});
}

@Action(AcceptTermsOfServiceByUser)
acceptTermsOfServiceByUser(ctx: StateContext<UserStateModel>) {
const state = ctx.getState();
const currentUser = state.currentUser.data;

if (!currentUser) {
return;
}

const updatePayload: Partial<User> = {
acceptedTermsOfService: true,
};
const apiRequest = UserMapper.toAcceptedTermsOfServiceRequest(updatePayload);

return this.userService
.updateUserAcceptedTermsOfService(currentUser.id, apiRequest)
.pipe(
tap((response: User): void => {
if (response.acceptedTermsOfService) {
ctx.patchState({
currentUser: {
...state.currentUser,
data: {
...currentUser,
acceptedTermsOfService: true,
},
},
});
}
})
);
}

@Action(ClearCurrentUser)
clearCurrentUser(ctx: StateContext<UserStateModel>) {
ctx.patchState({
Expand Down
2 changes: 1 addition & 1 deletion src/app/features/files/mappers/resource-metadata.mapper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IdentifiersMapper } from '@shared/mappers/identifiers.mapper';
import { ResourceMetadata } from '@shared/models';
import { ResourceMetadata } from '@osf/shared/models';

import { GetResourceCustomMetadataResponse } from '../models/get-resource-custom-metadata-response.model';
import { GetResourceShortInfoResponse } from '../models/get-resource-short-info-response.model';
Expand Down
1 change: 1 addition & 0 deletions src/app/features/home/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { TosConsentBannerComponent } from './tos-consent-banner/tos-consent-banner.component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
@if (!acceptedTermsOfServiceChange()) {
<div class="p-4 bg-white">
<p-message styleClass="w-full" severity="warn">
<ng-template #container>
<div class="flex flex-column w-full">
<div class="flex items-center gap-2">
<osf-icon iconClass="fas fa-triangle-exclamation"></osf-icon>

<span class="font-medium">
{{ 'toast.tos-consent.message' | translate }}
<a class="font-bold" [routerLink]="['/terms-of-use']" target="_blank">
{{ 'toast.tos-consent.termsOfUse' | translate }}
</a>
{{ 'toast.tos-consent.and' | translate }}
<a class="font-bold" [routerLink]="['/privacy-policy']" target="_blank">
{{ 'toast.tos-consent.privacyPolicy' | translate }}
</a>
</span>
</div>

<div class="flex align-items-center justify-content-between mt-2">
<div class="flex align-items-center gap-2">
<p-checkbox
[ngModel]="acceptedTermsOfService()"
(ngModelChange)="acceptedTermsOfService.set($event)"
binary="true"
inputId="terms"
>
</p-checkbox>

<label for="terms" class="m-0 cursor-pointer">
{{ 'toast.tos-consent.haveReadAndAgree' | translate }}
</label>
</div>

<p-button
link
[label]="'toast.tos-consent.continue' | translate"
[disabled]="!acceptedTermsOfService()"
(onClick)="onContinue()"
>
</p-button>
</div>
</div>
</ng-template>
</p-message>
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { TosConsentBannerComponent } from './tos-consent-banner.component';

describe.skip('TosConsentBannerComponent', () => {
let component: TosConsentBannerComponent;
let fixture: ComponentFixture<TosConsentBannerComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TosConsentBannerComponent],
}).compileComponents();

fixture = TestBed.createComponent(TosConsentBannerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createDispatchMap, select } from '@ngxs/store';

import { TranslatePipe, TranslateService } from '@ngx-translate/core';

import { Button } from 'primeng/button';
import { Checkbox } from 'primeng/checkbox';
import { Message } from 'primeng/message';

import { Component, computed, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router';

import { AcceptTermsOfServiceByUser, UserSelectors } from '@core/store/user';
import { IconComponent } from '@osf/shared/components';
import { ToastService } from '@osf/shared/services';

@Component({
selector: 'osf-tos-consent-banner',
imports: [FormsModule, Checkbox, Button, Message, TranslatePipe, IconComponent, RouterLink],
templateUrl: './tos-consent-banner.component.html',
styleUrls: ['./tos-consent-banner.component.scss'],
})
export class TosConsentBannerComponent {
private readonly toastService = inject(ToastService);
private readonly translateService = inject(TranslateService);

readonly actions = createDispatchMap({ acceptTermsOfServiceByUser: AcceptTermsOfServiceByUser });
readonly currentUser = select(UserSelectors.getCurrentUser);

acceptedTermsOfService = signal(false);
errorMessage: string | null = null;

acceptedTermsOfServiceChange = computed(() => this.currentUser()?.acceptedTermsOfService);

onContinue() {
if (!this.acceptedTermsOfService()) {
this.errorMessage = this.translateService.instant('toast.tos-consent.error-message');
this.toastService.showError(this.errorMessage as string);
return;
}

this.errorMessage = null;
this.actions.acceptTermsOfServiceByUser();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
[buttonLabel]="'home.loggedIn.dashboard.createProject' | translate"
(buttonClick)="createProject()"
/>
<osf-tos-consent-banner></osf-tos-consent-banner>
<div>
<div class="quick-search-container py-4 px-3 md:px-4">
<p class="text-center mb-4 xl:mb-6">
Expand Down Expand Up @@ -75,6 +76,7 @@ <h1>{{ 'home.loggedIn.hosting.title' | translate }}</h1>
[buttonLabel]="'home.loggedIn.dashboard.createProject' | translate"
(buttonClick)="createProject()"
/>
<osf-tos-consent-banner></osf-tos-consent-banner>
<div class="flex items-center justify-center min-h-screen bg-white pt-4">
<div class="text-center max-w-2xl px-6 w-full">
<p class="mb-4">{{ 'home.loggedIn.dashboard.noCreatedProject' | translate }}</p>
Expand Down
3 changes: 3 additions & 0 deletions src/app/features/home/pages/dashboard/dashboard.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { MyResourcesItem, MyResourcesSearchFilters, TableParameters } from '@osf
import { ProjectRedirectDialogService } from '@osf/shared/services';
import { ClearMyResources, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores';

import { TosConsentBannerComponent } from '../../components';

@Component({
selector: 'osf-dashboard',
imports: [
Expand All @@ -38,6 +40,7 @@ import { ClearMyResources, GetMyProjects, MyResourcesSelectors } from '@osf/shar
IconComponent,
TranslatePipe,
LoadingSpinnerComponent,
TosConsentBannerComponent,
],
templateUrl: './dashboard.component.html',
styleUrl: './dashboard.component.scss',
Expand Down
Empty file.
10 changes: 9 additions & 1 deletion src/app/shared/mappers/user/user.mapper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
User,
User, UserAcceptedTermsOfServiceJsonApi,
UserData,
UserDataJsonApi,
UserDataResponseJsonApi,
Expand Down Expand Up @@ -35,6 +35,7 @@ export class UserMapper {
defaultRegionId: user.relationships?.default_region?.data?.id,
allowIndexing: user.attributes?.allow_indexing,
canViewReviews: user.attributes.can_view_reviews === true, // [NS] Do not simplify it
acceptedTermsOfService: user.attributes.accepted_terms_of_service,
};
}

Expand Down Expand Up @@ -67,4 +68,11 @@ export class UserMapper {
suffix: name.suffix ?? '',
};
}

static toAcceptedTermsOfServiceRequest(name: Partial<User>): UserAcceptedTermsOfServiceJsonApi {
return {
accepted_terms_of_service: name.acceptedTermsOfService ?? false,
};
}

}
Empty file.
6 changes: 6 additions & 0 deletions src/app/shared/models/user/user.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface User {
allowIndexing: boolean | undefined;
isModerator?: boolean;
canViewReviews: boolean;
acceptedTermsOfService: boolean;
}

export interface UserSettings {
Expand All @@ -47,6 +48,7 @@ export interface UserDataJsonApi {
date_registered: string;
allow_indexing?: boolean;
can_view_reviews: boolean;
accepted_terms_of_service: boolean;
};
relationships: {
default_region: {
Expand Down Expand Up @@ -103,3 +105,7 @@ export interface UserData {
activeFlags: string[];
currentUser: User | null;
}

export interface UserAcceptedTermsOfServiceJsonApi {
accepted_terms_of_service: boolean;
}
8 changes: 8 additions & 0 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,14 @@
"recentActivity": "Recent Activity"
},
"toast": {
"tos-consent": {
"message": "Notice: We've updated our",
"termsOfUse": " terms of use ",
"and": " and ",
"privacyPolicy": " privacy policy.",
"haveReadAndAgree": "I've read and agree to the terms and conditions",
"continue": "Continue"
},
"cookie-consent": {
"message": "Notice: This website relies on cookies to help provide a better user experience. By clicking accept or continuing to use the site, you consent to our use of cookies. See our Privacy Policy and Cookie Use for more information.",
"accept": "Accept cookies"
Expand Down
Loading