[ACA-2745] Viewer - return to location on close (#1196)

* use viewNodeExtras when calling action

* open viewer based on ViewNodeExtras data

* resolve closing destination based on ViewNodeExtras query params

* remove unused param

* call ViewNodeAction with correct params

* update tests

* update docs
This commit is contained in:
Cilibiu Bogdan 2019-09-06 14:17:29 +03:00 committed by GitHub
parent cbe9cf4690
commit edf1e52e94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 186 additions and 82 deletions

View File

@ -76,51 +76,51 @@ 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:
| 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.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 |
| 1.8.0 | VIEW_NODE | string | Lightweight preview of a node by id. Can be invoked from extensions. |
| 1.8.0 | CLOSE_PREVIEW | n/a | Closes the viewer ( preview of the item ) |
| 1.9.0 | RESET_SELECTION | n/a | Resets active document list selection |
| 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.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 |
| 1.8.0 | VIEW_NODE | NodeId<`string`> , [ViewNodeExtras](../features/file-viewer.md#details)<`any`> | Lightweight preview of a node by id. Can be invoked from extensions. For details also see [File Viewer](../features/file-viewer.md#details) |
| 1.8.0 | CLOSE_PREVIEW | n/a | Closes the viewer ( preview of the item ) |
| 1.9.0 | RESET_SELECTION | n/a | Resets active document list selection |

View File

@ -59,3 +59,66 @@ At the bottom of the content the Viewer Controls allow users to interact with th
- Toggle audio
- Audio volume
- Full screen
## Details
The viewer can be invoked by calling [ViewNodeAction](../extending/application-actions). This method supports a second parameter `ViewNodeExtras` which affects the behaviour on opening and closing hte viewer as detailed bellow.
```typescript
// ViewNodeExtras
export interface ViewNodeExtras {
location?: string;
path?: string;
}
```
```typescript
// app.routes.ts
...
{
path: 'custom-path',
children: [
{
path: '',
component: CustomComponent
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
loadChildren: './components/viewer/viewer.module#AppViewerModule'
}
]
}
]
}
...
```
```typescript
// custom-component.component.ts
import { ViewNodeAction } from '@alfresco/aca-shared/store';
import { Router } from '@angular/router';
@Component({...})
export class CustomComponent {
constructor(private store: Store<AppStore>, private router: Router)
viewNode(nodeId: string) {
this.store.dispatch(new ViewNodeAction(nodeId, { location: this.router.url }));
}
}
```
In the above example, passing `location` when calling `ViewNodeAction` will open the viewer relative to current activated route _/custom-path/(viewer:view/nodeId)_.
If no location is passed, the viewer will default to open on a [predefined route](https://github.com/Alfresco/alfresco-content-app/blob/development/src/app/app.routes.ts#L58). In this case, in order to get back to original location from where the viewer was invoked, `ViewNodeAction` should be called by passing a `path`.
```typescript
// when invoked from an extension
this.store.dispatch(new ViewNodeAction(nodeId, { path: this.router.url }));
```

View File

@ -33,6 +33,11 @@ export enum ViewerActionTypes {
ClosePreview = 'CLOSE_PREVIEW'
}
export interface ViewNodeExtras {
location?: string;
path?: string;
}
export class ViewFileAction implements Action {
readonly type = ViewerActionTypes.ViewFile;
@ -42,7 +47,7 @@ export class ViewFileAction implements Action {
export class ViewNodeAction implements Action {
readonly type = ViewerActionTypes.ViewNode;
constructor(public nodeId: string, public location?: string) {}
constructor(public nodeId: string, public viewNodeExtras?: ViewNodeExtras) {}
}
export class FullscreenViewerAction implements Action {

View File

@ -185,9 +185,8 @@ describe('FavoritesComponent', () => {
fixture.detectChanges();
component.onNodeDoubleClick(nodeEntity);
expect(component.showPreview).toHaveBeenCalledWith(
nodeEntity,
mockRouter.url
);
expect(component.showPreview).toHaveBeenCalledWith(nodeEntity, {
location: mockRouter.url
});
});
});

View File

@ -112,7 +112,7 @@ export class FavoritesComponent extends PageComponent implements OnInit {
}
if (node.entry.isFile) {
this.showPreview(node, this.router.url);
this.showPreview(node, { location: this.router.url });
}
}
}

View File

@ -151,7 +151,7 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy {
return;
}
this.showPreview(node, this.router.url);
this.showPreview(node, { location: this.router.url });
}
}

View File

@ -43,7 +43,8 @@ import {
getDocumentDisplayMode,
isInfoDrawerOpened,
getSharedUrl,
ViewNodeAction
ViewNodeAction,
ViewNodeExtras
} from '@alfresco/aca-shared/store';
import { isLocked, isLibrary } from '../utils/node.utils';
@ -105,12 +106,12 @@ export abstract class PageComponent implements OnInit, OnDestroy {
this.onDestroy$.complete();
}
showPreview(node: MinimalNodeEntity, location?: string) {
showPreview(node: MinimalNodeEntity, extras?: ViewNodeExtras) {
if (node && node.entry) {
const id =
(<any>node).entry.nodeId || (<any>node).entry.guid || node.entry.id;
this.store.dispatch(new ViewNodeAction(id, location));
this.store.dispatch(new ViewNodeAction(id, extras));
}
}

View File

@ -124,6 +124,8 @@ describe('RecentFilesComponent', () => {
fixture.detectChanges();
component.onNodeDoubleClick(node);
expect(component.showPreview).toHaveBeenCalledWith(node, mockRouter.url);
expect(component.showPreview).toHaveBeenCalledWith(node, {
location: mockRouter.url
});
});
});

View File

@ -77,7 +77,7 @@ export class RecentFilesComponent extends PageComponent implements OnInit {
onNodeDoubleClick(node: MinimalNodeEntity) {
if (node && node.entry) {
this.showPreview(node, this.router.url);
this.showPreview(node, { location: this.router.url });
}
}

View File

@ -126,7 +126,7 @@ export class SearchResultsRowComponent implements OnInit, OnDestroy {
showPreview(event: MouseEvent) {
event.stopPropagation();
this.store.dispatch(
new ViewNodeAction(this.node.entry.id, this.router.url)
new ViewNodeAction(this.node.entry.id, { location: this.router.url })
);
}

View File

@ -305,7 +305,9 @@ describe('SearchComponent', () => {
component.onNodeDoubleClick(node);
expect(component.showPreview).toHaveBeenCalledWith(node, router.url);
expect(component.showPreview).toHaveBeenCalledWith(node, {
location: router.url
});
});
it('should re-run search on pagination change', () => {

View File

@ -253,7 +253,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
return;
}
this.showPreview(node, this.router.url);
this.showPreview(node, { location: this.router.url });
}
}

View File

@ -131,6 +131,8 @@ describe('SharedFilesComponent', () => {
fixture.detectChanges();
component.preview(node);
expect(component.showPreview).toHaveBeenCalledWith(node, mockRouter.url);
expect(component.showPreview).toHaveBeenCalledWith(node, {
location: mockRouter.url
});
});
});

View File

@ -79,6 +79,6 @@ export class SharedFilesComponent extends PageComponent implements OnInit {
}
preview(node: MinimalNodeEntity) {
this.showPreview(node, this.router.url);
this.showPreview(node, { location: this.router.url });
}
}

View File

@ -69,7 +69,9 @@ export class ViewNodeComponent {
(<any>selection.file).entry.guid ||
selection.file.entry.id;
this.store.dispatch(new ViewNodeAction(id, this.router.url));
this.store.dispatch(
new ViewNodeAction(id, { location: this.router.url })
);
});
}
}

View File

@ -11,7 +11,7 @@
[allowDownload]="false"
[allowFullScreen]="false"
[overlayMode]="true"
(showViewerChange)="onViewerVisibilityChanged($event)"
(showViewerChange)="onViewerVisibilityChanged()"
[canNavigateBefore]="previousNodeId"
[canNavigateNext]="nextNodeId"
(navigateBefore)="onNavigateBefore()"

View File

@ -59,6 +59,8 @@ import { ReloadDocumentListAction } from '@alfresco/aca-shared/store';
host: { class: 'app-viewer' }
})
export class AppViewerComponent implements OnInit, OnDestroy {
private navigationPath: string;
onDestroy$ = new Subject<boolean>();
folderId: string = null;
@ -149,6 +151,10 @@ export class AppViewerComponent implements OnInit, OnDestroy {
}
});
this.route.queryParams.subscribe(params => {
this.navigationPath = params.path;
});
if (this.route.snapshot.data && this.route.snapshot.data.navigateSource) {
const source = this.route.snapshot.data.navigateSource.toLowerCase();
if (this.navigationSources.includes(source)) {
@ -231,12 +237,12 @@ export class AppViewerComponent implements OnInit, OnDestroy {
onNavigateBefore(): void {
const location = this.getFileLocation();
this.store.dispatch(new ViewNodeAction(this.previousNodeId, location));
this.store.dispatch(new ViewNodeAction(this.previousNodeId, { location }));
}
onNavigateNext(): void {
const location = this.getFileLocation();
this.store.dispatch(new ViewNodeAction(this.nextNodeId, location));
this.store.dispatch(new ViewNodeAction(this.nextNodeId, { location }));
}
/**
@ -425,7 +431,7 @@ export class AppViewerComponent implements OnInit, OnDestroy {
private getFileLocation(): string {
return this.router
.parseUrl(this.router.url)
.parseUrl(this.navigationPath || this.router.url)
.root.children[PRIMARY_OUTLET].toString();
}
}

View File

@ -74,12 +74,14 @@ describe('ViewerEffects', () => {
});
describe('ViewNode', () => {
it('should open viewer from file location', fakeAsync(() => {
store.dispatch(new ViewNodeAction('nodeId', 'some-location'));
it('should open viewer from file location if', fakeAsync(() => {
store.dispatch(
new ViewNodeAction('nodeId', { location: 'some-location' })
);
tick(100);
expect(router.navigateByUrl['calls'].argsFor(0)[0].toString()).toEqual(
'/some-location/(viewer:view/nodeId)?source=some-location'
'/some-location/(viewer:view/nodeId)?location=some-location'
);
}));
@ -91,5 +93,14 @@ describe('ViewerEffects', () => {
'/view/(viewer:nodeId)'
);
}));
it('should navigate to viewer route with query param if path is passed', fakeAsync(() => {
store.dispatch(new ViewNodeAction('nodeId', { path: 'absolute-path' }));
tick(100);
expect(router.navigateByUrl['calls'].argsFor(0)[0].toString()).toEqual(
'/view/(viewer:nodeId)?path=absolute-path'
);
}));
});
});

View File

@ -77,17 +77,28 @@ export class ViewerEffects {
viewNode$ = this.actions$.pipe(
ofType<ViewNodeAction>(ViewerActionTypes.ViewNode),
map(action => {
if (action.location) {
const location = this.getNavigationCommands(action.location);
if (action.viewNodeExtras) {
const { location, path } = action.viewNodeExtras;
this.router.navigate(
[...location, { outlets: { viewer: ['view', action.nodeId] } }],
{
queryParams: {
source: action.location
if (location) {
const navigation = this.getNavigationCommands(location);
this.router.navigate(
[...navigation, { outlets: { viewer: ['view', action.nodeId] } }],
{
queryParams: { location }
}
}
);
);
}
if (path) {
this.router.navigate(
['view', { outlets: { viewer: [action.nodeId] } }],
{
queryParams: { path }
}
);
}
} else {
this.router.navigate([
'view',