mirror of https://github.com/apache/nifi.git
NIFI-13094: About dialog (#8698)
* NIFI-13094: - Adding the about dialog. - Adding favicon. * NIFI-13094: - Addressing review feedback. This closes #8698
This commit is contained in:
parent
7bb6eebf2e
commit
36bf498340
|
@ -44,7 +44,6 @@ import { MatOptionModule } from '@angular/material/core';
|
|||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { selectAbout } from '../../../../state/about/about.selectors';
|
||||
import { loadAbout } from '../../../../state/about/about.actions';
|
||||
import { debounceTime } from 'rxjs';
|
||||
import { NiFiCommon } from '../../../../service/nifi-common.service';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
@ -133,7 +132,6 @@ export class FlowConfigurationHistoryListing implements OnInit, OnDestroy {
|
|||
|
||||
ngOnInit(): void {
|
||||
this.refresh();
|
||||
this.store.dispatch(loadAbout());
|
||||
|
||||
this.onFormChanges();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { filter, switchMap, take, tap } from 'rxjs';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
@ -52,13 +52,12 @@ import {
|
|||
selectTimeOffset
|
||||
} from '../../../../state/flow-configuration/flow-configuration.selectors';
|
||||
import { selectAbout } from '../../../../state/about/about.selectors';
|
||||
import { loadAbout } from '../../../../state/about/about.actions';
|
||||
|
||||
@Component({
|
||||
templateUrl: './manage-remote-ports.component.html',
|
||||
styleUrls: ['./manage-remote-ports.component.scss']
|
||||
})
|
||||
export class ManageRemotePorts implements OnInit, OnDestroy {
|
||||
export class ManageRemotePorts implements OnDestroy {
|
||||
initialSortColumn: 'name' | 'type' | 'tasks' | 'count' | 'size' | 'duration' | 'compression' | 'actions' = 'name';
|
||||
initialSortDirection: 'asc' | 'desc' = 'asc';
|
||||
activeSort: Sort = {
|
||||
|
@ -167,10 +166,6 @@ export class ManageRemotePorts implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.store.dispatch(loadAbout());
|
||||
}
|
||||
|
||||
isInitialLoading(state: RemotePortsState): boolean {
|
||||
// using the current timestamp to detect the initial load event
|
||||
return state.loadedTimestamp == initialState.loadedTimestamp;
|
||||
|
|
|
@ -19,7 +19,6 @@ import { Component, OnInit } from '@angular/core';
|
|||
import { Store } from '@ngrx/store';
|
||||
import { NiFiState } from '../../../state';
|
||||
import { loadProvenanceOptions } from '../state/provenance-event-listing/provenance-event-listing.actions';
|
||||
import { loadAbout } from '../../../state/about/about.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'provenance',
|
||||
|
@ -31,6 +30,5 @@ export class Provenance implements OnInit {
|
|||
|
||||
ngOnInit(): void {
|
||||
this.store.dispatch(loadProvenanceOptions());
|
||||
this.store.dispatch(loadAbout());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,20 +15,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { NiFiState } from '../../../state';
|
||||
import { loadAbout } from '../../../state/about/about.actions';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'queue',
|
||||
templateUrl: './queue.component.html',
|
||||
styleUrls: ['./queue.component.scss']
|
||||
})
|
||||
export class Queue implements OnInit {
|
||||
constructor(private store: Store<NiFiState>) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.store.dispatch(loadAbout());
|
||||
}
|
||||
}
|
||||
export class Queue {}
|
||||
|
|
|
@ -22,6 +22,4 @@ export const loadAbout = createAction('[About] Load About');
|
|||
|
||||
export const loadAboutSuccess = createAction('[About] Load About Success', props<{ response: LoadAboutResponse }>());
|
||||
|
||||
export const aboutApiError = createAction('[About] About Api Error', props<{ error: string }>());
|
||||
|
||||
export const clearAboutApiError = createAction('[About] Clear About Api Error');
|
||||
export const openAboutDialog = createAction('[About] Open About Dialog');
|
||||
|
|
|
@ -18,14 +18,20 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||
import * as AboutActions from './about.actions';
|
||||
import { catchError, from, map, of, switchMap } from 'rxjs';
|
||||
import * as ErrorActions from '../error/error.actions';
|
||||
import { catchError, from, map, of, switchMap, tap } from 'rxjs';
|
||||
import { AboutService } from '../../service/about.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { AboutDialog } from '../../ui/common/about-dialog/about-dialog.component';
|
||||
import { MEDIUM_DIALOG } from '../../index';
|
||||
|
||||
@Injectable()
|
||||
export class AboutEffects {
|
||||
constructor(
|
||||
private actions$: Actions,
|
||||
private aboutService: AboutService
|
||||
private aboutService: AboutService,
|
||||
private dialog: MatDialog
|
||||
) {}
|
||||
|
||||
loadAbout$ = createEffect(() =>
|
||||
|
@ -39,10 +45,25 @@ export class AboutEffects {
|
|||
response
|
||||
})
|
||||
),
|
||||
catchError((error) => of(AboutActions.aboutApiError({ error: error.error })))
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(ErrorActions.snackBarError({ error: errorResponse.error }))
|
||||
)
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
openAboutDialog$ = createEffect(
|
||||
() =>
|
||||
this.actions$.pipe(
|
||||
ofType(AboutActions.openAboutDialog),
|
||||
tap(() => {
|
||||
this.dialog.open(AboutDialog, {
|
||||
...MEDIUM_DIALOG
|
||||
});
|
||||
})
|
||||
),
|
||||
{ dispatch: false }
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,11 +17,10 @@
|
|||
|
||||
import { createReducer, on } from '@ngrx/store';
|
||||
import { AboutState } from './index';
|
||||
import { aboutApiError, clearAboutApiError, loadAbout, loadAboutSuccess } from './about.actions';
|
||||
import { loadAbout, loadAboutSuccess } from './about.actions';
|
||||
|
||||
export const initialState: AboutState = {
|
||||
about: null,
|
||||
error: null,
|
||||
status: 'pending'
|
||||
};
|
||||
|
||||
|
@ -34,17 +33,6 @@ export const aboutReducer = createReducer(
|
|||
on(loadAboutSuccess, (state, { response }) => ({
|
||||
...state,
|
||||
about: response.about,
|
||||
error: null,
|
||||
status: 'success' as const
|
||||
})),
|
||||
on(aboutApiError, (state, { error }) => ({
|
||||
...state,
|
||||
error: error,
|
||||
status: 'error' as const
|
||||
})),
|
||||
on(clearAboutApiError, (state) => ({
|
||||
...state,
|
||||
error: null,
|
||||
status: 'pending' as const
|
||||
}))
|
||||
);
|
||||
|
|
|
@ -27,14 +27,13 @@ export interface About {
|
|||
uri: string;
|
||||
contentViewerUrl: string;
|
||||
timezone: string;
|
||||
buildTag: string;
|
||||
buildRevision: string;
|
||||
buildBranch: string;
|
||||
buildTimestamp: string;
|
||||
buildTag?: string;
|
||||
buildRevision?: string;
|
||||
buildBranch?: string;
|
||||
buildTimestamp?: string;
|
||||
}
|
||||
|
||||
export interface AboutState {
|
||||
about: About | null;
|
||||
error: string | null;
|
||||
status: 'pending' | 'loading' | 'error' | 'success';
|
||||
status: 'pending' | 'loading' | 'success';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<!--
|
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
~ contributor license agreements. See the NOTICE file distributed with
|
||||
~ this work for additional information regarding copyright ownership.
|
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
~ (the "License"); you may not use this file except in compliance with
|
||||
~ the License. You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="about-dialog" tabindex="0">
|
||||
<h2 mat-dialog-title>About</h2>
|
||||
<mat-dialog-content>
|
||||
<div class="flex flex-col justify-between gap-y-5">
|
||||
@if (about$ | async; as about) {
|
||||
<div class="flex justify-center">
|
||||
<img ngSrc="assets/icons/nifi-logo-about.svg" priority width="300" height="128" alt="NiFi Logo" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<div class="font-bold">{{ about.version }}</div>
|
||||
@if (about.buildTimestamp) {
|
||||
<div class="text-xs ml-2">{{ about.buildTimestamp }}</div>
|
||||
}
|
||||
@if (about.buildTag && about.buildTag !== 'HEAD') {
|
||||
<div class="text-xs ml-2">Tagged {{ about.buildTag }}</div>
|
||||
}
|
||||
@if (about.buildRevision) {
|
||||
<div class="text-xs ml-2">From {{ about.buildRevision }} on branch {{ about.buildBranch }}</div>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
Apache NiFi is a framework to support highly scalable and flexible dataflows. It can be run on
|
||||
laptops up through clusters of enterprise class servers. Instead of dictating a particular dataflow
|
||||
or behavior it empowers you to design your own optimal dataflow tailored to your specific
|
||||
environment.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Close</button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
.about-dialog {
|
||||
@include mat.button-density(-1);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AboutDialog } from './about-dialog.component';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { initialState } from '../../../state/component-state/component-state.reducer';
|
||||
|
||||
describe('AboutDialog', () => {
|
||||
let component: AboutDialog;
|
||||
let fixture: ComponentFixture<AboutDialog>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [AboutDialog, NoopAnimationsModule],
|
||||
providers: [provideMockStore({ initialState })]
|
||||
});
|
||||
fixture = TestBed.createComponent(AboutDialog);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { AsyncPipe, NgOptimizedImage } from '@angular/common';
|
||||
import { NifiTooltipDirective } from '../tooltips/nifi-tooltip.directive';
|
||||
import { NifiSpinnerDirective } from '../spinner/nifi-spinner.directive';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { AboutState } from '../../../state/about';
|
||||
import { selectAbout } from '../../../state/about/about.selectors';
|
||||
|
||||
@Component({
|
||||
selector: 'about',
|
||||
standalone: true,
|
||||
templateUrl: './about-dialog.component.html',
|
||||
imports: [
|
||||
MatButtonModule,
|
||||
MatDialogModule,
|
||||
MatTableModule,
|
||||
MatSortModule,
|
||||
NifiTooltipDirective,
|
||||
NifiSpinnerDirective,
|
||||
AsyncPipe,
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
NgOptimizedImage
|
||||
],
|
||||
styleUrls: ['./about-dialog.component.scss']
|
||||
})
|
||||
export class AboutDialog {
|
||||
about$ = this.store.select(selectAbout);
|
||||
|
||||
constructor(private store: Store<AboutState>) {}
|
||||
}
|
|
@ -140,7 +140,7 @@
|
|||
<i class="fa fa-fw fa-question-circle primary-color mr-2"></i>
|
||||
Help
|
||||
</button>
|
||||
<button mat-menu-item class="global-menu-item">
|
||||
<button mat-menu-item class="global-menu-item" (click)="viewAbout()">
|
||||
<i class="fa fa-fw fa-info-circle primary-color mr-2"></i>
|
||||
About
|
||||
</button>
|
||||
|
|
|
@ -36,6 +36,7 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
|
|||
import { OS_SETTING, LIGHT_THEME, DARK_THEME, ThemingService } from '../../../service/theming.service';
|
||||
import { loadFlowConfiguration } from '../../../state/flow-configuration/flow-configuration.actions';
|
||||
import { startCurrentUserPolling, stopCurrentUserPolling } from '../../../state/current-user/current-user.actions';
|
||||
import { loadAbout, openAboutDialog } from '../../../state/about/about.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'navigation',
|
||||
|
@ -83,6 +84,7 @@ export class Navigation implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.store.dispatch(loadAbout());
|
||||
this.store.dispatch(loadFlowConfiguration());
|
||||
this.store.dispatch(startCurrentUserPolling());
|
||||
}
|
||||
|
@ -123,6 +125,10 @@ export class Navigation implements OnInit, OnDestroy {
|
|||
);
|
||||
}
|
||||
|
||||
viewAbout() {
|
||||
this.store.dispatch(openAboutDialog());
|
||||
}
|
||||
|
||||
getCanvasLink(): string {
|
||||
const canvasRoute = this.storage.getItem<string>('current-canvas-route');
|
||||
return canvasRoute || '/';
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<svg id="nifi-drop" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 37"><defs><style>.cls-1{fill:#728e9b;}</style></defs><title>Untitled-1</title><path id="drop" class="cls-1" d="M13 35.88A12.55 12.55 0 0 1 .41 23.33c0-6 4.68-11.69 7.88-15.87A45.4 45.4 0 0 0 13 0a45.38 45.38 0 0 0 4.68 7.43c3.2 4.18 7.88 9.9 7.88 15.87h-8v4.6H13v8zm4.91-3v-4.1h-4.1v4.1h4.1zm-4.1 4.1a12.24 12.24 0 0 0 4.1-.79v-2.11h-4.1v2.85zm8.7-8.7v-4.1h-4.1v4.1h4.1zm.24 5.34l.51-.51v-3.63h-4.1v4.1h3.59zM19.39 37a12.26 12.26 0 0 0 3-1.86h-3V37zm6.38-8.73a12.21 12.21 0 0 0 .79-4.1h-2.85v4.1h2.06zm-1 4.55a12.24 12.24 0 0 0 1.86-3h-1.9v3z"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.1 KiB |
|
@ -21,7 +21,7 @@
|
|||
<meta charset="utf-8" />
|
||||
<title>NiFi</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
<link rel="icon" type="image/svg+xml" href="assets/icons/nifi-drop.svg" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
</head>
|
||||
|
|
Loading…
Reference in New Issue