mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-09-17 14:21:14 +00:00
[ACA-2087] Overlay Viewer (#1175)
* viewer outlet over preview route * use ViewNodeAction over ViewFileAction * pass data to dynamic component * ViewNodeComponent for view file custom actions * update docs * pass primary url to show preview outlet * update tests * reset selection on navigation event * document list update selection action when not viewer * close viewer for move and delete from viewer * location as router commands to work with search query * make viewer to behave like former preview * viewer error route * call correct preview method * remove view/error route * navigate to show error * span element for action name * fix folder navigation * fix test * page title fix * update tests * locate better the viewer toolbar * fix viewer url link * update navigation rules * document-list directive tests * try workaround for chrome 76 * try another workaround for using chromedriver 75 instead of 76 * ViewerEffects tests * reset selection over reload * fix tests * add reset event test * remove actions * context menu action refresh on favourite * reset selection on navigation * add delete and upload events * takeUntil after operators * remove chrome workaround parameter * filter navigation event
This commit is contained in:
committed by
Suzana Dirla
parent
8643a8806d
commit
e31c0d6caf
@@ -123,3 +123,4 @@ Below is the list of public actions types you can use in the plugin definitions
|
|||||||
| 1.7.0 | HIDE_SEARCH_FILTER | n/a | Hide 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 | 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.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 |
|
||||||
|
@@ -18,7 +18,8 @@ The components are used to create custom:
|
|||||||
| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. |
|
| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. |
|
||||||
| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. |
|
| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. |
|
||||||
| app.toolbar.toggleFavoriteLibrary | ToggleFavoriteLibraryComponent | The toolbar button component that toggles Favorite library state for the selection. |
|
| app.toolbar.toggleFavoriteLibrary | ToggleFavoriteLibraryComponent | The toolbar button component that toggles Favorite library state for the selection. |
|
||||||
| app.toolbar.toggleJoinLibrary | ToggleJoinLibraryComponent | The toolbar button component that toggles Join/Cancel Join request for the selected library. |
|
| app.toolbar.toggleJoinLibrary | ToggleJoinLibraryComponent | The toolbar button component that toggles Join/Cancel Join request for the selected library |
|
||||||
|
| app.toolbar.viewNode | ViewNodeComponent | Action component to view files |
|
||||||
|
|
||||||
See [Registration](/extending/registration) section for more details
|
See [Registration](/extending/registration) section for more details
|
||||||
on how to register your own entries to be re-used at runtime.
|
on how to register your own entries to be re-used at runtime.
|
||||||
|
@@ -31,6 +31,7 @@ const page = new BrowsingPage();
|
|||||||
const { dataTable, toolbar } = page;
|
const { dataTable, toolbar } = page;
|
||||||
const contextMenu = dataTable.menu;
|
const contextMenu = dataTable.menu;
|
||||||
const viewer = new Viewer();
|
const viewer = new Viewer();
|
||||||
|
const viewerToolbar = viewer.toolbar;
|
||||||
|
|
||||||
|
|
||||||
export async function checkContextMenu(item: string, expectedContextMenu: string[]) {
|
export async function checkContextMenu(item: string, expectedContextMenu: string[]) {
|
||||||
@@ -93,7 +94,7 @@ export async function checkViewerToolbarPrimaryActions(item: string, expectedToo
|
|||||||
await dataTable.doubleClickOnRowByName(item);
|
await dataTable.doubleClickOnRowByName(item);
|
||||||
await viewer.waitForViewerToOpen();
|
await viewer.waitForViewerToOpen();
|
||||||
|
|
||||||
let actualPrimaryActions = await toolbar.getButtons();
|
let actualPrimaryActions = await viewerToolbar.getButtons();
|
||||||
|
|
||||||
actualPrimaryActions = removeClosePreviousNextOldInfo(actualPrimaryActions);
|
actualPrimaryActions = removeClosePreviousNextOldInfo(actualPrimaryActions);
|
||||||
|
|
||||||
@@ -106,9 +107,9 @@ export async function checkViewerToolbarPrimaryActions(item: string, expectedToo
|
|||||||
export async function checkViewerToolbarMoreActions(item: string, expectedToolbarMore: string[]) {
|
export async function checkViewerToolbarMoreActions(item: string, expectedToolbarMore: string[]) {
|
||||||
await dataTable.doubleClickOnRowByName(item);
|
await dataTable.doubleClickOnRowByName(item);
|
||||||
await viewer.waitForViewerToOpen();
|
await viewer.waitForViewerToOpen();
|
||||||
await toolbar.openMoreMenu();
|
await viewerToolbar.openMoreMenu();
|
||||||
|
|
||||||
const actualMoreActions = await toolbar.menu.getMenuItems();
|
const actualMoreActions = await viewerToolbar.menu.getMenuItems();
|
||||||
|
|
||||||
expect(actualMoreActions.length).toBe(expectedToolbarMore.length, 'Incorrect number of toolbar More menu items');
|
expect(actualMoreActions.length).toBe(expectedToolbarMore.length, 'Incorrect number of toolbar More menu items');
|
||||||
expect(JSON.stringify(actualMoreActions)).toEqual(JSON.stringify(expectedToolbarMore), 'Incorrect toolbar More actions');
|
expect(JSON.stringify(actualMoreActions)).toEqual(JSON.stringify(expectedToolbarMore), 'Incorrect toolbar More actions');
|
||||||
|
@@ -128,14 +128,14 @@ describe('Viewer general', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Viewer opens when accessing the preview URL for a file - [C279285]', async () => {
|
it('Viewer opens when accessing the preview URL for a file - [C279285]', async () => {
|
||||||
const previewURL = `personal-files/${parentId}/preview/${xlsxFileId}`;
|
const previewURL = `personal-files/${parentId}/(viewer:view/${xlsxFileId})`
|
||||||
await page.load(previewURL);
|
await page.load(previewURL);
|
||||||
expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened');
|
expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened');
|
||||||
expect(await viewer.getFileTitle()).toEqual(xlsxFile);
|
expect(await viewer.getFileTitle()).toEqual(xlsxFile);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Viewer does not open when accessing the preview URL for a file without permissions - [C279287]', async () => {
|
it('Viewer does not open when accessing the preview URL for a file without permissions - [C279287]', async () => {
|
||||||
const previewURL = `libraries/${docLibId}/preview/${fileAdminId}`;
|
const previewURL = `libraries/${docLibId}/(viewer:view/${fileAdminId})`
|
||||||
await page.load(previewURL);
|
await page.load(previewURL);
|
||||||
expect(await viewer.isViewerOpened()).toBe(false, 'Viewer should not be opened!');
|
expect(await viewer.isViewerOpened()).toBe(false, 'Viewer should not be opened!');
|
||||||
});
|
});
|
||||||
|
@@ -27,10 +27,10 @@ import * as app from './navigation.rules';
|
|||||||
|
|
||||||
describe('navigation.evaluators', () => {
|
describe('navigation.evaluators', () => {
|
||||||
describe('isPreview', () => {
|
describe('isPreview', () => {
|
||||||
it('should return [true] if url contains `/preview/`', () => {
|
it('should return [true] if url contains `viewer:view`', () => {
|
||||||
const context: any = {
|
const context: any = {
|
||||||
navigation: {
|
navigation: {
|
||||||
url: 'path/preview/id'
|
url: 'path/(viewer:view/id)'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -271,20 +271,30 @@ describe('navigation.evaluators', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('isSharedPreview', () => {
|
describe('isSharedPreview', () => {
|
||||||
it('should return [true] if url starts with `/shared/preview/`', () => {
|
it('should return [true] if url starts with `/shared` and contains `viewer:view', () => {
|
||||||
const context: any = {
|
const context: any = {
|
||||||
navigation: {
|
navigation: {
|
||||||
url: '/shared/preview/path'
|
url: '/shared/(viewer:view)'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(app.isSharedPreview(context)).toBe(true);
|
expect(app.isSharedPreview(context)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return [false] if url does not start with `/shared/preview/`', () => {
|
it('should return [false] if url does not start with `/shared`', () => {
|
||||||
const context: any = {
|
const context: any = {
|
||||||
navigation: {
|
navigation: {
|
||||||
url: '/path/shared/preview/'
|
url: '/path/shared/(viewer:view)'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(app.isSharedPreview(context)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return [false] if url starts with `/shared` and does not includes `viewer:view`', () => {
|
||||||
|
const context: any = {
|
||||||
|
navigation: {
|
||||||
|
url: '/shared/something'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -293,20 +303,30 @@ describe('navigation.evaluators', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('isFavoritesPreview', () => {
|
describe('isFavoritesPreview', () => {
|
||||||
it('should return [true] if url starts with `/favorites/preview/`', () => {
|
it('should return [true] if url starts with `/favorites` and includes `viewer:view`', () => {
|
||||||
const context: any = {
|
const context: any = {
|
||||||
navigation: {
|
navigation: {
|
||||||
url: '/favorites/preview/path'
|
url: '/favorites/(viewer:view)'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(app.isFavoritesPreview(context)).toBe(true);
|
expect(app.isFavoritesPreview(context)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return [false] if url does not start with `/favorites/preview/`', () => {
|
it('should return [false] if url does not start with `/favorites`', () => {
|
||||||
const context: any = {
|
const context: any = {
|
||||||
navigation: {
|
navigation: {
|
||||||
url: '/path/favorites/preview/'
|
url: '/path/favorites/(viewer:view)'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(app.isFavoritesPreview(context)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return [false] if url starts with `/favorites` and does not include `viewer:view`', () => {
|
||||||
|
const context: any = {
|
||||||
|
navigation: {
|
||||||
|
url: '/favorites/other'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ import { RuleContext } from '@alfresco/adf-extensions';
|
|||||||
*/
|
*/
|
||||||
export function isPreview(context: RuleContext): boolean {
|
export function isPreview(context: RuleContext): boolean {
|
||||||
const { url } = context.navigation;
|
const { url } = context.navigation;
|
||||||
return url && (url.includes('/preview/') || url.includes('/view/'));
|
return url && (url.includes('viewer:view') || url.includes('/view/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,7 +165,7 @@ export function isNotSearchResults(context: RuleContext): boolean {
|
|||||||
*/
|
*/
|
||||||
export function isSharedPreview(context: RuleContext): boolean {
|
export function isSharedPreview(context: RuleContext): boolean {
|
||||||
const { url } = context.navigation;
|
const { url } = context.navigation;
|
||||||
return url && url.startsWith('/shared/preview/');
|
return url && url.startsWith('/shared') && url.includes('viewer:view');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,7 +174,7 @@ export function isSharedPreview(context: RuleContext): boolean {
|
|||||||
*/
|
*/
|
||||||
export function isFavoritesPreview(context: RuleContext): boolean {
|
export function isFavoritesPreview(context: RuleContext): boolean {
|
||||||
const { url } = context.navigation;
|
const { url } = context.navigation;
|
||||||
return url && url.startsWith('/favorites/preview/');
|
return url && url.startsWith('/favorites') && url.includes('viewer:view');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -38,6 +38,7 @@ export enum AppActionTypes {
|
|||||||
ToggleDocumentDisplayMode = 'TOGGLE_DOCUMENT_DISPLAY_MODE',
|
ToggleDocumentDisplayMode = 'TOGGLE_DOCUMENT_DISPLAY_MODE',
|
||||||
Logout = 'LOGOUT',
|
Logout = 'LOGOUT',
|
||||||
ReloadDocumentList = 'RELOAD_DOCUMENT_LIST',
|
ReloadDocumentList = 'RELOAD_DOCUMENT_LIST',
|
||||||
|
ResetSelection = 'RESET_SELECTION',
|
||||||
SetInfoDrawerState = 'SET_INFO_DRAWER_STATE',
|
SetInfoDrawerState = 'SET_INFO_DRAWER_STATE',
|
||||||
SetInfoDrawerMetadataAspect = 'SET_INFO_DRAWER_METADATA_ASPECT',
|
SetInfoDrawerMetadataAspect = 'SET_INFO_DRAWER_METADATA_ASPECT',
|
||||||
CloseModalDialogs = 'CLOSE_MODAL_DIALOGS'
|
CloseModalDialogs = 'CLOSE_MODAL_DIALOGS'
|
||||||
@@ -91,6 +92,12 @@ export class ReloadDocumentListAction implements Action {
|
|||||||
constructor(public payload?: any) {}
|
constructor(public payload?: any) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ResetSelectionAction implements Action {
|
||||||
|
readonly type = AppActionTypes.ResetSelection;
|
||||||
|
|
||||||
|
constructor(public payload?: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
export class SetInfoDrawerStateAction implements Action {
|
export class SetInfoDrawerStateAction implements Action {
|
||||||
readonly type = AppActionTypes.SetInfoDrawerState;
|
readonly type = AppActionTypes.SetInfoDrawerState;
|
||||||
|
|
||||||
|
@@ -36,7 +36,7 @@ export enum ViewerActionTypes {
|
|||||||
export class ViewFileAction implements Action {
|
export class ViewFileAction implements Action {
|
||||||
readonly type = ViewerActionTypes.ViewFile;
|
readonly type = ViewerActionTypes.ViewFile;
|
||||||
|
|
||||||
constructor(public payload: MinimalNodeEntity, public parentId?: string) {}
|
constructor(public payload?: MinimalNodeEntity, public parentId?: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ViewNodeAction implements Action {
|
export class ViewNodeAction implements Action {
|
||||||
|
@@ -33,7 +33,7 @@ import {
|
|||||||
SharedLinksApiService
|
SharedLinksApiService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
import { ActivatedRoute, Router, ActivationEnd } from '@angular/router';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppExtensionService } from './extensions/extension.service';
|
import { AppExtensionService } from './extensions/extension.service';
|
||||||
import {
|
import {
|
||||||
@@ -97,22 +97,21 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.loadAppSettings();
|
this.loadAppSettings();
|
||||||
|
|
||||||
const { router, pageTitle, route } = this;
|
const { router, pageTitle } = this;
|
||||||
|
|
||||||
router.events
|
this.router.events
|
||||||
.pipe(filter(event => event instanceof NavigationEnd))
|
.pipe(
|
||||||
.subscribe(() => {
|
filter(
|
||||||
let currentRoute = route.root;
|
event =>
|
||||||
|
event instanceof ActivationEnd &&
|
||||||
while (currentRoute.firstChild) {
|
event.snapshot.children.length === 0
|
||||||
currentRoute = currentRoute.firstChild;
|
)
|
||||||
}
|
)
|
||||||
|
.subscribe((event: ActivationEnd) => {
|
||||||
const snapshot: any = currentRoute.snapshot || {};
|
const snapshot: any = event.snapshot || {};
|
||||||
const data: any = snapshot.data || {};
|
const data: any = snapshot.data || {};
|
||||||
|
|
||||||
pageTitle.setTitle(data.title || '');
|
pageTitle.setTitle(data.title || '');
|
||||||
|
|
||||||
this.store.dispatch(new SetCurrentUrlAction(router.url));
|
this.store.dispatch(new SetCurrentUrlAction(router.url));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -80,19 +80,57 @@ export const APP_ROUTES: Routes = [
|
|||||||
pathMatch: 'full'
|
pathMatch: 'full'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'favorites',
|
path: 'personal-files',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
loadChildren:
|
component: FilesComponent,
|
||||||
'./components/favorites/favorites.module#AppFavoritesModule'
|
data: {
|
||||||
|
sortingPreferenceKey: 'personal-files',
|
||||||
|
title: 'APP.BROWSE.PERSONAL.TITLE',
|
||||||
|
defaultNodeId: '-my-'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'preview/:nodeId',
|
path: 'view/:nodeId',
|
||||||
loadChildren: './components/preview/preview.module#PreviewModule',
|
outlet: 'viewer',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
data: {
|
data: {
|
||||||
navigateSource: 'favorites'
|
navigateSource: 'personal-files'
|
||||||
|
},
|
||||||
|
loadChildren:
|
||||||
|
'./components/viewer/viewer.module#AppViewerModule'
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'personal-files/:folderId',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: FilesComponent,
|
||||||
|
data: {
|
||||||
|
title: 'APP.BROWSE.PERSONAL.TITLE',
|
||||||
|
sortingPreferenceKey: 'personal-files'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'view/:nodeId',
|
||||||
|
outlet: 'viewer',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
data: {
|
||||||
|
navigateSource: 'personal-files'
|
||||||
|
},
|
||||||
|
loadChildren:
|
||||||
|
'./components/viewer/viewer.module#AppViewerModule'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -106,9 +144,14 @@ export const APP_ROUTES: Routes = [
|
|||||||
title: 'APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE',
|
title: 'APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE',
|
||||||
sortingPreferenceKey: 'libraries'
|
sortingPreferenceKey: 'libraries'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':folderId',
|
path: 'libraries/:folderId',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
component: FilesComponent,
|
component: FilesComponent,
|
||||||
data: {
|
data: {
|
||||||
title: 'APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE',
|
title: 'APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE',
|
||||||
@@ -116,69 +159,59 @@ export const APP_ROUTES: Routes = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':folderId/preview/:nodeId',
|
path: 'view/:nodeId',
|
||||||
loadChildren: './components/preview/preview.module#PreviewModule',
|
outlet: 'viewer',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
data: {
|
data: {
|
||||||
navigateSource: 'libraries'
|
navigateSource: 'libraries'
|
||||||
|
},
|
||||||
|
loadChildren:
|
||||||
|
'./components/viewer/viewer.module#AppViewerModule'
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'favorite/libraries',
|
path: 'favorite/libraries',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
component: FavoriteLibrariesComponent,
|
component: FavoriteLibrariesComponent,
|
||||||
data: {
|
data: {
|
||||||
title: 'APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE',
|
title: 'APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE',
|
||||||
sortingPreferenceKey: 'favorite-libraries'
|
sortingPreferenceKey: 'favorite-libraries'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'personal-files',
|
path: 'favorites',
|
||||||
data: {
|
data: {
|
||||||
sortingPreferenceKey: 'personal-files'
|
sortingPreferenceKey: 'favorites'
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: FilesComponent,
|
loadChildren:
|
||||||
data: {
|
'./components/favorites/favorites.module#AppFavoritesModule'
|
||||||
title: 'APP.BROWSE.PERSONAL.TITLE',
|
|
||||||
defaultNodeId: '-my-'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':folderId',
|
path: 'view/:nodeId',
|
||||||
component: FilesComponent,
|
outlet: 'viewer',
|
||||||
data: {
|
children: [
|
||||||
title: 'APP.BROWSE.PERSONAL.TITLE'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'preview/:nodeId',
|
path: '',
|
||||||
loadChildren: './components/preview/preview.module#PreviewModule',
|
|
||||||
data: {
|
data: {
|
||||||
navigateSource: 'personal-files'
|
navigateSource: 'favorites'
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
loadChildren:
|
||||||
path: ':folderId/preview/:nodeId',
|
'./components/viewer/viewer.module#AppViewerModule'
|
||||||
loadChildren: './components/preview/preview.module#PreviewModule',
|
|
||||||
data: {
|
|
||||||
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'
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -193,11 +226,18 @@ export const APP_ROUTES: Routes = [
|
|||||||
'./components/recent-files/recent-files.module#AppRecentFilesModule'
|
'./components/recent-files/recent-files.module#AppRecentFilesModule'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'preview/:nodeId',
|
path: 'view/:nodeId',
|
||||||
loadChildren: './components/preview/preview.module#PreviewModule',
|
outlet: 'viewer',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
data: {
|
data: {
|
||||||
navigateSource: 'recent-files'
|
navigateSource: 'recent-files'
|
||||||
|
},
|
||||||
|
loadChildren:
|
||||||
|
'./components/viewer/viewer.module#AppViewerModule'
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -210,11 +250,18 @@ export const APP_ROUTES: Routes = [
|
|||||||
'./components/shared-files/shared-files.module#AppSharedFilesModule'
|
'./components/shared-files/shared-files.module#AppSharedFilesModule'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'preview/:nodeId',
|
path: 'view/:nodeId',
|
||||||
loadChildren: './components/preview/preview.module#PreviewModule',
|
outlet: 'viewer',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
data: {
|
data: {
|
||||||
navigateSource: 'shared'
|
navigateSource: 'shared'
|
||||||
|
},
|
||||||
|
loadChildren:
|
||||||
|
'./components/viewer/viewer.module#AppViewerModule'
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
canActivateChild: [AppSharedRuleGuard],
|
canActivateChild: [AppSharedRuleGuard],
|
||||||
@@ -235,16 +282,22 @@ export const APP_ROUTES: Routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
component: SearchResultsComponent,
|
component: SearchResultsComponent,
|
||||||
data: {
|
data: {
|
||||||
title: 'APP.BROWSE.SEARCH.TITLE',
|
title: 'APP.BROWSE.SEARCH.TITLE'
|
||||||
reuse: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'preview/:nodeId',
|
path: 'view/:nodeId',
|
||||||
loadChildren: './components/preview/preview.module#PreviewModule',
|
outlet: 'viewer',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
data: {
|
data: {
|
||||||
navigateSource: 'search'
|
navigateSource: 'search'
|
||||||
|
},
|
||||||
|
loadChildren:
|
||||||
|
'./components/viewer/viewer.module#AppViewerModule'
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -259,11 +312,18 @@ export const APP_ROUTES: Routes = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'preview/:nodeId',
|
path: 'view/:nodeId',
|
||||||
loadChildren: './components/preview/preview.module#PreviewModule',
|
outlet: 'viewer',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
data: {
|
data: {
|
||||||
navigateSource: 'search'
|
navigateSource: 'search'
|
||||||
|
},
|
||||||
|
loadChildren:
|
||||||
|
'./components/viewer/viewer.module#AppViewerModule'
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@@ -20,7 +20,10 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'custom'">
|
<ng-container *ngSwitchCase="'custom'">
|
||||||
<adf-dynamic-component [id]="actionRef.component"></adf-dynamic-component>
|
<adf-dynamic-component
|
||||||
|
[data]="actionRef.data"
|
||||||
|
[id]="actionRef.component"
|
||||||
|
></adf-dynamic-component>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngSwitchDefault>
|
<ng-container *ngSwitchDefault>
|
||||||
|
@@ -47,7 +47,10 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'custom'">
|
<ng-container *ngSwitchCase="'custom'">
|
||||||
<adf-dynamic-component [id]="entry.component"></adf-dynamic-component>
|
<adf-dynamic-component
|
||||||
|
[data]="entry.data"
|
||||||
|
[id]="entry.component"
|
||||||
|
></adf-dynamic-component>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
@@ -25,12 +25,18 @@
|
|||||||
|
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
import {
|
||||||
|
TestBed,
|
||||||
|
ComponentFixture,
|
||||||
|
fakeAsync,
|
||||||
|
tick
|
||||||
|
} from '@angular/core/testing';
|
||||||
import {
|
import {
|
||||||
AlfrescoApiService,
|
AlfrescoApiService,
|
||||||
NodeFavoriteDirective,
|
NodeFavoriteDirective,
|
||||||
DataTableComponent,
|
DataTableComponent,
|
||||||
AppConfigPipe
|
AppConfigPipe,
|
||||||
|
UploadService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { DocumentListComponent } from '@alfresco/adf-content-services';
|
import { DocumentListComponent } from '@alfresco/adf-content-services';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
@@ -44,8 +50,13 @@ describe('FavoritesComponent', () => {
|
|||||||
let alfrescoApi: AlfrescoApiService;
|
let alfrescoApi: AlfrescoApiService;
|
||||||
let contentApi: ContentApiService;
|
let contentApi: ContentApiService;
|
||||||
let router: Router;
|
let router: Router;
|
||||||
|
const mockRouter = {
|
||||||
|
url: 'favorites',
|
||||||
|
navigate: () => {}
|
||||||
|
};
|
||||||
let page;
|
let page;
|
||||||
let node;
|
let node;
|
||||||
|
let uploadService: UploadService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
page = {
|
page = {
|
||||||
@@ -78,6 +89,12 @@ describe('FavoritesComponent', () => {
|
|||||||
FavoritesComponent,
|
FavoritesComponent,
|
||||||
AppConfigPipe
|
AppConfigPipe
|
||||||
],
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: Router,
|
||||||
|
useValue: mockRouter
|
||||||
|
}
|
||||||
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,6 +108,7 @@ describe('FavoritesComponent', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
contentApi = TestBed.get(ContentApiService);
|
contentApi = TestBed.get(ContentApiService);
|
||||||
|
uploadService = TestBed.get(UploadService);
|
||||||
router = TestBed.get(Router);
|
router = TestBed.get(Router);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -132,14 +150,44 @@ describe('FavoritesComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('refresh', () => {
|
it('should call document list reload on fileUploadComplete event', fakeAsync(() => {
|
||||||
it('should call document list reload', () => {
|
|
||||||
spyOn(component, 'reload');
|
spyOn(component, 'reload');
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
component.reload();
|
fixture.detectChanges();
|
||||||
|
uploadService.fileUploadComplete.next();
|
||||||
|
tick(500);
|
||||||
|
|
||||||
expect(component.reload).toHaveBeenCalled();
|
expect(component.reload).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call document list reload on fileUploadDeleted event', fakeAsync(() => {
|
||||||
|
spyOn(component, 'reload');
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
uploadService.fileUploadDeleted.next();
|
||||||
|
tick(500);
|
||||||
|
|
||||||
|
expect(component.reload).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should navigate if node is folder', () => {
|
||||||
|
const nodeEntity = <any>{ entry: { isFolder: true } };
|
||||||
|
spyOn(component, 'navigate').and.stub();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.onNodeDoubleClick(nodeEntity);
|
||||||
|
expect(component.navigate).toHaveBeenCalledWith(nodeEntity.entry);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call showPreview if node is file', () => {
|
||||||
|
const nodeEntity = <any>{ entry: { isFile: true } };
|
||||||
|
spyOn(component, 'showPreview').and.stub();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.onNodeDoubleClick(nodeEntity);
|
||||||
|
expect(component.showPreview).toHaveBeenCalledWith(
|
||||||
|
nodeEntity,
|
||||||
|
mockRouter.url
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -112,7 +112,7 @@ export class FavoritesComponent extends PageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node.entry.isFile) {
|
if (node.entry.isFile) {
|
||||||
this.showPreview(node);
|
this.showPreview(node, this.router.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -49,9 +49,12 @@ describe('FilesComponent', () => {
|
|||||||
let fixture: ComponentFixture<FilesComponent>;
|
let fixture: ComponentFixture<FilesComponent>;
|
||||||
let component: FilesComponent;
|
let component: FilesComponent;
|
||||||
let uploadService: UploadService;
|
let uploadService: UploadService;
|
||||||
let router: Router;
|
|
||||||
let nodeActionsService: NodeActionsService;
|
let nodeActionsService: NodeActionsService;
|
||||||
let contentApi: ContentApiService;
|
let contentApi: ContentApiService;
|
||||||
|
let router = {
|
||||||
|
url: '',
|
||||||
|
navigate: jasmine.createSpy('navigate')
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -64,6 +67,10 @@ describe('FilesComponent', () => {
|
|||||||
AppConfigPipe
|
AppConfigPipe
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
{
|
||||||
|
provide: Router,
|
||||||
|
useValue: router
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: ActivatedRoute,
|
provide: ActivatedRoute,
|
||||||
useValue: {
|
useValue: {
|
||||||
@@ -122,7 +129,6 @@ describe('FilesComponent', () => {
|
|||||||
node.isFolder = false;
|
node.isFolder = false;
|
||||||
node.parentId = 'parent-id';
|
node.parentId = 'parent-id';
|
||||||
spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node }));
|
spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node }));
|
||||||
spyOn(router, 'navigate');
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@@ -231,24 +237,24 @@ describe('FilesComponent', () => {
|
|||||||
describe('Node navigation', () => {
|
describe('Node navigation', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node }));
|
spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node }));
|
||||||
spyOn(router, 'navigate');
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigates to node when id provided', () => {
|
it('should navigates to node when id provided', () => {
|
||||||
|
router.url = '/personal-files';
|
||||||
component.navigate(node.id);
|
component.navigate(node.id);
|
||||||
|
|
||||||
expect(router.navigate).toHaveBeenCalledWith(
|
expect(router.navigate).toHaveBeenCalledWith([
|
||||||
['./', node.id],
|
'/personal-files',
|
||||||
jasmine.any(Object)
|
node.id
|
||||||
);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigates to home when id not provided', () => {
|
it('should navigates to home when id not provided', () => {
|
||||||
|
router.url = '/personal-files';
|
||||||
component.navigate();
|
component.navigate();
|
||||||
|
|
||||||
expect(router.navigate).toHaveBeenCalledWith(['./'], jasmine.any(Object));
|
expect(router.navigate).toHaveBeenCalledWith(['/personal-files']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate home if node is root', () => {
|
it('should navigate home if node is root', () => {
|
||||||
@@ -258,9 +264,10 @@ describe('FilesComponent', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
router.url = '/personal-files';
|
||||||
component.navigate(node.id);
|
component.navigate(node.id);
|
||||||
|
|
||||||
expect(router.navigate).toHaveBeenCalledWith(['./'], jasmine.any(Object));
|
expect(router.navigate).toHaveBeenCalledWith(['/personal-files']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -131,15 +131,15 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
navigate(nodeId: string = null) {
|
navigate(nodeId: string = null) {
|
||||||
const commands = ['./'];
|
const location = this.router.url.match(/.*?(?=\/|$)/g)[1];
|
||||||
|
|
||||||
|
const commands = [`/${location}`];
|
||||||
|
|
||||||
if (nodeId && !this.isRootNode(nodeId)) {
|
if (nodeId && !this.isRootNode(nodeId)) {
|
||||||
commands.push(nodeId);
|
commands.push(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.router.navigate(commands, {
|
this.router.navigate(commands);
|
||||||
relativeTo: this.route.parent
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateTo(node: MinimalNodeEntity) {
|
navigateTo(node: MinimalNodeEntity) {
|
||||||
@@ -151,7 +151,7 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showPreview(node);
|
this.showPreview(node, this.router.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,13 +29,10 @@ import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core';
|
|||||||
import { AppLayoutComponent } from './app-layout.component';
|
import { AppLayoutComponent } from './app-layout.component';
|
||||||
import { AppTestingModule } from '../../../testing/app-testing.module';
|
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import {
|
import { AppStore, SetSelectedNodesAction } from '@alfresco/aca-shared/store';
|
||||||
AppStore,
|
|
||||||
SetSelectedNodesAction,
|
|
||||||
getAppSelection
|
|
||||||
} from '@alfresco/aca-shared/store';
|
|
||||||
import { Router, NavigationStart } from '@angular/router';
|
import { Router, NavigationStart } from '@angular/router';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
import { ResetSelectionAction } from '@alfresco/aca-shared/store';
|
||||||
|
|
||||||
class MockRouter {
|
class MockRouter {
|
||||||
private url = 'some-url';
|
private url = 'some-url';
|
||||||
@@ -139,30 +136,17 @@ describe('AppLayoutComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset selection before navigation', done => {
|
it('should reset selection before navigation', () => {
|
||||||
fixture.detectChanges();
|
|
||||||
const selection = [<any>{ entry: { id: 'nodeId', name: 'name' } }];
|
const selection = [<any>{ entry: { id: 'nodeId', name: 'name' } }];
|
||||||
|
spyOn(store, 'dispatch').and.stub();
|
||||||
|
fixture.detectChanges();
|
||||||
store.dispatch(new SetSelectedNodesAction(selection));
|
store.dispatch(new SetSelectedNodesAction(selection));
|
||||||
|
|
||||||
router.navigateByUrl('somewhere/over/the/rainbow');
|
router.navigateByUrl('somewhere/over/the/rainbow');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
store.select(getAppSelection).subscribe(state => {
|
|
||||||
expect(state.isEmpty).toBe(true);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not reset selection if route is `/search`', done => {
|
expect(store.dispatch['calls'].mostRecent().args).toEqual([
|
||||||
fixture.detectChanges();
|
new ResetSelectionAction()
|
||||||
const selection = [<any>{ entry: { id: 'nodeId', name: 'name' } }];
|
]);
|
||||||
store.dispatch(new SetSelectedNodesAction(selection));
|
|
||||||
|
|
||||||
router.navigateByUrl('/search;q=');
|
|
||||||
fixture.detectChanges();
|
|
||||||
store.select(getAppSelection).subscribe(state => {
|
|
||||||
expect(state.isEmpty).toBe(false);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should close menu on mobile screen size', () => {
|
it('should close menu on mobile screen size', () => {
|
||||||
|
@@ -44,7 +44,7 @@ import { BreakpointObserver } from '@angular/cdk/layout';
|
|||||||
import {
|
import {
|
||||||
AppStore,
|
AppStore,
|
||||||
getCurrentFolder,
|
getCurrentFolder,
|
||||||
SetSelectedNodesAction
|
ResetSelectionAction
|
||||||
} from '@alfresco/aca-shared/store';
|
} from '@alfresco/aca-shared/store';
|
||||||
import { Directionality } from '@angular/cdk/bidi';
|
import { Directionality } from '@angular/cdk/bidi';
|
||||||
|
|
||||||
@@ -139,16 +139,12 @@ export class AppLayoutComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.router.events
|
this.router.events
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(event => {
|
filter(event => event instanceof NavigationStart),
|
||||||
return (
|
|
||||||
event instanceof NavigationStart &&
|
|
||||||
// search employs reuse route strategy
|
|
||||||
!event.url.startsWith('/search;')
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
takeUntil(this.onDestroy$)
|
takeUntil(this.onDestroy$)
|
||||||
)
|
)
|
||||||
.subscribe(() => this.store.dispatch(new SetSelectedNodesAction([])));
|
.subscribe(() => {
|
||||||
|
this.store.dispatch(new ResetSelectionAction());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
@@ -37,13 +37,13 @@ import { AppExtensionService } from '../extensions/extension.service';
|
|||||||
import { ContentManagementService } from '../services/content-management.service';
|
import { ContentManagementService } from '../services/content-management.service';
|
||||||
import {
|
import {
|
||||||
AppStore,
|
AppStore,
|
||||||
ViewFileAction,
|
|
||||||
ReloadDocumentListAction,
|
ReloadDocumentListAction,
|
||||||
getCurrentFolder,
|
getCurrentFolder,
|
||||||
getAppSelection,
|
getAppSelection,
|
||||||
getDocumentDisplayMode,
|
getDocumentDisplayMode,
|
||||||
isInfoDrawerOpened,
|
isInfoDrawerOpened,
|
||||||
getSharedUrl
|
getSharedUrl,
|
||||||
|
ViewNodeAction
|
||||||
} from '@alfresco/aca-shared/store';
|
} from '@alfresco/aca-shared/store';
|
||||||
import { isLocked, isLibrary } from '../utils/node.utils';
|
import { isLocked, isLibrary } from '../utils/node.utils';
|
||||||
|
|
||||||
@@ -105,10 +105,12 @@ export abstract class PageComponent implements OnInit, OnDestroy {
|
|||||||
this.onDestroy$.complete();
|
this.onDestroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
showPreview(node: MinimalNodeEntity) {
|
showPreview(node: MinimalNodeEntity, location?: string) {
|
||||||
if (node && node.entry) {
|
if (node && node.entry) {
|
||||||
const parentId = this.node ? this.node.id : null;
|
const id =
|
||||||
this.store.dispatch(new ViewFileAction(node, parentId));
|
(<any>node).entry.nodeId || (<any>node).entry.guid || node.entry.id;
|
||||||
|
|
||||||
|
this.store.dispatch(new ViewNodeAction(id, location));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,23 +23,34 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
import {
|
||||||
|
TestBed,
|
||||||
|
ComponentFixture,
|
||||||
|
fakeAsync,
|
||||||
|
tick
|
||||||
|
} from '@angular/core/testing';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
AlfrescoApiService,
|
AlfrescoApiService,
|
||||||
NodeFavoriteDirective,
|
NodeFavoriteDirective,
|
||||||
DataTableComponent,
|
DataTableComponent,
|
||||||
AppConfigPipe
|
AppConfigPipe,
|
||||||
|
UploadService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { DocumentListComponent } from '@alfresco/adf-content-services';
|
import { DocumentListComponent } from '@alfresco/adf-content-services';
|
||||||
import { RecentFilesComponent } from './recent-files.component';
|
import { RecentFilesComponent } from './recent-files.component';
|
||||||
import { AppTestingModule } from '../../testing/app-testing.module';
|
import { AppTestingModule } from '../../testing/app-testing.module';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
describe('RecentFilesComponent', () => {
|
describe('RecentFilesComponent', () => {
|
||||||
let fixture: ComponentFixture<RecentFilesComponent>;
|
let fixture: ComponentFixture<RecentFilesComponent>;
|
||||||
let component: RecentFilesComponent;
|
let component: RecentFilesComponent;
|
||||||
let alfrescoApi: AlfrescoApiService;
|
let alfrescoApi: AlfrescoApiService;
|
||||||
let page;
|
let page;
|
||||||
|
let uploadService: UploadService;
|
||||||
|
const mockRouter = {
|
||||||
|
url: 'recent-files'
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
page = {
|
page = {
|
||||||
@@ -60,6 +71,12 @@ describe('RecentFilesComponent', () => {
|
|||||||
RecentFilesComponent,
|
RecentFilesComponent,
|
||||||
AppConfigPipe
|
AppConfigPipe
|
||||||
],
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: Router,
|
||||||
|
useValue: mockRouter
|
||||||
|
}
|
||||||
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -67,6 +84,7 @@ describe('RecentFilesComponent', () => {
|
|||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
alfrescoApi = TestBed.get(AlfrescoApiService);
|
alfrescoApi = TestBed.get(AlfrescoApiService);
|
||||||
|
uploadService = TestBed.get(UploadService);
|
||||||
alfrescoApi.reset();
|
alfrescoApi.reset();
|
||||||
|
|
||||||
spyOn(alfrescoApi.peopleApi, 'getPerson').and.returnValue(
|
spyOn(alfrescoApi.peopleApi, 'getPerson').and.returnValue(
|
||||||
@@ -80,14 +98,32 @@ describe('RecentFilesComponent', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('refresh', () => {
|
it('should call document list reload on fileUploadComplete event', fakeAsync(() => {
|
||||||
it('should call document list reload', () => {
|
|
||||||
spyOn(component, 'reload');
|
spyOn(component, 'reload');
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
component.reload();
|
fixture.detectChanges();
|
||||||
|
uploadService.fileUploadComplete.next();
|
||||||
|
tick(500);
|
||||||
|
|
||||||
expect(component.reload).toHaveBeenCalled();
|
expect(component.reload).toHaveBeenCalled();
|
||||||
});
|
}));
|
||||||
|
|
||||||
|
it('should call document list reload on fileUploadDeleted event', fakeAsync(() => {
|
||||||
|
spyOn(component, 'reload');
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
uploadService.fileUploadDeleted.next();
|
||||||
|
tick(500);
|
||||||
|
|
||||||
|
expect(component.reload).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call showPreview method', () => {
|
||||||
|
const node = <any>{ entry: {} };
|
||||||
|
spyOn(component, 'showPreview');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.onNodeDoubleClick(node);
|
||||||
|
expect(component.showPreview).toHaveBeenCalledWith(node, mockRouter.url);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -33,6 +33,7 @@ import { AppStore } from '@alfresco/aca-shared/store';
|
|||||||
import { AppExtensionService } from '../../extensions/extension.service';
|
import { AppExtensionService } from '../../extensions/extension.service';
|
||||||
import { UploadService } from '@alfresco/adf-core';
|
import { UploadService } from '@alfresco/adf-core';
|
||||||
import { debounceTime } from 'rxjs/operators';
|
import { debounceTime } from 'rxjs/operators';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './recent-files.component.html'
|
templateUrl: './recent-files.component.html'
|
||||||
@@ -47,7 +48,8 @@ export class RecentFilesComponent extends PageComponent implements OnInit {
|
|||||||
extensions: AppExtensionService,
|
extensions: AppExtensionService,
|
||||||
content: ContentManagementService,
|
content: ContentManagementService,
|
||||||
private uploadService: UploadService,
|
private uploadService: UploadService,
|
||||||
private breakpointObserver: BreakpointObserver
|
private breakpointObserver: BreakpointObserver,
|
||||||
|
private router: Router
|
||||||
) {
|
) {
|
||||||
super(store, extensions, content);
|
super(store, extensions, content);
|
||||||
}
|
}
|
||||||
@@ -75,7 +77,7 @@ export class RecentFilesComponent extends PageComponent implements OnInit {
|
|||||||
|
|
||||||
onNodeDoubleClick(node: MinimalNodeEntity) {
|
onNodeDoubleClick(node: MinimalNodeEntity) {
|
||||||
if (node && node.entry) {
|
if (node && node.entry) {
|
||||||
this.showPreview(node);
|
this.showPreview(node, this.router.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,11 +32,12 @@ import {
|
|||||||
OnDestroy
|
OnDestroy
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MinimalNodeEntity } from '@alfresco/js-api';
|
import { MinimalNodeEntity } from '@alfresco/js-api';
|
||||||
import { ViewFileAction, NavigateToFolder } from '@alfresco/aca-shared/store';
|
import { ViewNodeAction, NavigateToFolder } from '@alfresco/aca-shared/store';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { BehaviorSubject, Subject } from 'rxjs';
|
import { BehaviorSubject, Subject } from 'rxjs';
|
||||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'aca-search-results-row',
|
selector: 'aca-search-results-row',
|
||||||
@@ -58,7 +59,8 @@ export class SearchResultsRowComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private store: Store<any>,
|
private store: Store<any>,
|
||||||
private alfrescoApiService: AlfrescoApiService
|
private alfrescoApiService: AlfrescoApiService,
|
||||||
|
private router: Router
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -123,7 +125,9 @@ export class SearchResultsRowComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
showPreview(event: MouseEvent) {
|
showPreview(event: MouseEvent) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.store.dispatch(new ViewFileAction(this.node));
|
this.store.dispatch(
|
||||||
|
new ViewNodeAction(this.node.entry.id, this.router.url)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(event: MouseEvent) {
|
navigate(event: MouseEvent) {
|
||||||
|
@@ -47,7 +47,7 @@ import {
|
|||||||
} from '@alfresco/aca-shared/store';
|
} from '@alfresco/aca-shared/store';
|
||||||
import { Pagination } from '@alfresco/js-api';
|
import { Pagination } from '@alfresco/js-api';
|
||||||
import { SearchQueryBuilderService } from '@alfresco/adf-content-services';
|
import { SearchQueryBuilderService } from '@alfresco/adf-content-services';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
describe('SearchComponent', () => {
|
describe('SearchComponent', () => {
|
||||||
let component: SearchResultsComponent;
|
let component: SearchResultsComponent;
|
||||||
@@ -57,6 +57,7 @@ describe('SearchComponent', () => {
|
|||||||
let queryBuilder: SearchQueryBuilderService;
|
let queryBuilder: SearchQueryBuilderService;
|
||||||
let alfrescoApi: AlfrescoApiService;
|
let alfrescoApi: AlfrescoApiService;
|
||||||
let translate: TranslationService;
|
let translate: TranslationService;
|
||||||
|
let router: Router;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -90,6 +91,7 @@ describe('SearchComponent', () => {
|
|||||||
queryBuilder = TestBed.get(SearchQueryBuilderService);
|
queryBuilder = TestBed.get(SearchQueryBuilderService);
|
||||||
alfrescoApi = TestBed.get(AlfrescoApiService);
|
alfrescoApi = TestBed.get(AlfrescoApiService);
|
||||||
translate = TestBed.get(TranslationService);
|
translate = TestBed.get(TranslationService);
|
||||||
|
router = TestBed.get(Router);
|
||||||
|
|
||||||
fixture = TestBed.createComponent(SearchResultsComponent);
|
fixture = TestBed.createComponent(SearchResultsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
@@ -303,7 +305,7 @@ describe('SearchComponent', () => {
|
|||||||
|
|
||||||
component.onNodeDoubleClick(node);
|
component.onNodeDoubleClick(node);
|
||||||
|
|
||||||
expect(component.showPreview).toHaveBeenCalledWith(node);
|
expect(component.showPreview).toHaveBeenCalledWith(node, router.url);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should re-run search on pagination change', () => {
|
it('should re-run search on pagination change', () => {
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { NodePaging, Pagination, MinimalNodeEntity } from '@alfresco/js-api';
|
import { NodePaging, Pagination, MinimalNodeEntity } from '@alfresco/js-api';
|
||||||
import { ActivatedRoute, Params } from '@angular/router';
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import {
|
import {
|
||||||
SearchQueryBuilderService,
|
SearchQueryBuilderService,
|
||||||
SearchFilterComponent
|
SearchFilterComponent
|
||||||
@@ -69,7 +69,8 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
|
|||||||
store: Store<AppStore>,
|
store: Store<AppStore>,
|
||||||
extensions: AppExtensionService,
|
extensions: AppExtensionService,
|
||||||
content: ContentManagementService,
|
content: ContentManagementService,
|
||||||
private translationService: TranslationService
|
private translationService: TranslationService,
|
||||||
|
private router: Router
|
||||||
) {
|
) {
|
||||||
super(store, extensions, content);
|
super(store, extensions, content);
|
||||||
|
|
||||||
@@ -251,7 +252,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showPreview(node);
|
this.showPreview(node, this.router.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,8 +19,8 @@
|
|||||||
currentFolderId="-sharedlinks-"
|
currentFolderId="-sharedlinks-"
|
||||||
selectionMode="multiple"
|
selectionMode="multiple"
|
||||||
[sorting]="['modifiedAt', 'desc']"
|
[sorting]="['modifiedAt', 'desc']"
|
||||||
(node-dblclick)="showPreview($event.detail?.node)"
|
(node-dblclick)="preview($event.detail?.node)"
|
||||||
(name-click)="showPreview($event.detail?.node)"
|
(name-click)="preview($event.detail?.node)"
|
||||||
>
|
>
|
||||||
<adf-custom-empty-content-template>
|
<adf-custom-empty-content-template>
|
||||||
<adf-empty-content
|
<adf-empty-content
|
||||||
|
@@ -23,23 +23,36 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
import {
|
||||||
|
TestBed,
|
||||||
|
ComponentFixture,
|
||||||
|
fakeAsync,
|
||||||
|
tick
|
||||||
|
} from '@angular/core/testing';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
AlfrescoApiService,
|
AlfrescoApiService,
|
||||||
NodeFavoriteDirective,
|
NodeFavoriteDirective,
|
||||||
DataTableComponent,
|
DataTableComponent,
|
||||||
AppConfigPipe
|
AppConfigPipe,
|
||||||
|
UploadService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { DocumentListComponent } from '@alfresco/adf-content-services';
|
import { DocumentListComponent } from '@alfresco/adf-content-services';
|
||||||
import { SharedFilesComponent } from './shared-files.component';
|
import { SharedFilesComponent } from './shared-files.component';
|
||||||
import { AppTestingModule } from '../../testing/app-testing.module';
|
import { AppTestingModule } from '../../testing/app-testing.module';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { ContentManagementService } from '../../services/content-management.service';
|
||||||
|
|
||||||
describe('SharedFilesComponent', () => {
|
describe('SharedFilesComponent', () => {
|
||||||
let fixture: ComponentFixture<SharedFilesComponent>;
|
let fixture: ComponentFixture<SharedFilesComponent>;
|
||||||
let component: SharedFilesComponent;
|
let component: SharedFilesComponent;
|
||||||
let alfrescoApi: AlfrescoApiService;
|
let alfrescoApi: AlfrescoApiService;
|
||||||
let page;
|
let page;
|
||||||
|
let uploadService: UploadService;
|
||||||
|
let contentManagementService: ContentManagementService;
|
||||||
|
const mockRouter = {
|
||||||
|
url: 'shared-files'
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
page = {
|
page = {
|
||||||
@@ -60,10 +73,18 @@ describe('SharedFilesComponent', () => {
|
|||||||
SharedFilesComponent,
|
SharedFilesComponent,
|
||||||
AppConfigPipe
|
AppConfigPipe
|
||||||
],
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: Router,
|
||||||
|
useValue: mockRouter
|
||||||
|
}
|
||||||
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture = TestBed.createComponent(SharedFilesComponent);
|
fixture = TestBed.createComponent(SharedFilesComponent);
|
||||||
|
uploadService = TestBed.get(UploadService);
|
||||||
|
contentManagementService = TestBed.get(ContentManagementService);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
alfrescoApi = TestBed.get(AlfrescoApiService);
|
alfrescoApi = TestBed.get(AlfrescoApiService);
|
||||||
@@ -74,14 +95,42 @@ describe('SharedFilesComponent', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('refresh', () => {
|
it('should call document list reload on linksUnshared event', fakeAsync(() => {
|
||||||
it('should call document list reload', () => {
|
|
||||||
spyOn(component, 'reload');
|
spyOn(component, 'reload');
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
component.reload();
|
fixture.detectChanges();
|
||||||
|
contentManagementService.linksUnshared.next();
|
||||||
|
tick(500);
|
||||||
|
|
||||||
expect(component.reload).toHaveBeenCalled();
|
expect(component.reload).toHaveBeenCalled();
|
||||||
});
|
}));
|
||||||
|
|
||||||
|
it('should call document list reload on fileUploadComplete event', fakeAsync(() => {
|
||||||
|
spyOn(component, 'reload');
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
uploadService.fileUploadComplete.next();
|
||||||
|
tick(500);
|
||||||
|
|
||||||
|
expect(component.reload).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call document list reload on fileUploadDeleted event', fakeAsync(() => {
|
||||||
|
spyOn(component, 'reload');
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
uploadService.fileUploadDeleted.next();
|
||||||
|
tick(500);
|
||||||
|
|
||||||
|
expect(component.reload).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call showPreview method', () => {
|
||||||
|
const node = <any>{ entry: {} };
|
||||||
|
spyOn(component, 'showPreview');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.preview(node);
|
||||||
|
expect(component.showPreview).toHaveBeenCalledWith(node, mockRouter.url);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -31,6 +31,8 @@ import { Store } from '@ngrx/store';
|
|||||||
import { AppExtensionService } from '../../extensions/extension.service';
|
import { AppExtensionService } from '../../extensions/extension.service';
|
||||||
import { debounceTime } from 'rxjs/operators';
|
import { debounceTime } from 'rxjs/operators';
|
||||||
import { UploadService } from '@alfresco/adf-core';
|
import { UploadService } from '@alfresco/adf-core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { MinimalNodeEntity } from '@alfresco/js-api';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './shared-files.component.html'
|
templateUrl: './shared-files.component.html'
|
||||||
@@ -45,7 +47,8 @@ export class SharedFilesComponent extends PageComponent implements OnInit {
|
|||||||
extensions: AppExtensionService,
|
extensions: AppExtensionService,
|
||||||
content: ContentManagementService,
|
content: ContentManagementService,
|
||||||
private uploadService: UploadService,
|
private uploadService: UploadService,
|
||||||
private breakpointObserver: BreakpointObserver
|
private breakpointObserver: BreakpointObserver,
|
||||||
|
private router: Router
|
||||||
) {
|
) {
|
||||||
super(store, extensions, content);
|
super(store, extensions, content);
|
||||||
}
|
}
|
||||||
@@ -74,4 +77,8 @@ export class SharedFilesComponent extends PageComponent implements OnInit {
|
|||||||
|
|
||||||
this.columns = this.extensions.documentListPresets.shared || [];
|
this.columns = this.extensions.documentListPresets.shared || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preview(node: MinimalNodeEntity) {
|
||||||
|
this.showPreview(node, this.router.url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,9 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'custom'">
|
<ng-container *ngSwitchCase="'custom'">
|
||||||
<adf-dynamic-component [id]="actionRef.component"></adf-dynamic-component>
|
<adf-dynamic-component
|
||||||
|
[data]="actionRef.data"
|
||||||
|
[id]="actionRef.component"
|
||||||
|
></adf-dynamic-component>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -39,6 +39,7 @@ import { ToggleJoinLibraryMenuComponent } from './toggle-join-library/toggle-joi
|
|||||||
import { DirectivesModule } from '../../directives/directives.module';
|
import { DirectivesModule } from '../../directives/directives.module';
|
||||||
import { ToggleFavoriteLibraryComponent } from './toggle-favorite-library/toggle-favorite-library.component';
|
import { ToggleFavoriteLibraryComponent } from './toggle-favorite-library/toggle-favorite-library.component';
|
||||||
import { ToggleEditOfflineComponent } from './toggle-edit-offline/toggle-edit-offline.component';
|
import { ToggleEditOfflineComponent } from './toggle-edit-offline/toggle-edit-offline.component';
|
||||||
|
import { ViewNodeComponent } from './view-node/view-node.component';
|
||||||
import { AppCommonModule } from '../common/common.module';
|
import { AppCommonModule } from '../common/common.module';
|
||||||
|
|
||||||
export function components() {
|
export function components() {
|
||||||
@@ -53,7 +54,8 @@ export function components() {
|
|||||||
ToggleJoinLibraryButtonComponent,
|
ToggleJoinLibraryButtonComponent,
|
||||||
ToggleJoinLibraryMenuComponent,
|
ToggleJoinLibraryMenuComponent,
|
||||||
ToggleFavoriteLibraryComponent,
|
ToggleFavoriteLibraryComponent,
|
||||||
ToggleEditOfflineComponent
|
ToggleEditOfflineComponent,
|
||||||
|
ViewNodeComponent
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
105
src/app/components/toolbar/view-node/view-node.component.spec.ts
Normal file
105
src/app/components/toolbar/view-node/view-node.component.spec.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*!
|
||||||
|
* @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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { ViewNodeComponent } from './view-node.component';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { CoreModule } from '@alfresco/adf-core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
|
describe('ToggleFavoriteComponent', () => {
|
||||||
|
let component: ViewNodeComponent;
|
||||||
|
let fixture;
|
||||||
|
const mockRouter = {
|
||||||
|
url: 'some-url'
|
||||||
|
};
|
||||||
|
const mockStore = <any>{
|
||||||
|
dispatch: jasmine.createSpy('dispatch'),
|
||||||
|
select: jasmine.createSpy('select').and.returnValue(
|
||||||
|
of({
|
||||||
|
file: {
|
||||||
|
entry: {
|
||||||
|
id: 'nodeId'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [CoreModule.forRoot()],
|
||||||
|
declarations: [ViewNodeComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: Store, useValue: mockStore },
|
||||||
|
{ provide: Router, useValue: mockRouter }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ViewNodeComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mockStore.dispatch.calls.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render as a menu button', () => {
|
||||||
|
component.data = {
|
||||||
|
menuButton: true
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(fixture.nativeElement.querySelector('.mat-menu-item')).not.toBe(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render as a icon button', () => {
|
||||||
|
component.data = {
|
||||||
|
iconButton: true
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(fixture.nativeElement.querySelector('.mat-icon-button')).not.toBe(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call ViewNodeAction onClick event', () => {
|
||||||
|
component.data = {
|
||||||
|
iconButton: true
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.onClick();
|
||||||
|
|
||||||
|
expect(mockStore.dispatch).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
75
src/app/components/toolbar/view-node/view-node.component.ts
Normal file
75
src/app/components/toolbar/view-node/view-node.component.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*!
|
||||||
|
* @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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, ViewEncapsulation, Input } from '@angular/core';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import {
|
||||||
|
AppStore,
|
||||||
|
ViewNodeAction,
|
||||||
|
getAppSelection
|
||||||
|
} from '@alfresco/aca-shared/store';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
import { SharedLinkEntry } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-view-node',
|
||||||
|
template: `
|
||||||
|
<button
|
||||||
|
*ngIf="data.iconButton"
|
||||||
|
mat-icon-button
|
||||||
|
[attr.title]="data.title | translate"
|
||||||
|
(click)="onClick()"
|
||||||
|
>
|
||||||
|
<mat-icon>visibility</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button *ngIf="data.menuButton" mat-menu-item (click)="onClick()">
|
||||||
|
<mat-icon>visibility</mat-icon>
|
||||||
|
<span>{{ data.title | translate }}</span>
|
||||||
|
</button>
|
||||||
|
`,
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: { class: 'app-view-node' }
|
||||||
|
})
|
||||||
|
export class ViewNodeComponent {
|
||||||
|
@Input() data: any;
|
||||||
|
|
||||||
|
constructor(private store: Store<AppStore>, private router: Router) {}
|
||||||
|
|
||||||
|
onClick() {
|
||||||
|
this.store
|
||||||
|
.select(getAppSelection)
|
||||||
|
.pipe(take(1))
|
||||||
|
.subscribe(selection => {
|
||||||
|
const id =
|
||||||
|
(<SharedLinkEntry>selection.file).entry.nodeId ||
|
||||||
|
(<any>selection.file).entry.guid ||
|
||||||
|
selection.file.entry.id;
|
||||||
|
|
||||||
|
this.store.dispatch(new ViewNodeAction(id, this.router.url));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -1,12 +1,21 @@
|
|||||||
<ng-container *ngIf="nodeId">
|
<ng-container *ngIf="nodeId">
|
||||||
<adf-viewer
|
<adf-viewer
|
||||||
|
[ngClass]="{
|
||||||
|
'right_side--hide': !showRightSide
|
||||||
|
}"
|
||||||
[nodeId]="nodeId"
|
[nodeId]="nodeId"
|
||||||
[allowNavigate]="false"
|
[allowNavigate]="navigateMultiple"
|
||||||
|
[allowRightSidebar]="true"
|
||||||
[allowPrint]="false"
|
[allowPrint]="false"
|
||||||
|
[showRightSidebar]="true"
|
||||||
[allowDownload]="false"
|
[allowDownload]="false"
|
||||||
[allowFullScreen]="false"
|
[allowFullScreen]="false"
|
||||||
[allowRightSidebar]="showRightSide"
|
[overlayMode]="true"
|
||||||
[showRightSidebar]="showRightSide"
|
(showViewerChange)="onViewerVisibilityChanged($event)"
|
||||||
|
[canNavigateBefore]="previousNodeId"
|
||||||
|
[canNavigateNext]="nextNodeId"
|
||||||
|
(navigateBefore)="onNavigateBefore()"
|
||||||
|
(navigateNext)="onNavigateNext()"
|
||||||
>
|
>
|
||||||
<adf-viewer-sidebar *ngIf="infoDrawerOpened$ | async">
|
<adf-viewer-sidebar *ngIf="infoDrawerOpened$ | async">
|
||||||
<aca-info-drawer [node]="selection.file"></aca-info-drawer>
|
<aca-info-drawer [node]="selection.file"></aca-info-drawer>
|
||||||
|
@@ -20,4 +20,8 @@
|
|||||||
.adf-viewer-toolbar .mat-toolbar > button:last-child {
|
.adf-viewer-toolbar .mat-toolbar > button:last-child {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.adf-viewer.right_side--hide .adf-viewer__sidebar__right {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,17 +28,29 @@ import {
|
|||||||
AppStore,
|
AppStore,
|
||||||
getAppSelection,
|
getAppSelection,
|
||||||
isInfoDrawerOpened,
|
isInfoDrawerOpened,
|
||||||
SetSelectedNodesAction
|
SetSelectedNodesAction,
|
||||||
|
ClosePreviewAction,
|
||||||
|
ViewerActionTypes,
|
||||||
|
ViewNodeAction
|
||||||
} from '@alfresco/aca-shared/store';
|
} from '@alfresco/aca-shared/store';
|
||||||
import { ContentActionRef, SelectionState } from '@alfresco/adf-extensions';
|
import { ContentActionRef, SelectionState } from '@alfresco/adf-extensions';
|
||||||
import { MinimalNodeEntryEntity } from '@alfresco/js-api';
|
import { MinimalNodeEntryEntity } from '@alfresco/js-api';
|
||||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute, Router, PRIMARY_OUTLET } from '@angular/router';
|
||||||
|
import {
|
||||||
|
UserPreferencesService,
|
||||||
|
ObjectUtils,
|
||||||
|
UploadService,
|
||||||
|
AlfrescoApiService
|
||||||
|
} from '@alfresco/adf-core';
|
||||||
|
import { ContentManagementService } from '../../services/content-management.service';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { from, Observable, Subject } from 'rxjs';
|
import { from, Observable, Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil, debounceTime } from 'rxjs/operators';
|
||||||
import { AppExtensionService } from '../../extensions/extension.service';
|
import { AppExtensionService } from '../../extensions/extension.service';
|
||||||
|
import { Actions, ofType } from '@ngrx/effects';
|
||||||
|
import { SearchRequest } from '@alfresco/js-api';
|
||||||
|
import { ReloadDocumentListAction } from '@alfresco/aca-shared/store';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-viewer',
|
selector: 'app-viewer',
|
||||||
templateUrl: './viewer.component.html',
|
templateUrl: './viewer.component.html',
|
||||||
@@ -49,6 +61,7 @@ import { AppExtensionService } from '../../extensions/extension.service';
|
|||||||
export class AppViewerComponent implements OnInit, OnDestroy {
|
export class AppViewerComponent implements OnInit, OnDestroy {
|
||||||
onDestroy$ = new Subject<boolean>();
|
onDestroy$ = new Subject<boolean>();
|
||||||
|
|
||||||
|
folderId: string = null;
|
||||||
nodeId: string = null;
|
nodeId: string = null;
|
||||||
node: MinimalNodeEntryEntity;
|
node: MinimalNodeEntryEntity;
|
||||||
selection: SelectionState;
|
selection: SelectionState;
|
||||||
@@ -58,11 +71,55 @@ export class AppViewerComponent implements OnInit, OnDestroy {
|
|||||||
openWith: ContentActionRef[] = [];
|
openWith: ContentActionRef[] = [];
|
||||||
toolbarActions: ContentActionRef[] = [];
|
toolbarActions: ContentActionRef[] = [];
|
||||||
|
|
||||||
|
navigateSource: string = null;
|
||||||
|
previousNodeId: string;
|
||||||
|
nextNodeId: string;
|
||||||
|
navigateMultiple = true;
|
||||||
|
routesSkipNavigation = ['shared', 'recent-files', 'favorites'];
|
||||||
|
navigationSources = [
|
||||||
|
'favorites',
|
||||||
|
'libraries',
|
||||||
|
'personal-files',
|
||||||
|
'recent-files',
|
||||||
|
'shared'
|
||||||
|
];
|
||||||
|
recentFileFilters = [
|
||||||
|
'TYPE:"content"',
|
||||||
|
'-PNAME:"0/wiki"',
|
||||||
|
'-TYPE:"app:filelink"',
|
||||||
|
'-TYPE:"fm:post"',
|
||||||
|
'-TYPE:"cm:thumbnail"',
|
||||||
|
'-TYPE:"cm:failedThumbnail"',
|
||||||
|
'-TYPE:"cm:rating"',
|
||||||
|
'-TYPE:"dl:dataList"',
|
||||||
|
'-TYPE:"dl:todoList"',
|
||||||
|
'-TYPE:"dl:issue"',
|
||||||
|
'-TYPE:"dl:contact"',
|
||||||
|
'-TYPE:"dl:eventAgenda"',
|
||||||
|
'-TYPE:"dl:event"',
|
||||||
|
'-TYPE:"dl:task"',
|
||||||
|
'-TYPE:"dl:simpletask"',
|
||||||
|
'-TYPE:"dl:meetingAgenda"',
|
||||||
|
'-TYPE:"dl:location"',
|
||||||
|
'-TYPE:"fm:topic"',
|
||||||
|
'-TYPE:"fm:post"',
|
||||||
|
'-TYPE:"ia:calendarEvent"',
|
||||||
|
'-TYPE:"lnk:link"'
|
||||||
|
];
|
||||||
|
|
||||||
|
private previewLocation: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private store: Store<AppStore>,
|
private store: Store<AppStore>,
|
||||||
protected extensions: AppExtensionService,
|
private extensions: AppExtensionService,
|
||||||
private contentApi: ContentApiService
|
private contentApi: ContentApiService,
|
||||||
|
private actions$: Actions,
|
||||||
|
private preferences: UserPreferencesService,
|
||||||
|
private content: ContentManagementService,
|
||||||
|
private apiService: AlfrescoApiService,
|
||||||
|
private uploadService: UploadService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -85,11 +142,50 @@ export class AppViewerComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.route.params.subscribe(params => {
|
this.route.params.subscribe(params => {
|
||||||
|
this.folderId = params.folderId;
|
||||||
const { nodeId } = params;
|
const { nodeId } = params;
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
this.displayNode(nodeId);
|
this.displayNode(nodeId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.route.snapshot.data && this.route.snapshot.data.navigateSource) {
|
||||||
|
const source = this.route.snapshot.data.navigateSource.toLowerCase();
|
||||||
|
if (this.navigationSources.includes(source)) {
|
||||||
|
this.navigateSource = this.route.snapshot.data.navigateSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actions$
|
||||||
|
.pipe(
|
||||||
|
ofType<ClosePreviewAction>(ViewerActionTypes.ClosePreview),
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
)
|
||||||
|
.subscribe(() => this.navigateToFileLocation());
|
||||||
|
|
||||||
|
this.content.nodesDeleted
|
||||||
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
|
.subscribe(() => this.navigateToFileLocation());
|
||||||
|
|
||||||
|
this.uploadService.fileUploadDeleted
|
||||||
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
|
.subscribe(() => this.navigateToFileLocation());
|
||||||
|
|
||||||
|
this.uploadService.fileUploadComplete
|
||||||
|
.pipe(
|
||||||
|
debounceTime(300),
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
)
|
||||||
|
.subscribe(file => this.apiService.nodeUpdated.next(file.data.entry));
|
||||||
|
|
||||||
|
this.previewLocation = this.router.url
|
||||||
|
.substr(0, this.router.url.indexOf('/', 1))
|
||||||
|
.replace(/\//g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
onViewerVisibilityChanged() {
|
||||||
|
this.store.dispatch(new ReloadDocumentListAction());
|
||||||
|
this.navigateToFileLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@@ -108,14 +204,228 @@ export class AppViewerComponent implements OnInit, OnDestroy {
|
|||||||
this.store.dispatch(new SetSelectedNodesAction([{ entry: this.node }]));
|
this.store.dispatch(new SetSelectedNodesAction([{ entry: this.node }]));
|
||||||
|
|
||||||
if (this.node && this.node.isFile) {
|
if (this.node && this.node.isFile) {
|
||||||
|
const nearest = await this.getNearestNodes(
|
||||||
|
this.node.id,
|
||||||
|
this.node.parentId
|
||||||
|
);
|
||||||
|
|
||||||
|
this.previousNodeId = nearest.left;
|
||||||
|
this.nextNodeId = nearest.right;
|
||||||
this.nodeId = this.node.id;
|
this.nodeId = this.node.id;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
if (!err || err.status !== 401) {
|
const statusCode = JSON.parse(error.message).error.statusCode;
|
||||||
// this.router.navigate([this.previewLocation, id]);
|
|
||||||
|
if (statusCode !== 401) {
|
||||||
|
this.router
|
||||||
|
.navigate([this.previewLocation, { outlets: { viewer: null } }])
|
||||||
|
.then(() => {
|
||||||
|
this.router.navigate([this.previewLocation, id]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onNavigateBefore(): void {
|
||||||
|
const location = this.getFileLocation();
|
||||||
|
|
||||||
|
this.store.dispatch(new ViewNodeAction(this.previousNodeId, location));
|
||||||
|
}
|
||||||
|
|
||||||
|
onNavigateNext(): void {
|
||||||
|
const location = this.getFileLocation();
|
||||||
|
this.store.dispatch(new ViewNodeAction(this.nextNodeId, location));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves nearest node information for the given node and folder.
|
||||||
|
* @param nodeId Unique identifier of the document node
|
||||||
|
* @param folderId Unique identifier of the containing folder node.
|
||||||
|
*/
|
||||||
|
async getNearestNodes(
|
||||||
|
nodeId: string,
|
||||||
|
folderId: string
|
||||||
|
): Promise<{ left: string; right: string }> {
|
||||||
|
const empty = {
|
||||||
|
left: null,
|
||||||
|
right: null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (nodeId && folderId) {
|
||||||
|
try {
|
||||||
|
const ids = await this.getFileIds(this.navigateSource, folderId);
|
||||||
|
const idx = ids.indexOf(nodeId);
|
||||||
|
|
||||||
|
if (idx >= 0) {
|
||||||
|
return {
|
||||||
|
left: ids[idx - 1] || null,
|
||||||
|
right: ids[idx + 1] || null
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of node identifiers for the folder and data source.
|
||||||
|
* @param source Data source name. Allowed values are: personal-files, libraries, favorites, shared, recent-files.
|
||||||
|
* @param folderId Containing folder node identifier for 'personal-files' and 'libraries' sources.
|
||||||
|
*/
|
||||||
|
async getFileIds(source: string, folderId?: string): Promise<string[]> {
|
||||||
|
if ((source === 'personal-files' || source === 'libraries') && folderId) {
|
||||||
|
const sortKey =
|
||||||
|
this.preferences.get('personal-files.sorting.key') || 'modifiedAt';
|
||||||
|
const sortDirection =
|
||||||
|
this.preferences.get('personal-files.sorting.direction') || 'desc';
|
||||||
|
const nodes = await this.contentApi
|
||||||
|
.getNodeChildren(folderId, {
|
||||||
|
// orderBy: `${sortKey} ${sortDirection}`,
|
||||||
|
fields: ['id', this.getRootField(sortKey)],
|
||||||
|
where: '(isFile=true)'
|
||||||
|
})
|
||||||
|
.toPromise();
|
||||||
|
|
||||||
|
const entries = nodes.list.entries.map(obj => obj.entry);
|
||||||
|
this.sort(entries, sortKey, sortDirection);
|
||||||
|
|
||||||
|
return entries.map(obj => obj.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source === 'favorites') {
|
||||||
|
const nodes = await this.contentApi
|
||||||
|
.getFavorites('-me-', {
|
||||||
|
where: '(EXISTS(target/file))',
|
||||||
|
fields: ['target']
|
||||||
|
})
|
||||||
|
.toPromise();
|
||||||
|
|
||||||
|
const sortKey =
|
||||||
|
this.preferences.get('favorites.sorting.key') || 'modifiedAt';
|
||||||
|
const sortDirection =
|
||||||
|
this.preferences.get('favorites.sorting.direction') || 'desc';
|
||||||
|
const files = nodes.list.entries.map(obj => obj.entry.target.file);
|
||||||
|
this.sort(files, sortKey, sortDirection);
|
||||||
|
|
||||||
|
return files.map(f => f.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source === 'shared') {
|
||||||
|
const sortingKey =
|
||||||
|
this.preferences.get('shared.sorting.key') || 'modifiedAt';
|
||||||
|
const sortingDirection =
|
||||||
|
this.preferences.get('shared.sorting.direction') || 'desc';
|
||||||
|
|
||||||
|
const nodes = await this.contentApi
|
||||||
|
.findSharedLinks({
|
||||||
|
fields: ['nodeId', this.getRootField(sortingKey)]
|
||||||
|
})
|
||||||
|
.toPromise();
|
||||||
|
|
||||||
|
const entries = nodes.list.entries.map(obj => obj.entry);
|
||||||
|
this.sort(entries, sortingKey, sortingDirection);
|
||||||
|
|
||||||
|
return entries.map(obj => obj.nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source === 'recent-files') {
|
||||||
|
const person = await this.contentApi.getPerson('-me-').toPromise();
|
||||||
|
const username = person.entry.id;
|
||||||
|
const sortingKey =
|
||||||
|
this.preferences.get('recent-files.sorting.key') || 'modifiedAt';
|
||||||
|
const sortingDirection =
|
||||||
|
this.preferences.get('recent-files.sorting.direction') || 'desc';
|
||||||
|
|
||||||
|
const query: SearchRequest = {
|
||||||
|
query: {
|
||||||
|
query: '*',
|
||||||
|
language: 'afts'
|
||||||
|
},
|
||||||
|
filterQueries: [
|
||||||
|
{ query: `cm:modified:[NOW/DAY-30DAYS TO NOW/DAY+1DAY]` },
|
||||||
|
{ query: `cm:modifier:${username} OR cm:creator:${username}` },
|
||||||
|
{
|
||||||
|
query: this.recentFileFilters.join(' AND ')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
fields: ['id', this.getRootField(sortingKey)],
|
||||||
|
include: ['path', 'properties', 'allowableOperations'],
|
||||||
|
sort: [
|
||||||
|
{
|
||||||
|
type: 'FIELD',
|
||||||
|
field: 'cm:modified',
|
||||||
|
ascending: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const nodes = await this.contentApi.search(query).toPromise();
|
||||||
|
|
||||||
|
const entries = nodes.list.entries.map(obj => obj.entry);
|
||||||
|
this.sort(entries, sortingKey, sortingDirection);
|
||||||
|
|
||||||
|
return entries.map(obj => obj.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private sort(items: any[], key: string, direction: string) {
|
||||||
|
const options: Intl.CollatorOptions = {};
|
||||||
|
|
||||||
|
if (key.includes('sizeInBytes') || key === 'name') {
|
||||||
|
options.numeric = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
items.sort((a: any, b: any) => {
|
||||||
|
let left = ObjectUtils.getValue(a, key);
|
||||||
|
if (left) {
|
||||||
|
left =
|
||||||
|
left instanceof Date ? left.valueOf().toString() : left.toString();
|
||||||
|
} else {
|
||||||
|
left = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let right = ObjectUtils.getValue(b, key);
|
||||||
|
if (right) {
|
||||||
|
right =
|
||||||
|
right instanceof Date ? right.valueOf().toString() : right.toString();
|
||||||
|
} else {
|
||||||
|
right = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return direction === 'asc'
|
||||||
|
? left.localeCompare(right, undefined, options)
|
||||||
|
: right.localeCompare(left, undefined, options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the root field name from the property path.
|
||||||
|
* Example: 'property1.some.child.property' => 'property1'
|
||||||
|
* @param path Property path
|
||||||
|
*/
|
||||||
|
getRootField(path: string) {
|
||||||
|
if (path) {
|
||||||
|
return path.split('.')[0];
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private navigateToFileLocation() {
|
||||||
|
const location = this.getFileLocation();
|
||||||
|
this.router.navigateByUrl(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFileLocation(): string {
|
||||||
|
return this.router
|
||||||
|
.parseUrl(this.router.url)
|
||||||
|
.root.children[PRIMARY_OUTLET].toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,10 @@ import { AppViewerComponent } from './viewer.component';
|
|||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
|
data: {
|
||||||
|
title: 'APP.PREVIEW.TITLE',
|
||||||
|
navigateMultiple: true
|
||||||
|
},
|
||||||
component: AppViewerComponent
|
component: AppViewerComponent
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -24,9 +24,135 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { DocumentListDirective } from './document-list.directive';
|
import { DocumentListDirective } from './document-list.directive';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { SetSelectedNodesAction } from '@alfresco/aca-shared/store';
|
||||||
|
|
||||||
describe('DocumentListDirective', () => {
|
describe('DocumentListDirective', () => {
|
||||||
it('should be defined', () => {
|
let documentListDirective;
|
||||||
expect(DocumentListDirective).toBeDefined();
|
|
||||||
|
const documentListMock = <any>{
|
||||||
|
currentFolderId: '',
|
||||||
|
stickyHeader: false,
|
||||||
|
includeFields: [],
|
||||||
|
sorting: [],
|
||||||
|
data: {
|
||||||
|
setSorting: jasmine.createSpy('setSorting')
|
||||||
|
},
|
||||||
|
selection: [],
|
||||||
|
reload: jasmine.createSpy('reload'),
|
||||||
|
resetSelection: jasmine.createSpy('resetSelection'),
|
||||||
|
ready: new Subject<any>()
|
||||||
|
};
|
||||||
|
|
||||||
|
const storeMock = <any>{
|
||||||
|
dispatch: jasmine.createSpy('dispatch')
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockRouter = <any>{
|
||||||
|
url: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
const contentManagementServiceMock = <any>{
|
||||||
|
reload: new Subject<any>(),
|
||||||
|
reset: new Subject<any>()
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockRoute = <any>{
|
||||||
|
snapshot: {
|
||||||
|
data: {
|
||||||
|
sortingPreferenceKey: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const userPreferencesServiceMock = <any>{
|
||||||
|
set: jasmine.createSpy('set'),
|
||||||
|
get: jasmine.createSpy('get')
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
documentListDirective = new DocumentListDirective(
|
||||||
|
storeMock,
|
||||||
|
contentManagementServiceMock,
|
||||||
|
documentListMock,
|
||||||
|
userPreferencesServiceMock,
|
||||||
|
mockRoute,
|
||||||
|
mockRouter
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
storeMock.dispatch.calls.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update store selection on `documentList.ready` if route includes `viewer:view`', () => {
|
||||||
|
mockRouter.url = '/some-route/(viewer:view)';
|
||||||
|
documentListDirective.ngOnInit();
|
||||||
|
documentListMock.ready.next();
|
||||||
|
|
||||||
|
expect(storeMock.dispatch).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update store selection on `documentList.ready`', () => {
|
||||||
|
mockRouter.url = '/some-route';
|
||||||
|
documentListDirective.ngOnInit();
|
||||||
|
documentListMock.ready.next();
|
||||||
|
|
||||||
|
expect(storeMock.dispatch).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set `isLibrary` to true if selected node is a library', () => {
|
||||||
|
mockRouter.url = '/some-route';
|
||||||
|
documentListMock.currentFolderId = '-mysites-';
|
||||||
|
documentListMock.selection = [{}];
|
||||||
|
documentListDirective.ngOnInit();
|
||||||
|
documentListMock.ready.next();
|
||||||
|
|
||||||
|
expect(storeMock.dispatch).toHaveBeenCalledWith(
|
||||||
|
new SetSelectedNodesAction([<any>{ isLibrary: true }])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update store selection on `node-unselect` event', () => {
|
||||||
|
mockRouter.url = '/some-route';
|
||||||
|
documentListDirective.ngOnInit();
|
||||||
|
documentListDirective.onNodeUnselect();
|
||||||
|
|
||||||
|
expect(storeMock.dispatch).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update store selection on `node-select` event', () => {
|
||||||
|
mockRouter.url = '/some-route';
|
||||||
|
documentListDirective.ngOnInit();
|
||||||
|
documentListDirective.onNodeSelect({ detail: { node: {} } });
|
||||||
|
|
||||||
|
expect(storeMock.dispatch).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset and reload document list on `reload` event', () => {
|
||||||
|
documentListDirective.ngOnInit();
|
||||||
|
contentManagementServiceMock.reload.next();
|
||||||
|
|
||||||
|
expect(documentListMock.resetSelection).toHaveBeenCalled();
|
||||||
|
expect(documentListMock.reload).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset store selection on `reload` event', () => {
|
||||||
|
documentListDirective.ngOnInit();
|
||||||
|
contentManagementServiceMock.reload.next();
|
||||||
|
|
||||||
|
expect(storeMock.dispatch).toHaveBeenCalledWith(
|
||||||
|
new SetSelectedNodesAction([])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset store selection and document list on `reset` event', () => {
|
||||||
|
documentListDirective.ngOnInit();
|
||||||
|
contentManagementServiceMock.reload.next();
|
||||||
|
|
||||||
|
expect(documentListMock.resetSelection).toHaveBeenCalled();
|
||||||
|
expect(storeMock.dispatch).toHaveBeenCalledWith(
|
||||||
|
new SetSelectedNodesAction([])
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -30,7 +30,7 @@ import { UserPreferencesService } from '@alfresco/adf-core';
|
|||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { SetSelectedNodesAction } from '@alfresco/aca-shared/store';
|
import { SetSelectedNodesAction } from '@alfresco/aca-shared/store';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil, filter } from 'rxjs/operators';
|
||||||
import { ContentManagementService } from '../services/content-management.service';
|
import { ContentManagementService } from '../services/content-management.service';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
@@ -81,12 +81,19 @@ export class DocumentListDirective implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.documentList.ready
|
this.documentList.ready
|
||||||
.pipe(takeUntil(this.onDestroy$))
|
.pipe(
|
||||||
|
filter(() => !this.router.url.includes('viewer:view')),
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
)
|
||||||
.subscribe(() => this.onReady());
|
.subscribe(() => this.onReady());
|
||||||
|
|
||||||
this.content.reload.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
|
this.content.reload.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
|
||||||
this.reload();
|
this.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.content.reset.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
|
||||||
|
this.reset();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@@ -138,4 +145,9 @@ export class DocumentListDirective implements OnInit, OnDestroy {
|
|||||||
this.store.dispatch(new SetSelectedNodesAction([]));
|
this.store.dispatch(new SetSelectedNodesAction([]));
|
||||||
this.documentList.reload();
|
this.documentList.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private reset() {
|
||||||
|
this.documentList.resetSelection();
|
||||||
|
this.store.dispatch(new SetSelectedNodesAction([]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,6 +50,7 @@ import {
|
|||||||
LibraryRoleColumnComponent
|
LibraryRoleColumnComponent
|
||||||
} from '@alfresco/adf-content-services';
|
} from '@alfresco/adf-content-services';
|
||||||
import { ToggleSharedComponent } from '../components/common/toggle-shared/toggle-shared.component';
|
import { ToggleSharedComponent } from '../components/common/toggle-shared/toggle-shared.component';
|
||||||
|
import { ViewNodeComponent } from '../components/toolbar/view-node/view-node.component';
|
||||||
|
|
||||||
export function setupExtensions(service: AppExtensionService): Function {
|
export function setupExtensions(service: AppExtensionService): Function {
|
||||||
return () => service.load();
|
return () => service.load();
|
||||||
@@ -99,7 +100,8 @@ export class CoreExtensionsModule {
|
|||||||
'app.columns.libraryStatus': LibraryStatusColumnComponent,
|
'app.columns.libraryStatus': LibraryStatusColumnComponent,
|
||||||
'app.columns.trashcanName': TrashcanNameColumnComponent,
|
'app.columns.trashcanName': TrashcanNameColumnComponent,
|
||||||
'app.columns.location': LocationLinkComponent,
|
'app.columns.location': LocationLinkComponent,
|
||||||
'app.toolbar.toggleEditOffline': ToggleEditOfflineComponent
|
'app.toolbar.toggleEditOffline': ToggleEditOfflineComponent,
|
||||||
|
'app.toolbar.viewNode': ViewNodeComponent
|
||||||
});
|
});
|
||||||
|
|
||||||
extensions.setAuthGuards({
|
extensions.setAuthGuards({
|
||||||
|
@@ -82,6 +82,7 @@ interface RestoredNode {
|
|||||||
})
|
})
|
||||||
export class ContentManagementService {
|
export class ContentManagementService {
|
||||||
reload = new Subject<any>();
|
reload = new Subject<any>();
|
||||||
|
reset = new Subject<any>();
|
||||||
nodesDeleted = new Subject<any>();
|
nodesDeleted = new Subject<any>();
|
||||||
libraryDeleted = new Subject<string>();
|
libraryDeleted = new Subject<string>();
|
||||||
libraryCreated = new Subject<SiteEntry>();
|
libraryCreated = new Subject<SiteEntry>();
|
||||||
|
@@ -29,7 +29,8 @@ import { map } from 'rxjs/operators';
|
|||||||
import {
|
import {
|
||||||
AppActionTypes,
|
AppActionTypes,
|
||||||
LogoutAction,
|
LogoutAction,
|
||||||
ReloadDocumentListAction
|
ReloadDocumentListAction,
|
||||||
|
ResetSelectionAction
|
||||||
} from '@alfresco/aca-shared/store';
|
} from '@alfresco/aca-shared/store';
|
||||||
import { AuthenticationService } from '@alfresco/adf-core';
|
import { AuthenticationService } from '@alfresco/adf-core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
@@ -52,6 +53,14 @@ export class AppEffects {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@Effect({ dispatch: false })
|
||||||
|
resetSelection = this.actions$.pipe(
|
||||||
|
ofType<ResetSelectionAction>(AppActionTypes.ResetSelection),
|
||||||
|
map(action => {
|
||||||
|
this.content.reset.next(action);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
@Effect({ dispatch: false })
|
@Effect({ dispatch: false })
|
||||||
logout$ = this.actions$.pipe(
|
logout$ = this.actions$.pipe(
|
||||||
ofType<LogoutAction>(AppActionTypes.Logout),
|
ofType<LogoutAction>(AppActionTypes.Logout),
|
||||||
|
95
src/app/store/effects/viewer.effects.spec.ts
Normal file
95
src/app/store/effects/viewer.effects.spec.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/*!
|
||||||
|
* @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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||||
|
import { AppTestingModule } from '../../testing/app-testing.module';
|
||||||
|
import { ViewerEffects } from './viewer.effects';
|
||||||
|
import { EffectsModule } from '@ngrx/effects';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import {
|
||||||
|
ViewFileAction,
|
||||||
|
ViewNodeAction,
|
||||||
|
SetSelectedNodesAction,
|
||||||
|
SetCurrentFolderAction
|
||||||
|
} from '@alfresco/aca-shared/store';
|
||||||
|
|
||||||
|
describe('ViewerEffects', () => {
|
||||||
|
let store: Store<any>;
|
||||||
|
let router: Router;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [AppTestingModule, EffectsModule.forRoot([ViewerEffects])]
|
||||||
|
});
|
||||||
|
|
||||||
|
store = TestBed.get(Store);
|
||||||
|
router = TestBed.get(Router);
|
||||||
|
|
||||||
|
spyOn(router, 'navigateByUrl').and.stub();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ViewFile', () => {
|
||||||
|
it('should preview file from store selection', fakeAsync(() => {
|
||||||
|
const node: any = { entry: { isFile: true, id: 'someId' } };
|
||||||
|
const folder: any = { isFolder: true, id: 'folder1' };
|
||||||
|
store.dispatch(new SetCurrentFolderAction(folder));
|
||||||
|
store.dispatch(new SetSelectedNodesAction([node]));
|
||||||
|
tick(100);
|
||||||
|
|
||||||
|
store.dispatch(new ViewFileAction());
|
||||||
|
tick(100);
|
||||||
|
expect(router.navigateByUrl).toHaveBeenCalledWith(
|
||||||
|
'/folder1/preview/someId'
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
it('should preview file from payload', fakeAsync(() => {
|
||||||
|
const node: any = { entry: { isFile: true, id: 'someId' } };
|
||||||
|
store.dispatch(new ViewFileAction(node));
|
||||||
|
tick(100);
|
||||||
|
expect(router.navigateByUrl).toHaveBeenCalledWith('/preview/someId');
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ViewNode', () => {
|
||||||
|
it('should open viewer from file location', fakeAsync(() => {
|
||||||
|
store.dispatch(new ViewNodeAction('nodeId', 'some-location'));
|
||||||
|
tick(100);
|
||||||
|
|
||||||
|
expect(router.navigateByUrl['calls'].argsFor(0)[0].toString()).toEqual(
|
||||||
|
'/some-location/(viewer:view/nodeId)?source=some-location'
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should navigate to viewer route if no location is passed', fakeAsync(() => {
|
||||||
|
store.dispatch(new ViewNodeAction('nodeId'));
|
||||||
|
tick(100);
|
||||||
|
|
||||||
|
expect(router.navigateByUrl['calls'].argsFor(0)[0].toString()).toEqual(
|
||||||
|
'/view/(viewer:nodeId)'
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
@@ -35,7 +35,13 @@ import {
|
|||||||
getAppSelection,
|
getAppSelection,
|
||||||
FullscreenViewerAction
|
FullscreenViewerAction
|
||||||
} from '@alfresco/aca-shared/store';
|
} from '@alfresco/aca-shared/store';
|
||||||
import { Router } from '@angular/router';
|
import {
|
||||||
|
Router,
|
||||||
|
UrlTree,
|
||||||
|
UrlSegmentGroup,
|
||||||
|
PRIMARY_OUTLET,
|
||||||
|
UrlSegment
|
||||||
|
} from '@angular/router';
|
||||||
import { Store, createSelector } from '@ngrx/store';
|
import { Store, createSelector } from '@ngrx/store';
|
||||||
import { AppExtensionService } from '../../extensions/extension.service';
|
import { AppExtensionService } from '../../extensions/extension.service';
|
||||||
|
|
||||||
@@ -72,8 +78,10 @@ export class ViewerEffects {
|
|||||||
ofType<ViewNodeAction>(ViewerActionTypes.ViewNode),
|
ofType<ViewNodeAction>(ViewerActionTypes.ViewNode),
|
||||||
map(action => {
|
map(action => {
|
||||||
if (action.location) {
|
if (action.location) {
|
||||||
|
const location = this.getNavigationCommands(action.location);
|
||||||
|
|
||||||
this.router.navigate(
|
this.router.navigate(
|
||||||
[action.location, { outlets: { viewer: ['view', action.nodeId] } }],
|
[...location, { outlets: { viewer: ['view', action.nodeId] } }],
|
||||||
{
|
{
|
||||||
queryParams: {
|
queryParams: {
|
||||||
source: action.location
|
source: action.location
|
||||||
@@ -163,4 +171,21 @@ export class ViewerEffects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getNavigationCommands(url: string): any[] {
|
||||||
|
const urlTree: UrlTree = this.router.parseUrl(url);
|
||||||
|
const urlSegmentGroup: UrlSegmentGroup =
|
||||||
|
urlTree.root.children[PRIMARY_OUTLET];
|
||||||
|
|
||||||
|
if (!urlSegmentGroup) {
|
||||||
|
return [url];
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlSegments: UrlSegment[] = urlSegmentGroup.segments;
|
||||||
|
|
||||||
|
return urlSegments.reduce(function(acc, item) {
|
||||||
|
acc.push(item.path, item.parameters);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -221,12 +221,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "app.toolbar.preview",
|
"id": "app.toolbar.preview",
|
||||||
|
"type": "custom",
|
||||||
"order": 300,
|
"order": 300,
|
||||||
|
"data": {
|
||||||
"title": "APP.ACTIONS.VIEW",
|
"title": "APP.ACTIONS.VIEW",
|
||||||
"icon": "visibility",
|
"iconButton": true
|
||||||
"actions": {
|
|
||||||
"click": "VIEW_FILE"
|
|
||||||
},
|
},
|
||||||
|
"component": "app.toolbar.viewNode",
|
||||||
"rules": {
|
"rules": {
|
||||||
"visible": "canViewFile"
|
"visible": "canViewFile"
|
||||||
}
|
}
|
||||||
@@ -504,12 +505,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "app.context.menu.preview",
|
"id": "app.context.menu.preview",
|
||||||
|
"type": "custom",
|
||||||
"order": 300,
|
"order": 300,
|
||||||
|
"data": {
|
||||||
"title": "APP.ACTIONS.VIEW",
|
"title": "APP.ACTIONS.VIEW",
|
||||||
"icon": "visibility",
|
"menuButton": true
|
||||||
"actions": {
|
|
||||||
"click": "VIEW_FILE"
|
|
||||||
},
|
},
|
||||||
|
"component": "app.toolbar.viewNode",
|
||||||
"rules": {
|
"rules": {
|
||||||
"visible": "canViewFile"
|
"visible": "canViewFile"
|
||||||
}
|
}
|
||||||
@@ -581,6 +583,7 @@
|
|||||||
"comment": "workaround for Recent Files and Search API issue",
|
"comment": "workaround for Recent Files and Search API issue",
|
||||||
"type": "custom",
|
"type": "custom",
|
||||||
"order": 802,
|
"order": 802,
|
||||||
|
"data": "['/favorites', '/favorite/libraries']",
|
||||||
"component": "app.toolbar.toggleFavorite",
|
"component": "app.toolbar.toggleFavorite",
|
||||||
"rules": {
|
"rules": {
|
||||||
"visible": "canToggleFavorite"
|
"visible": "canToggleFavorite"
|
||||||
|
Reference in New Issue
Block a user