diff --git a/docs/extending/application-actions.md b/docs/extending/application-actions.md index 368e09826..b0ba5e08c 100644 --- a/docs/extending/application-actions.md +++ b/docs/extending/application-actions.md @@ -76,48 +76,49 @@ and perform document list reload if needed. Below is the list of public actions types you can use in the plugin definitions as a reference to the action: -| Name | Payload | Description | -| ---------------------- | ------------------- | ----------------------------------------------------------------------------------------------- | -| SET_CURRENT_FOLDER | Node | Notify components about currently opened folder. | -| SET_CURRENT_URL | string | Notify components about current browser URL. | -| SET_USER_PROFILE | Person | Assign current user profile. | -| TOGGLE_INFO_DRAWER | n/a | Toggle info drawer for the selected node. | -| ADD_FAVORITE | MinimalNodeEntity[] | Add nodes (or selection) to favorites. | -| REMOVE_FAVORITE | MinimalNodeEntity[] | Removes nodes (or selection) from favorites. | -| DELETE_LIBRARY | string | Delete a Library by id. Takes selected node if payload not provided. | -| CREATE_LIBRARY | n/a | Invoke a "Create Library" dialog. | -| SET_SELECTED_NODES | MinimalNodeEntity[] | Notify components about selected nodes. | -| DELETE_NODES | MinimalNodeEntity[] | Delete the nodes (or selection). Supports undo actions. | -| UNDO_DELETE_NODES | any[] | Reverts deletion of nodes (or selection). | -| RESTORE_DELETED_NODES | MinimalNodeEntity[] | Restores deleted nodes (or selection). Typically used with Trashcan. | -| PURGE_DELETED_NODES | MinimalNodeEntity[] | Permanently delete nodes (or selection). Typically used with Trashcan. | -| DOWNLOAD_NODES | MinimalNodeEntity[] | Download nodes (or selections). Creates a ZIP archive for folders or multiple items. | -| CREATE_FOLDER | string | Invoke a "Create Folder" dialog for the opened folder (or the parent folder id in the payload). | -| EDIT_FOLDER | MinimalNodeEntity | Invoke an "Edit Folder" dialog for the node (or selection). | -| SHARE_NODE | MinimalNodeEntity | Invoke a "Share" dialog for the node (or selection). | -| UNSHARE_NODES | MinimalNodeEntity[] | Remove nodes (or selection) from the shared nodes (does not remove content). | -| COPY_NODES | MinimalNodeEntity[] | Invoke a "Copy" dialog for the nodes (or selection). Supports undo actions. | -| MOVE_NODES | MinimalNodeEntity[] | Invoke a "Move" dialog for the nodes (or selection). Supports undo actions. | -| MANAGE_PERMISSIONS | MinimalNodeEntity | Invoke a "Manage Permissions" dialog for the node (or selection). | -| MANAGE_VERSIONS | MinimalNodeEntity | Invoke a "Manage Versions" dialog for the node (or selection). | -| NAVIGATE_URL | string | Navigate to a given route URL within the application. | -| NAVIGATE_ROUTE | any[] | Navigate to a particular Route (supports parameters). | -| NAVIGATE_FOLDER | MinimalNodeEntity | Navigate to a folder based on the Node properties. | -| NAVIGATE_PARENT_FOLDER | MinimalNodeEntity | Navigate to a containing folder based on the Node properties. | -| NAVIGATE_LIBRARY | string | Navigate to library. | -| SEARCH_BY_TERM | string | Perform a simple search by the term and navigate to Search results. | -| SNACKBAR_INFO | string | Show information snackbar with the message provided. | -| SNACKBAR_WARNING | string | Show warning snackbar with the message provided. | -| SNACKBAR_ERROR | string | Show error snackbar with the message provided. | -| UPLOAD_FILES | n/a | Invoke "Upload Files" dialog and upload files to the currently opened folder. | -| UPLOAD_FOLDER | n/a | Invoke "Upload Folder" dialog and upload selected folder to the currently opened one. | -| UPLOAD_FILE_VERSION | n/a | Invoke "New File Version" dialog. | -| VIEW_FILE | MinimalNodeEntity | Preview the file (or selection) in the Viewer. | -| UNLOCK_WRITE | NodeEntry | Unlock file from read only mode | -| PRINT_FILE | MinimalNodeEntity | Print the file opened in the Viewer (or selected). | -| FULLSCREEN_VIEWER | n/a | Enters fullscreen mode to view the file opened in the Viewer. | -| LOGOUT | n/a | Log out and redirect to Login screen. | -| RELOAD_DOCUMENT_LIST | n/a | Reload active document list | -| TOGGLE_SEARCH_FILTER | n/a | Toggle Filter component visibility in Search Results. | -| SHOW_SEARCH_FILTER | n/a | Show Filter component in Search Results. | -| HIDE_SEARCH_FILTER | n/a | Hide Filter component in Search Results | +| Version | Name | Payload | Description | +| ------- | ---------------------- | ------------------- | ----------------------------------------------------------------------------------------------- | +| 1.7.0 | SET_CURRENT_FOLDER | Node | Notify components about currently opened folder. | +| 1.7.0 | SET_CURRENT_URL | string | Notify components about current browser URL. | +| 1.7.0 | SET_USER_PROFILE | Person | Assign current user profile. | +| 1.7.0 | TOGGLE_INFO_DRAWER | n/a | Toggle info drawer for the selected node. | +| 1.7.0 | ADD_FAVORITE | MinimalNodeEntity[] | Add nodes (or selection) to favorites. | +| 1.7.0 | REMOVE_FAVORITE | MinimalNodeEntity[] | Removes nodes (or selection) from favorites. | +| 1.7.0 | DELETE_LIBRARY | string | Delete a Library by id. Takes selected node if payload not provided. | +| 1.7.0 | CREATE_LIBRARY | n/a | Invoke a "Create Library" dialog. | +| 1.7.0 | SET_SELECTED_NODES | MinimalNodeEntity[] | Notify components about selected nodes. | +| 1.7.0 | DELETE_NODES | MinimalNodeEntity[] | Delete the nodes (or selection). Supports undo actions. | +| 1.7.0 | UNDO_DELETE_NODES | any[] | Reverts deletion of nodes (or selection). | +| 1.7.0 | RESTORE_DELETED_NODES | MinimalNodeEntity[] | Restores deleted nodes (or selection). Typically used with Trashcan. | +| 1.7.0 | PURGE_DELETED_NODES | MinimalNodeEntity[] | Permanently delete nodes (or selection). Typically used with Trashcan. | +| 1.7.0 | DOWNLOAD_NODES | MinimalNodeEntity[] | Download nodes (or selections). Creates a ZIP archive for folders or multiple items. | +| 1.7.0 | CREATE_FOLDER | string | Invoke a "Create Folder" dialog for the opened folder (or the parent folder id in the payload). | +| 1.7.0 | EDIT_FOLDER | MinimalNodeEntity | Invoke an "Edit Folder" dialog for the node (or selection). | +| 1.7.0 | SHARE_NODE | MinimalNodeEntity | Invoke a "Share" dialog for the node (or selection). | +| 1.7.0 | UNSHARE_NODES | MinimalNodeEntity[] | Remove nodes (or selection) from the shared nodes (does not remove content). | +| 1.7.0 | COPY_NODES | MinimalNodeEntity[] | Invoke a "Copy" dialog for the nodes (or selection). Supports undo actions. | +| 1.7.0 | MOVE_NODES | MinimalNodeEntity[] | Invoke a "Move" dialog for the nodes (or selection). Supports undo actions. | +| 1.7.0 | MANAGE_PERMISSIONS | MinimalNodeEntity | Invoke a "Manage Permissions" dialog for the node (or selection). | +| 1.7.0 | MANAGE_VERSIONS | MinimalNodeEntity | Invoke a "Manage Versions" dialog for the node (or selection). | +| 1.7.0 | NAVIGATE_URL | string | Navigate to a given route URL within the application. | +| 1.7.0 | NAVIGATE_ROUTE | any[] | Navigate to a particular Route (supports parameters). | +| 1.7.0 | NAVIGATE_FOLDER | MinimalNodeEntity | Navigate to a folder based on the Node properties. | +| 1.7.0 | NAVIGATE_PARENT_FOLDER | MinimalNodeEntity | Navigate to a containing folder based on the Node properties. | +| 1.7.0 | NAVIGATE_LIBRARY | string | Navigate to library. | +| 1.7.0 | SEARCH_BY_TERM | string | Perform a simple search by the term and navigate to Search results. | +| 1.7.0 | SNACKBAR_INFO | string | Show information snackbar with the message provided. | +| 1.7.0 | SNACKBAR_WARNING | string | Show warning snackbar with the message provided. | +| 1.7.0 | SNACKBAR_ERROR | string | Show error snackbar with the message provided. | +| 1.7.0 | UPLOAD_FILES | n/a | Invoke "Upload Files" dialog and upload files to the currently opened folder. | +| 1.7.0 | UPLOAD_FOLDER | n/a | Invoke "Upload Folder" dialog and upload selected folder to the currently opened one. | +| 1.7.0 | UPLOAD_FILE_VERSION | n/a | Invoke "New File Version" dialog. | +| 1.7.0 | VIEW_FILE | MinimalNodeEntity | Preview the file (or selection) in the Viewer. | +| 1.8.0 | VIEW_NODE | string | Lightweight preview of a node by id. Can be invoked from extensions. | +| 1.7.0 | UNLOCK_WRITE | NodeEntry | Unlock file from read only mode | +| 1.7.0 | PRINT_FILE | MinimalNodeEntity | Print the file opened in the Viewer (or selected). | +| 1.7.0 | FULLSCREEN_VIEWER | n/a | Enters fullscreen mode to view the file opened in the Viewer. | +| 1.7.0 | LOGOUT | n/a | Log out and redirect to Login screen. | +| 1.7.0 | RELOAD_DOCUMENT_LIST | n/a | Reload active document list | +| 1.7.0 | TOGGLE_SEARCH_FILTER | n/a | Toggle Filter component visibility in Search Results. | +| 1.7.0 | SHOW_SEARCH_FILTER | n/a | Show Filter component in Search Results. | +| 1.7.0 | HIDE_SEARCH_FILTER | n/a | Hide Filter component in Search Results | diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index bf59dfb77..0fa7814d4 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -52,6 +52,22 @@ export const APP_ROUTES: Routes = [ loadChildren: './components/shared-link-view/shared-link-view.module#AppSharedLinkViewModule' }, + { + path: 'view', + component: AppLayoutComponent, + children: [ + { + path: ':nodeId', + outlet: 'viewer', + children: [ + { + path: '', + loadChildren: './components/viewer/viewer.module#AppViewerModule' + } + ] + } + ] + }, { path: '', component: AppLayoutComponent, @@ -149,6 +165,18 @@ export const APP_ROUTES: Routes = [ navigateSource: 'personal-files' } } + // Do not remove, will be enabled in future iterations + // { + // path: 'view/:nodeId', + // outlet: 'viewer', + // children: [ + // { + // path: '', + // loadChildren: + // './components/viewer/viewer.module#AppViewerModule' + // } + // ] + // } ] }, { diff --git a/src/app/components/layout/app-layout/app-layout.component.html b/src/app/components/layout/app-layout/app-layout.component.html index 6348de370..d206527e8 100644 --- a/src/app/components/layout/app-layout/app-layout.component.html +++ b/src/app/components/layout/app-layout/app-layout.component.html @@ -39,3 +39,5 @@ + + diff --git a/src/app/components/layout/app-layout/app-layout.theme.scss b/src/app/components/layout/app-layout/app-layout.theme.scss index db0104dc7..6313742fd 100644 --- a/src/app/components/layout/app-layout/app-layout.theme.scss +++ b/src/app/components/layout/app-layout/app-layout.theme.scss @@ -1,6 +1,16 @@ @mixin app-layout-theme($theme) { .app-layout { @include flex-column; + + router-outlet[name='viewer'] + * { + width: 100%; + height: 100%; + z-index: 999; + position: absolute; + top: 0; + right: 0; + background-color: white; + } } @media screen and (max-width: 599px) { diff --git a/src/app/components/preview/preview-extension.component.ts b/src/app/components/preview/preview-extension.component.ts deleted file mode 100644 index a2c9b3b88..000000000 --- a/src/app/components/preview/preview-extension.component.ts +++ /dev/null @@ -1,107 +0,0 @@ -/*! - * @license - * Alfresco Example Content Application - * - * Copyright (C) 2005 - 2019 Alfresco Software Limited - * - * This file is part of the Alfresco Example Content Application. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * The Alfresco Example Content Application is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * The Alfresco Example Content Application is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ - -import { - Component, - Input, - ComponentRef, - OnInit, - ComponentFactoryResolver, - ViewChild, - ViewContainerRef, - OnDestroy, - OnChanges -} from '@angular/core'; -import { AppExtensionService } from '../../extensions/extension.service'; -import { MinimalNodeEntryEntity } from '@alfresco/js-api'; - -@Component({ - selector: 'app-preview-extension', - template: ` -
- ` -}) -export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy { - @ViewChild('content', { read: ViewContainerRef }) - content: ViewContainerRef; - - @Input() - id: string; - - @Input() - url: string; - - @Input() - extension: string; - - @Input() - node: MinimalNodeEntryEntity; - - private componentRef: ComponentRef; - - constructor( - private extensions: AppExtensionService, - private componentFactoryResolver: ComponentFactoryResolver - ) {} - - ngOnInit() { - if (!this.id) { - return; - } - - const componentType = this.extensions.getComponentById(this.id); - if (componentType) { - const factory = this.componentFactoryResolver.resolveComponentFactory( - componentType - ); - if (factory) { - this.content.clear(); - this.componentRef = this.content.createComponent(factory, 0); - this.updateInstance(); - } - } - } - - ngOnChanges() { - this.updateInstance(); - } - - ngOnDestroy() { - if (this.componentRef) { - this.componentRef.destroy(); - this.componentRef = null; - } - } - - private updateInstance() { - if (this.componentRef && this.componentRef.instance) { - const instance = this.componentRef.instance; - - instance.node = this.node; - instance.url = this.url; - instance.extension = this.extension; - } - } -} diff --git a/src/app/components/preview/preview.module.ts b/src/app/components/preview/preview.module.ts index be00e38b2..de14caae6 100644 --- a/src/app/components/preview/preview.module.ts +++ b/src/app/components/preview/preview.module.ts @@ -32,7 +32,6 @@ import { CoreExtensionsModule } from '../../extensions/core.extensions.module'; import { DirectivesModule } from '../../directives/directives.module'; import { AppInfoDrawerModule } from '../info-drawer/info.drawer.module'; import { PreviewComponent } from './preview.component'; -import { PreviewExtensionComponent } from './preview-extension.component'; import { AppToolbarModule } from '../toolbar/toolbar.module'; const routes: Routes = [ @@ -57,7 +56,7 @@ const routes: Routes = [ CoreExtensionsModule.forChild(), AppToolbarModule ], - declarations: [PreviewComponent, PreviewExtensionComponent], + declarations: [PreviewComponent], exports: [PreviewComponent] }) export class PreviewModule {} diff --git a/src/app/components/viewer/viewer.component.html b/src/app/components/viewer/viewer.component.html new file mode 100644 index 000000000..79f1f914f --- /dev/null +++ b/src/app/components/viewer/viewer.component.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/app/components/viewer/viewer.component.scss b/src/app/components/viewer/viewer.component.scss new file mode 100644 index 000000000..0b8d31cbc --- /dev/null +++ b/src/app/components/viewer/viewer.component.scss @@ -0,0 +1,23 @@ +.app-viewer { + width: 100%; + height: 100%; + + .adf-viewer-toolbar .adf-toolbar-divider { + display: none; + } + + .adf-viewer-toolbar-actions { + display: flex; + flex-direction: row; + align-items: center; + + .adf-toolbar-divider { + display: inline; + } + } + + // todo: remove this when viewer supports extensions + .adf-viewer-toolbar .mat-toolbar > button:last-child { + display: none; + } +} diff --git a/src/app/components/viewer/viewer.component.ts b/src/app/components/viewer/viewer.component.ts new file mode 100644 index 000000000..c50d28cee --- /dev/null +++ b/src/app/components/viewer/viewer.component.ts @@ -0,0 +1,121 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ContentActionRef, SelectionState } from '@alfresco/adf-extensions'; +import { Store } from '@ngrx/store'; +import { AppStore } from '../../store/states'; +import { + appSelection, + infoDrawerOpened +} from '../../store/selectors/app.selectors'; +import { takeUntil } from 'rxjs/operators'; +import { Subject, Observable, from } from 'rxjs'; +import { AppExtensionService } from '../../extensions/extension.service'; +import { MinimalNodeEntryEntity } from '@alfresco/js-api'; +import { ContentApiService } from '../../services/content-api.service'; +import { SetSelectedNodesAction } from '../../store/actions'; + +@Component({ + selector: 'app-viewer', + templateUrl: './viewer.component.html', + styleUrls: ['./viewer.component.scss'], + encapsulation: ViewEncapsulation.None, + host: { class: 'app-viewer' } +}) +export class AppViewerComponent implements OnInit, OnDestroy { + onDestroy$ = new Subject(); + + nodeId: string = null; + node: MinimalNodeEntryEntity; + selection: SelectionState; + infoDrawerOpened$: Observable; + + showRightSide = false; + openWith: ContentActionRef[] = []; + toolbarActions: ContentActionRef[] = []; + + constructor( + private route: ActivatedRoute, + private store: Store, + protected extensions: AppExtensionService, + private contentApi: ContentApiService + ) {} + + ngOnInit() { + this.infoDrawerOpened$ = this.store.select(infoDrawerOpened); + + from(this.infoDrawerOpened$) + .pipe(takeUntil(this.onDestroy$)) + .subscribe(val => { + this.showRightSide = val; + }); + + this.store + .select(appSelection) + .pipe(takeUntil(this.onDestroy$)) + .subscribe(selection => { + this.selection = selection; + + this.toolbarActions = this.extensions.getViewerToolbarActions(); + this.openWith = this.extensions.openWithActions; + }); + + this.route.params.subscribe(params => { + const { nodeId } = params; + if (nodeId) { + this.displayNode(nodeId); + } + }); + } + + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } + + trackById(_: number, obj: { id: string }) { + return obj.id; + } + + async displayNode(id: string) { + if (id) { + try { + this.node = await this.contentApi.getNodeInfo(id).toPromise(); + this.store.dispatch(new SetSelectedNodesAction([{ entry: this.node }])); + + if (this.node && this.node.isFile) { + this.nodeId = this.node.id; + return; + } + } catch (err) { + if (!err || err.status !== 401) { + // this.router.navigate([this.previewLocation, id]); + } + } + } + } +} diff --git a/src/app/components/preview/preview-extension.component.spec.ts b/src/app/components/viewer/viewer.module.ts similarity index 51% rename from src/app/components/preview/preview-extension.component.spec.ts rename to src/app/components/viewer/viewer.module.ts index 217b28359..49e7ea1b0 100644 --- a/src/app/components/preview/preview-extension.component.spec.ts +++ b/src/app/components/viewer/viewer.module.ts @@ -23,10 +23,36 @@ * along with Alfresco. If not, see . */ -import { PreviewExtensionComponent } from './preview-extension.component'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule, Routes } from '@angular/router'; +import { CoreModule } from '@alfresco/adf-core'; +import { ContentDirectiveModule } from '@alfresco/adf-content-services'; +import { DirectivesModule } from '../../directives/directives.module'; +import { AppInfoDrawerModule } from '../info-drawer/info.drawer.module'; +import { CoreExtensionsModule } from '../../extensions/core.extensions.module'; +import { AppToolbarModule } from '../toolbar/toolbar.module'; +import { AppViewerComponent } from './viewer.component'; -describe('PreviewExtensionComponent', () => { - it('should be defined', () => { - expect(PreviewExtensionComponent).toBeDefined(); - }); -}); +const routes: Routes = [ + { + path: '', + component: AppViewerComponent + } +]; + +@NgModule({ + imports: [ + CommonModule, + RouterModule.forChild(routes), + CoreModule.forChild(), + ContentDirectiveModule, + DirectivesModule, + AppInfoDrawerModule, + CoreExtensionsModule.forChild(), + AppToolbarModule + ], + declarations: [AppViewerComponent], + exports: [AppViewerComponent] +}) +export class AppViewerModule {} diff --git a/src/app/extensions/evaluators/navigation.evaluators.ts b/src/app/extensions/evaluators/navigation.evaluators.ts index 0e601cf18..e08db1cd0 100644 --- a/src/app/extensions/evaluators/navigation.evaluators.ts +++ b/src/app/extensions/evaluators/navigation.evaluators.ts @@ -31,7 +31,7 @@ import { RuleContext } from '@alfresco/adf-extensions'; */ export function isPreview(context: RuleContext): boolean { const { url } = context.navigation; - return url && url.includes('/preview/'); + return url && (url.includes('/preview/') || url.includes('/view/')); } /** diff --git a/src/app/store/actions/viewer.actions.ts b/src/app/store/actions/viewer.actions.ts index 75bbb257c..dea6c81e3 100644 --- a/src/app/store/actions/viewer.actions.ts +++ b/src/app/store/actions/viewer.actions.ts @@ -27,8 +27,14 @@ import { Action } from '@ngrx/store'; import { MinimalNodeEntity } from '@alfresco/js-api'; export const VIEW_FILE = 'VIEW_FILE'; +export const VIEW_NODE = 'VIEW_NODE'; export class ViewFileAction implements Action { readonly type = VIEW_FILE; constructor(public payload: MinimalNodeEntity, public parentId?: string) {} } + +export class ViewNodeAction implements Action { + readonly type = VIEW_NODE; + constructor(public nodeId: string, public location?: string) {} +} diff --git a/src/app/store/effects/viewer.effects.ts b/src/app/store/effects/viewer.effects.ts index 8d639d1fc..fc7be8f10 100644 --- a/src/app/store/effects/viewer.effects.ts +++ b/src/app/store/effects/viewer.effects.ts @@ -31,6 +31,7 @@ import { Router } from '@angular/router'; import { Store, createSelector } from '@ngrx/store'; import { AppStore } from '../states'; import { appSelection, currentFolder } from '../selectors/app.selectors'; +import { ViewNodeAction, VIEW_NODE } from '../actions/viewer.actions'; export const fileToPreview = createSelector( appSelection, @@ -51,6 +52,28 @@ export class ViewerEffects { private router: Router ) {} + @Effect({ dispatch: false }) + viewNode$ = this.actions$.pipe( + ofType(VIEW_NODE), + map(action => { + if (action.location) { + this.router.navigate( + [action.location, { outlets: { viewer: ['view', action.nodeId] } }], + { + queryParams: { + source: action.location + } + } + ); + } else { + this.router.navigate([ + 'view', + { outlets: { viewer: [action.nodeId] } } + ]); + } + }) + ); + @Effect({ dispatch: false }) viewFile$ = this.actions$.pipe( ofType(VIEW_FILE),