diff --git a/deploy/app.js b/deploy/app.js index dbd94662acbc6966556397751ce98c767f98082a..67c350ee2dc46fc19e180983eff6e29590c9abd0 100644 --- a/deploy/app.js +++ b/deploy/app.js @@ -92,6 +92,11 @@ app.get('/', (req, res) => { res.status(200).send(`${indexTemplate.replace(/\$\$NONCE\$\$/g, res.locals.nonce)}`) }) +/** + * User route, for user profile/management + */ +app.use('/user', require('./user')) + /** * only use compression for production * this allows locally built aot to be served without errors diff --git a/deploy/auth/hbp-oidc-v2.js b/deploy/auth/hbp-oidc-v2.js new file mode 100644 index 0000000000000000000000000000000000000000..a49018f85a4c34d38021356989302cf445ffc176 --- /dev/null +++ b/deploy/auth/hbp-oidc-v2.js @@ -0,0 +1,44 @@ +const passport = require('passport') +const { configureAuth } = require('./oidc') + +const HOSTNAME = process.env.HOSTNAME || 'http://localhost:3000' +const HOST_PATHNAME = process.env.HOST_PATHNAME || '' +const clientId = process.env.HBP_CLIENTID_V2 || 'no hbp id' +const clientSecret = process.env.HBP_CLIENTSECRET_V2 || 'no hbp client secret' +const discoveryUrl = 'https://iam.humanbrainproject.eu/auth/realms/hbp' +const redirectUri = `${HOSTNAME}${HOST_PATHNAME}/hbp-oidc-v2/cb` +const cb = (tokenset, {sub, given_name, family_name, ...rest}, done) => { + return done(null, { + id: `hbp-oidc-v2:${sub}`, + name: `${given_name} ${family_name}`, + type: `hbp-oidc-v2`, + tokenset, + rest + }) +} + +module.exports = async (app) => { + try { + const { oidcStrategy } = await configureAuth({ + clientId, + clientSecret, + discoveryUrl, + redirectUri, + cb, + scope: 'openid email offline_access profile collab.drive', + clientConfig: { + redirect_uris: [ redirectUri ], + response_types: [ 'code' ] + } + }) + + passport.use('hbp-oidc-v2', oidcStrategy) + app.get('/hbp-oidc-v2/auth', passport.authenticate('hbp-oidc-v2')) + app.get('/hbp-oidc-v2/cb', passport.authenticate('hbp-oidc-v2', { + successRedirect: `${HOST_PATHNAME}/`, + failureRedirect: `${HOST_PATHNAME}/` + })) + } catch (e) { + console.error(e) + } +} diff --git a/deploy/auth/index.js b/deploy/auth/index.js index 0ad0941dfe9804a442d131130410d6a4098acaea..a2e6c45640632b543fbd86190c4c54956a51c9b8 100644 --- a/deploy/auth/index.js +++ b/deploy/auth/index.js @@ -1,4 +1,5 @@ const hbpOidc = require('./hbp-oidc') +const hbpOidc2 = require('./hbp-oidc-v2') const passport = require('passport') const objStoreDb = new Map() const HOST_PATHNAME = process.env.HOST_PATHNAME || '' @@ -20,14 +21,7 @@ module.exports = async (app) => { }) await hbpOidc(app) - - app.get('/user', (req, res) => { - if (req.user) { - return res.status(200).send(JSON.stringify(req.user)) - } else { - return res.status(401).end() - } - }) + await hbpOidc2(app) app.get('/logout', (req, res) => { if (req.user && req.user.id) objStoreDb.delete(req.user.id) diff --git a/deploy/package.json b/deploy/package.json index 179368f6174b0b2019cb64a816b6954b38b94f83..61322a47263a032086c2dbebd3eb7bacb61aefce 100644 --- a/deploy/package.json +++ b/deploy/package.json @@ -19,6 +19,7 @@ "body-parser": "^1.19.0", "express": "^4.16.4", "express-session": "^1.15.6", + "hbp-seafile": "0.0.6", "helmet-csp": "^2.8.0", "jwt-decode": "^2.2.0", "memorystore": "^1.6.1", diff --git a/deploy/user/index.js b/deploy/user/index.js new file mode 100644 index 0000000000000000000000000000000000000000..9d1809c963bfa07e6f518991f567f0b4e5af4137 --- /dev/null +++ b/deploy/user/index.js @@ -0,0 +1,37 @@ +const router = require('express').Router() +const { readUserData, saveUserData } = require('./store') +const bodyParser = require('body-parser') + +const loggedInOnlyMiddleware = (req, res, next) => { + const { user } = req + if (!user) return res.status(401).end() + return next() +} + +router.get('', loggedInOnlyMiddleware, (req, res) => { + return res.status(200).send(JSON.stringify(req.user)) +}) + +router.get('/config', loggedInOnlyMiddleware, async (req, res) => { + const { user } = req + try{ + const data = await readUserData(user) + res.status(200).json(data) + } catch (e){ + console.error(e) + res.status(500).send(e.toString()) + } +}) + +router.post('/config', loggedInOnlyMiddleware, bodyParser.json(), async (req, res) => { + const { user, body } = req + try { + await saveUserData(user, body) + res.status(200).end() + } catch (e) { + console.error(e) + res.status(500).send(e.toString()) + } +}) + +module.exports = router \ No newline at end of file diff --git a/deploy/user/store.js b/deploy/user/store.js new file mode 100644 index 0000000000000000000000000000000000000000..93c9db2b1adce8baa58ecf4fca6bfecaecfc650c --- /dev/null +++ b/deploy/user/store.js @@ -0,0 +1,68 @@ +const { Seafile } = require('hbp-seafile') +const { Readable } = require('stream') + +const IAV_DIR_NAME = `interactive-atlas-viewer` +const IAV_DIRECTORY = `/${IAV_DIR_NAME}/` +const IAV_FILENAME = 'data.json' + +const getNewSeafilehandle = async ({ accessToken }) => { + const seafileHandle = new Seafile({ accessToken }) + await seafileHandle.init() + return seafileHandle +} + +const saveUserData = async (user, data) => { + const { access_token } = user && user.tokenset || {} + if (!access_token) throw new Error(`user or user.tokenset not set can only save logged in user data`) + + let handle = await getNewSeafilehandle({ accessToken: access_token }) + + const s = await handle.ls() + const found = s.find(({ type, name }) => type === 'dir' && name === IAV_DIR_NAME) + + // if dir exists, check permission. throw if no writable or readable permission + if (found && !/w/.test(found.permission) && !/r/.test(found.permission)){ + throw new Error(`Writing to file not permitted. Current permission: ${found.permission}`) + } + + // create new dir if does not exist. Should have rw permission + if (!found) { + await handle.mkdir({ dir: IAV_DIR_NAME }) + } + + const fileLs = await handle.ls({ dir: IAV_DIRECTORY }) + const fileFound = fileLs.find(({ type, name }) => type === 'file' && name === IAV_FILENAME ) + + const rStream = new Readable() + rStream.path = IAV_FILENAME + rStream.push(JSON.stringify(data)) + rStream.push(null) + + if(!fileFound) { + return handle.uploadFile({ readStream: rStream, filename: `${IAV_FILENAME}` }, { dir: IAV_DIRECTORY }) + } + + if (fileFound && !/w/.test(fileFound.permission)) { + return new Error('file permission cannot be written') + } + + return handle.updateFile({ dir: IAV_DIRECTORY, replaceFilepath: `${IAV_DIRECTORY}${IAV_FILENAME}` }, { readStream: rStream, filename: IAV_FILENAME }) +} + +const readUserData = async (user) => { + const { access_token } = user && user.tokenset || {} + if (!access_token) throw new Error(`user or user.tokenset not set can only save logged in user data`) + + let handle = await getNewSeafilehandle({ accessToken: access_token }) + try { + const r = await handle.readFile({ dir: `${IAV_DIRECTORY}${IAV_FILENAME}` }) + return JSON.parse(r) + }catch(e){ + return {} + } +} + +module.exports = { + saveUserData, + readUserData +} diff --git a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-100.png b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-100.png index f9a9fb0e291661a457f9492df96a50462999969d..1c1b7c0551c9fc58c556dbf1d1165e7fb4eb2082 100644 Binary files a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-100.png and b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-100.png differ diff --git a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-200.png b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-200.png index 36c143ae11e00675c45d16c5a95457a0b5880b43..c498a3400f0fdf1d6655fb39105b2539154aead7 100644 Binary files a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-200.png and b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-200.png differ diff --git a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-300.png b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-300.png index 52f57e5412d618286dfd716fce13b13cef10741d..45f4ebfd1862c803bab85ec6cae263269413d503 100644 Binary files a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-300.png and b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-300.png differ diff --git a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-400.png b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-400.png index df6e4acd8be3ae0cccca705dc824932e70eefd05..169ac5d905616ba9695d8e17b435d7115c42defe 100644 Binary files a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-400.png and b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015-400.png differ diff --git a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015.png b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015.png index 897168863448a2efce76cd5496cd4154cc48545e..6e8dd6a421d873a5245886b7f549bef596ac8631 100644 Binary files a/src/res/images/AllenMouseCommonCoordinateFrameworkv32015.png and b/src/res/images/AllenMouseCommonCoordinateFrameworkv32015.png differ diff --git a/src/res/images/BigBrainHistology-100.png b/src/res/images/BigBrainHistology-100.png index 50559bfb8359d5a71eb2a82dc276f72f80951f7a..491b38f62572693241225478d6b358bc3446a2e7 100644 Binary files a/src/res/images/BigBrainHistology-100.png and b/src/res/images/BigBrainHistology-100.png differ diff --git a/src/res/images/BigBrainHistology-200.png b/src/res/images/BigBrainHistology-200.png index a17a7f83393e0a57dfe80afad199150b16e605d4..973cc48c4837d60addda9df44f8162ac9090a7c8 100644 Binary files a/src/res/images/BigBrainHistology-200.png and b/src/res/images/BigBrainHistology-200.png differ diff --git a/src/res/images/BigBrainHistology-300.png b/src/res/images/BigBrainHistology-300.png index f7cd0b28ec4cbbc78488799057878e1e8225215f..7e45f517fe01be30e919ef22d492a38ffb8d319a 100644 Binary files a/src/res/images/BigBrainHistology-300.png and b/src/res/images/BigBrainHistology-300.png differ diff --git a/src/res/images/BigBrainHistology-400.png b/src/res/images/BigBrainHistology-400.png index 7d8fea17c6d8dd5b3075df22c1f3ce9054275abb..31b225679dffeab9242870d25b8ded9fd9752cea 100644 Binary files a/src/res/images/BigBrainHistology-400.png and b/src/res/images/BigBrainHistology-400.png differ diff --git a/src/res/images/BigBrainHistology.png b/src/res/images/BigBrainHistology.png index 94cc67bd8cae7c3701d9c3c6c15f2dc9be8400b6..196cb72a548674d2e5b1bf807583f03bddba7447 100644 Binary files a/src/res/images/BigBrainHistology.png and b/src/res/images/BigBrainHistology.png differ diff --git a/src/res/images/ICBM2009cNonlinearAsymmetric-100.png b/src/res/images/ICBM2009cNonlinearAsymmetric-100.png index c8912fe70f594f0a0d60ad7596802ad690166c56..6017f988f33bb52b8b89ce58de5ce6ff3b185a58 100644 Binary files a/src/res/images/ICBM2009cNonlinearAsymmetric-100.png and b/src/res/images/ICBM2009cNonlinearAsymmetric-100.png differ diff --git a/src/res/images/ICBM2009cNonlinearAsymmetric-200.png b/src/res/images/ICBM2009cNonlinearAsymmetric-200.png index 7f38858407d751d4e5b1a200ed310d2c81cb6e07..cc6c3e1907ed116b3a5e8bc3a0e1ca2b28a1dddb 100644 Binary files a/src/res/images/ICBM2009cNonlinearAsymmetric-200.png and b/src/res/images/ICBM2009cNonlinearAsymmetric-200.png differ diff --git a/src/res/images/ICBM2009cNonlinearAsymmetric-300.png b/src/res/images/ICBM2009cNonlinearAsymmetric-300.png index 481339124d296586ec68455fd0298aed2b392e2b..9891303c6fa9a9b333757781fbd6db6b06b082b4 100644 Binary files a/src/res/images/ICBM2009cNonlinearAsymmetric-300.png and b/src/res/images/ICBM2009cNonlinearAsymmetric-300.png differ diff --git a/src/res/images/ICBM2009cNonlinearAsymmetric-400.png b/src/res/images/ICBM2009cNonlinearAsymmetric-400.png index 0ce95ddc0988de4f48ce8cdcda9c6b0d98b66a79..36a665a1089f5cc739a8c35669ee04387235c48a 100644 Binary files a/src/res/images/ICBM2009cNonlinearAsymmetric-400.png and b/src/res/images/ICBM2009cNonlinearAsymmetric-400.png differ diff --git a/src/res/images/ICBM2009cNonlinearAsymmetric.png b/src/res/images/ICBM2009cNonlinearAsymmetric.png index a5d2276eff480aefe8ab7116c500d82b1201fbfa..4ceb2a5dc1782c3f6bfea540115ed60133157f10 100644 Binary files a/src/res/images/ICBM2009cNonlinearAsymmetric.png and b/src/res/images/ICBM2009cNonlinearAsymmetric.png differ diff --git a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-100.png b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-100.png index 859167bec29489f7e787d3820081e8d21d69564d..6017f988f33bb52b8b89ce58de5ce6ff3b185a58 100644 Binary files a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-100.png and b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-100.png differ diff --git a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-200.png b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-200.png index 5c9e3d78e789eefef7e608d5807713f7d3a59769..cc6c3e1907ed116b3a5e8bc3a0e1ca2b28a1dddb 100644 Binary files a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-200.png and b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-200.png differ diff --git a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-300.png b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-300.png index 29c1ea8f17d80fc7c850fcca49964791dc96ea15..9891303c6fa9a9b333757781fbd6db6b06b082b4 100644 Binary files a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-300.png and b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-300.png differ diff --git a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-400.png b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-400.png index 07b8475e00616e3235955d65cbc3598f0bd82e82..36a665a1089f5cc739a8c35669ee04387235c48a 100644 Binary files a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-400.png and b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric-400.png differ diff --git a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric.png b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric.png index a5d2276eff480aefe8ab7116c500d82b1201fbfa..4ceb2a5dc1782c3f6bfea540115ed60133157f10 100644 Binary files a/src/res/images/MNI152ICBM2009cNonlinearAsymmetric.png and b/src/res/images/MNI152ICBM2009cNonlinearAsymmetric.png differ diff --git a/src/res/images/MNIColin27-100.png b/src/res/images/MNIColin27-100.png index d4748838a9e7d341efe95a9823fa7edca0b6126a..bbad31958b0e3f63daaae893458eba6dcc42eeb0 100644 Binary files a/src/res/images/MNIColin27-100.png and b/src/res/images/MNIColin27-100.png differ diff --git a/src/res/images/MNIColin27-200.png b/src/res/images/MNIColin27-200.png index 683341c65daaa5170e77197e614acbcaa3e48f5b..0930bf9602385e92ad63db342fc294134a129d8b 100644 Binary files a/src/res/images/MNIColin27-200.png and b/src/res/images/MNIColin27-200.png differ diff --git a/src/res/images/MNIColin27-300.png b/src/res/images/MNIColin27-300.png index d55090287e9925d795fbf6772024e6cb81100563..877b80eb63077be4a58702ffb358ce6c397a077b 100644 Binary files a/src/res/images/MNIColin27-300.png and b/src/res/images/MNIColin27-300.png differ diff --git a/src/res/images/MNIColin27-400.png b/src/res/images/MNIColin27-400.png index 6bd9fd61b6ab6bde92c61cd035bb0999ecf1ed3b..9c2cfec121a36b827672dc46997e1248646fdde7 100644 Binary files a/src/res/images/MNIColin27-400.png and b/src/res/images/MNIColin27-400.png differ diff --git a/src/res/images/MNIColin27.png b/src/res/images/MNIColin27.png index 6993e50d6ef7484530a7df17b8ca5bee7bdac249..928bbf1b6832a221b64a36052af8d6eef6afca39 100644 Binary files a/src/res/images/MNIColin27.png and b/src/res/images/MNIColin27.png differ diff --git a/src/res/images/WaxholmSpaceratbrainMRIDTI-100.png b/src/res/images/WaxholmSpaceratbrainMRIDTI-100.png index e1eddc7bfbc2e46b4c4bcf1146558a27d277c57b..178ebbab4cdc5f608f56f758f42ba51d486cd17f 100644 Binary files a/src/res/images/WaxholmSpaceratbrainMRIDTI-100.png and b/src/res/images/WaxholmSpaceratbrainMRIDTI-100.png differ diff --git a/src/res/images/WaxholmSpaceratbrainMRIDTI-200.png b/src/res/images/WaxholmSpaceratbrainMRIDTI-200.png index 938d3c00746f6c4a1eefa6468715a8af0e205cf0..521e7b8dc678879a5a0282f2ba30b03373db3b40 100644 Binary files a/src/res/images/WaxholmSpaceratbrainMRIDTI-200.png and b/src/res/images/WaxholmSpaceratbrainMRIDTI-200.png differ diff --git a/src/res/images/WaxholmSpaceratbrainMRIDTI-300.png b/src/res/images/WaxholmSpaceratbrainMRIDTI-300.png index 37e7635b5b55e317455e12d727654b89d872067a..038b3fdf7f7a6aa737a75346d563030d2df2b80e 100644 Binary files a/src/res/images/WaxholmSpaceratbrainMRIDTI-300.png and b/src/res/images/WaxholmSpaceratbrainMRIDTI-300.png differ diff --git a/src/res/images/WaxholmSpaceratbrainMRIDTI-400.png b/src/res/images/WaxholmSpaceratbrainMRIDTI-400.png index 57840e545f0f1804104c93cd09fb65bb16813e4b..acb3875861b20bdf14b23cc23067ac40c3f6dc3e 100644 Binary files a/src/res/images/WaxholmSpaceratbrainMRIDTI-400.png and b/src/res/images/WaxholmSpaceratbrainMRIDTI-400.png differ diff --git a/src/res/images/WaxholmSpaceratbrainMRIDTI.png b/src/res/images/WaxholmSpaceratbrainMRIDTI.png index 0a2b6cef1d493cad67ab3850d0bdd1969e7a6e21..9c3130dc6c2f4dcd57b168bb1f43a2aa91564a80 100644 Binary files a/src/res/images/WaxholmSpaceratbrainMRIDTI.png and b/src/res/images/WaxholmSpaceratbrainMRIDTI.png differ diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 81add5950f138e5be60b233086dc4f0ddf9dd9b8..fdec1e6ef5ec008b4d4bb01f8a3897ff4c482934 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -19,7 +19,10 @@ export class AuthService implements OnDestroy { */ public loginMethods: IAuthMethod[] = [{ name: 'HBP OIDC', - href: 'hbp-oidc/auth', + href: 'hbp-oidc/auth' + }, { + name: 'HBP OIDC v2 (beta)', + href: 'hbp-oidc-v2/auth' }] constructor(private httpClient: HttpClient) { diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts index 53fe04a19c044f2bf9a02a03db8edfd8a051fa1e..1e93b4ee8e44bba5aec691c20fde79f87165a301 100644 --- a/src/services/state/ngViewerState.store.ts +++ b/src/services/state/ngViewerState.store.ts @@ -1,11 +1,11 @@ import { Injectable, OnDestroy } from '@angular/core'; -import { Actions, Effect, ofType } from '@ngrx/effects'; -import { Action, select, Store } from '@ngrx/store' -import { combineLatest, fromEvent, Observable, Subscription } from 'rxjs'; -import { distinctUntilChanged, filter, map, mapTo, scan, shareReplay, withLatestFrom } from 'rxjs/operators'; +import { Observable, combineLatest, fromEvent, Subscription, from, of } from 'rxjs'; +import { Effect, Actions, ofType } from '@ngrx/effects'; +import { withLatestFrom, map, distinctUntilChanged, scan, shareReplay, filter, mapTo, debounceTime, catchError, skip, throttleTime } from 'rxjs/operators'; import { AtlasViewerConstantsServices } from 'src/atlasViewer/atlasViewer.constantService.service'; -import { getNgIds, IavRootStoreInterface } from '../stateStore.service'; import { SNACKBAR_MESSAGE } from './uiState.store'; +import { getNgIds, IavRootStoreInterface, GENERAL_ACTION_TYPES } from '../stateStore.service'; +import { Action, select, Store } from '@ngrx/store' export const FOUR_PANEL = 'FOUR_PANEL' export const V_ONE_THREE = 'V_ONE_THREE' @@ -132,14 +132,72 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Stat ...prevState, forceShowSegment : action.forceShowSegment, } - case NEHUBA_READY: { - const { nehubaReady } = action - return { - ...prevState, - nehubaReady, - } - } - default: return prevState + case ADD_NG_LAYER: + return { + ...prevState, + + /* this configration hides the layer if a non mixable layer already present */ + + /* this configuration does not the addition of multiple non mixable layers */ + // layers : action.layer.mixability === 'nonmixable' && prevState.layers.findIndex(l => l.mixability === 'nonmixable') >= 0 + // ? prevState.layers + // : prevState.layers.concat(action.layer) + + /* this configuration allows the addition of multiple non mixables */ + // layers : prevState.layers.map(l => mapLayer(l, action.layer)).concat(action.layer) + layers : mixNgLayers(prevState.layers, action.layer) + + // action.layer.constructor === Array + // ? prevState.layers.concat(action.layer) + // : prevState.layers.concat({ + // ...action.layer, + // ...( action.layer.mixability === 'nonmixable' && prevState.layers.findIndex(l => l.mixability === 'nonmixable') >= 0 + // ? {visible: false} + // : {}) + // }) + } + case REMOVE_NG_LAYERS: + const { layers } = action + const layerNameSet = new Set(layers.map(l => l.name)) + return { + ...prevState, + layers: prevState.layers.filter(l => !layerNameSet.has(l.name)) + } + case REMOVE_NG_LAYER: + return { + ...prevState, + layers : prevState.layers.filter(l => l.name !== action.layer.name) + } + case SHOW_NG_LAYER: + return { + ...prevState, + layers : prevState.layers.map(l => l.name === action.layer.name + ? { ...l, visible: true } + : l) + } + case HIDE_NG_LAYER: + return { + ...prevState, + + layers : prevState.layers.map(l => l.name === action.layer.name + ? { ...l, visible: false } + : l) + } + case FORCE_SHOW_SEGMENT: + return { + ...prevState, + forceShowSegment : action.forceShowSegment + } + case NEHUBA_READY: + const { nehubaReady } = action + return { + ...prevState, + nehubaReady + } + case GENERAL_ACTION_TYPES.APPLY_STATE: + const { ngViewerState } = (action as any).state + return ngViewerState + default: return prevState } } @@ -187,11 +245,54 @@ export class NgViewerUseEffect implements OnDestroy { private subscriptions: Subscription[] = [] + @Effect() + public applySavedUserConfig$: Observable<any> + constructor( private actions: Actions, private store$: Store<IavRootStoreInterface>, - private constantService: AtlasViewerConstantsServices, - ) { + private constantService: AtlasViewerConstantsServices + ){ + + // TODO either split backend user to be more granular, or combine the user config into a single subscription + this.subscriptions.push( + this.store$.pipe( + select('ngViewerState'), + distinctUntilChanged(), + debounceTime(200), + skip(1), + // Max frequency save once every second + throttleTime(1000) + ).subscribe(({panelMode, panelOrder}) => { + fetch(`${this.constantService.backendUrl}user/config`, { + method: 'POST', + headers: { + 'Content-type': 'application/json' + }, + body: JSON.stringify({ ngViewerState: { panelMode, panelOrder } }) + }) + }) + ) + + this.applySavedUserConfig$ = from(fetch(`${this.constantService.backendUrl}user/config`).then(r => r.json())).pipe( + catchError((err,caught) => of(null)), + filter(v => !!v), + withLatestFrom(this.store$), + map(([{ngViewerState: fetchedNgViewerState}, state]) => { + const { ngViewerState } = state + return { + type: GENERAL_ACTION_TYPES.APPLY_STATE, + state: { + ...state, + ngViewerState: { + ...ngViewerState, + ...fetchedNgViewerState + } + } + } + }) + ) + const toggleMaxmimise$ = this.actions.pipe( ofType(ACTION_TYPES.TOGGLE_MAXIMISE), shareReplay(1), diff --git a/src/ui/signinModal/signinModal.style.css b/src/ui/signinModal/signinModal.style.css index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cbbec1ebb685a7f543a0e476c8eff9401befb834 100644 --- a/src/ui/signinModal/signinModal.style.css +++ b/src/ui/signinModal/signinModal.style.css @@ -0,0 +1,5 @@ +a +{ + display:inline-block; + margin-top: 0.25rem; +} \ No newline at end of file diff --git a/src/ui/signinModal/signinModal.template.html b/src/ui/signinModal/signinModal.template.html index e2ab8fe9f88393075d7597edaec26356b2d1b309..faac23115ca6575f9497a2ae2805d8ce7a061968 100644 --- a/src/ui/signinModal/signinModal.template.html +++ b/src/ui/signinModal/signinModal.template.html @@ -12,9 +12,6 @@ </div> <ng-template #notLoggedIn> - <span> - Login via - </span> <a *ngFor="let m of loginMethods" [href]="m.href"> <button