Skip to content

Commit 7ac66e8

Browse files
Merge pull request #1936 from CentreForDigitalHumanities/feature/namechange-alert
Feature/namechange alert
2 parents 0244eec + 9b17ec1 commit 7ac66e8

File tree

10 files changed

+154
-11
lines changed

10 files changed

+154
-11
lines changed

frontend/src/app/app.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<ia-notifications></ia-notifications>
22
<ia-menu role="banner" *ngIf="!iframe"></ia-menu>
3+
<ia-alert></ia-alert>
34
<main id="main" tabindex="-1">
45
<router-outlet></router-outlet>
56
<ia-dialog></ia-dialog>

frontend/src/app/app.component.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ import { environment } from '@environments/environment';
77
selector: 'ia-root',
88
templateUrl: './app.component.html',
99
styleUrls: ['./app.component.scss'],
10-
standalone: false
10+
standalone: false,
1111
})
1212
export class AppComponent {
1313
public iframe: boolean;
1414

15-
constructor(private authService: AuthService) {
15+
constructor(
16+
private authService: AuthService,
17+
) {
1618
this.authService.setInitialAuth();
1719
this.iframe = environment.runInIFrame;
1820
}
19-
}
2021

22+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<div *ngIf="visible$|async" class="notification is-warning is-light">
2+
@let alertConfig = alertMessage$ | async;
3+
<button class="delete" (click)="hideAlert()" aria-label="close"></button>
4+
@if (alertConfig.renderHtml) {
5+
<div [innerHtml]="alertConfig.message"></div>
6+
}
7+
@else {
8+
<div>{{alertConfig.message}}</div>
9+
}
10+
11+
</div>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.notification {
2+
--padding-size: .75rem;
3+
4+
padding: var(--padding-size) var(--padding-size);
5+
border-radius: 0;
6+
font-size: small;
7+
8+
button {
9+
top: var(--padding-size)
10+
}
11+
}
12+
13+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { AlertComponent } from './alert.component';
4+
5+
describe('AlertComponent', () => {
6+
let component: AlertComponent;
7+
let fixture: ComponentFixture<AlertComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
declarations: [AlertComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(AlertComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Component, DestroyRef } from '@angular/core';
2+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3+
import { AlertConfig, AlertService } from '@services/alert.service';
4+
import _ from 'lodash';
5+
import { BehaviorSubject, filter } from 'rxjs';
6+
7+
@Component({
8+
selector: 'ia-alert',
9+
standalone: false,
10+
templateUrl: './alert.component.html',
11+
styleUrl: './alert.component.scss',
12+
})
13+
export class AlertComponent {
14+
visible$ = new BehaviorSubject<boolean>(false);
15+
alertMessage$ = new BehaviorSubject<AlertConfig>(undefined);
16+
17+
constructor(
18+
private alertService: AlertService,
19+
private destroyRef: DestroyRef
20+
) {
21+
this.alertService.alert$
22+
.pipe(
23+
takeUntilDestroyed(this.destroyRef),
24+
filter(_.negate(_.isUndefined))
25+
)
26+
.subscribe({
27+
next: (config: AlertConfig) => this.showAlert(config),
28+
});
29+
}
30+
31+
showAlert(message: AlertConfig): void {
32+
this.alertMessage$.next(message);
33+
this.visible$.next(true);
34+
}
35+
36+
hideAlert(): void {
37+
this.alertMessage$.value.onDismiss?.();
38+
this.visible$.next(false);
39+
this.alertMessage$.next(undefined);
40+
}
41+
}

frontend/src/app/core/core.module.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { MenuDropdownComponent } from './menu/menu-dropdown/menu-dropdown.compon
77
import { FooterComponent } from './footer/footer.component';
88
import { DialogComponent } from './dialog/dialog.component';
99
import { CorpusSelectionModule } from '../corpus-selection/corpus-selection.module';
10+
import { AlertComponent } from './alert/alert.component';
1011

1112

1213
/** toplevel components such as the home page and navbar */
@@ -18,16 +19,15 @@ import { CorpusSelectionModule } from '../corpus-selection/corpus-selection.modu
1819
MenuComponent,
1920
MenuDropdownComponent,
2021
NotificationsComponent,
22+
AlertComponent,
2123
],
22-
imports: [
23-
SharedModule,
24-
CorpusSelectionModule,
25-
],
24+
imports: [SharedModule, CorpusSelectionModule],
2625
exports: [
26+
AlertComponent,
2727
DialogComponent,
2828
FooterComponent,
2929
MenuComponent,
3030
NotificationsComponent,
31-
]
31+
],
3232
})
33-
export class CoreModule { }
33+
export class CoreModule {}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { AlertService } from './alert.service';
4+
5+
describe('AlertService', () => {
6+
let service: AlertService;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
service = TestBed.inject(AlertService);
11+
});
12+
13+
it('should be created', () => {
14+
expect(service).toBeTruthy();
15+
});
16+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Injectable } from '@angular/core';
2+
import { environment } from '@environments/environment';
3+
import { ReplaySubject } from 'rxjs';
4+
5+
export interface AlertConfig {
6+
message: string;
7+
onDismiss?: () => any;
8+
renderHtml?: boolean;
9+
}
10+
11+
const nameChangeAlert = {
12+
message:
13+
'"I-Analyzer" has been renamed, and is now called "Textcavator". Read more about this change in <a href="https://github.com/CentreForDigitalHumanities/Textcavator/discussions/1958" target="_blank">this announcement on GitHub.</a>',
14+
onDismiss: () => localStorage.setItem('closedNameChangeAlert', 'true'),
15+
renderHtml: true,
16+
};
17+
18+
@Injectable({
19+
providedIn: 'root',
20+
})
21+
export class AlertService {
22+
alert$ = new ReplaySubject<AlertConfig>(1);
23+
24+
constructor() {
25+
this.showNamechangeAlert();
26+
}
27+
28+
private showNamechangeAlert(): void {
29+
if (
30+
environment.showNamechangeAlert &&
31+
!localStorage.getItem('closedNameChangeAlert')
32+
) {
33+
this.alert$.next(nameChangeAlert);
34+
}
35+
}
36+
}

frontend/src/environments/environment.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ export const environment = {
1515
logo: '/assets/logo.svg',
1616
logoAlt: undefined,
1717
},
18-
appDescription:
19-
`<p>
18+
appDescription: `<p>
2019
Textcavator is a tool for exploring corpora (large collections of texts).
2120
You can use Textcavator to find relevant documents or visualise broader trends
2221
in the corpus.
@@ -45,4 +44,5 @@ export const environment = {
4544
sourceUrl: 'https://github.com/CentreForDigitalHumanities/Textcavator/',
4645
logos: undefined,
4746
showCorpusFilters: true,
47+
showNamechangeAlert: true,
4848
};

0 commit comments

Comments
 (0)