From 3a8bf0632bf7797706333170d8194fc18e4b4327 Mon Sep 17 00:00:00 2001 From: Nikolay <46225163+vigdorov@users.noreply.github.com> Date: Sun, 2 Aug 2020 12:18:54 +0300 Subject: [PATCH] =?UTF-8?q?HM-105.=20=D0=9F=D0=BE=D0=B4=D1=82=D1=8F=D0=BD?= =?UTF-8?q?=D1=83=D1=82=D1=8C=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA?= =?UTF-8?q?=D1=83=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F.?= =?UTF-8?q?=20(#47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/TokenAPI.js | 2 ++ src/app.html | 20 ++++++----- src/app.js | 5 +++ src/components/main-menu/MainMenu.css | 52 ++++++++++++++++----------- src/components/main-menu/MainMenu.js | 35 ++++++++++++++---- src/consts.js | 1 + src/services/UserInfoService.js | 26 ++++++++++++++ src/utils/jwtDecode.js | 27 ++++++++++++++ 8 files changed, 134 insertions(+), 34 deletions(-) create mode 100644 src/services/UserInfoService.js create mode 100644 src/utils/jwtDecode.js diff --git a/src/api/TokenAPI.js b/src/api/TokenAPI.js index 6608780..ae1fc45 100644 --- a/src/api/TokenAPI.js +++ b/src/api/TokenAPI.js @@ -1,5 +1,6 @@ import LocalStorageAPI from './LocalStorageAPI'; import {LOCAL_STORAGE_TYPE} from './consts'; +import userInfoService from '../services/UserInfoService'; const API_NAME = 'storageServiceUITokenApi'; @@ -27,6 +28,7 @@ class TokenApi { saveTokenPair = (tokens) => { this.localApi.createOrUpdate(tokens.refresh_token); this.sessionApi.createOrUpdate(tokens.access_token); + userInfoService.setUserLogin(); } /** diff --git a/src/app.html b/src/app.html index 325d21a..c3e328e 100644 --- a/src/app.html +++ b/src/app.html @@ -13,19 +13,23 @@ @@ -405,4 +409,4 @@ - + \ No newline at end of file diff --git a/src/app.js b/src/app.js index 207a2b0..26919db 100644 --- a/src/app.js +++ b/src/app.js @@ -11,10 +11,15 @@ import RouterPagesContainer from './components/router-pages-container/index'; import LogsPage from './components/logs-page/index'; import LoginPage from './components/login-page'; import authServiceApi from './api/AuthServiceAPI'; +import userInfoService from './services/UserInfoService'; +import {EVENTS} from './consts'; const initAppComponents = () => { const mainMenu = new MainMenu(); mainMenu.render(); + userInfoService.subscribe(EVENTS.CHANGE_USER_INFO, ({avatar}) => { + mainMenu.setAvatar(avatar); + }); const routerPagesContainer = new RouterPagesContainer(mainMenu); diff --git a/src/components/main-menu/MainMenu.css b/src/components/main-menu/MainMenu.css index e019414..7651254 100644 --- a/src/components/main-menu/MainMenu.css +++ b/src/components/main-menu/MainMenu.css @@ -1,40 +1,52 @@ -.Logo { +.MainMenu__logo { width: 50px; margin: 0 20px; } -.Logo__box { +.MainMenu__logoBox { width: 170px; cursor: pointer; } -.Buttons__container { - margin: 0 20px; - display: flex; - justify-content: center; -} -.NavButton { - margin: 5px 10px; -} - .nav-item { cursor: pointer; } -@media (max-width: 900px){ - .Buttons__container { - flex-direction: column; - align-items: center; - justify-content: center; - } -} -.Avatar { +.MainMenu__avatar { border-radius: 50%; - background-image: url('https://d5qmjlya0ygtg.cloudfront.net/569/c5295/f9ad/47c8/96a0/66a65609b38d/original/331698.jpg'); background-repeat: no-repeat; background-position: center; background-origin: border-box; width: 50px; height: 50px; background-size: cover; + cursor: pointer; +} + +.MainMenu__exitButton { + cursor: pointer; + color: hsla(0, 0%, 100%, 0.5); +} + +.MainMenu__exitButton:hover { + color: #fff; +} + +.MainMenu__profileButton { + display: none; +} + +@media (max-width: 991px) { + .MainMenu__profileButton { + display: block; + } + + .MainMenu__exitButton { + padding-left: 0; + margin-left: 0; + } + + .MainMenu__avatar { + display: none; + } } diff --git a/src/components/main-menu/MainMenu.js b/src/components/main-menu/MainMenu.js index 05293c4..38342d2 100644 --- a/src/components/main-menu/MainMenu.js +++ b/src/components/main-menu/MainMenu.js @@ -4,19 +4,25 @@ import routeService from '../../services/RouteService'; import './MainMenu.css'; import {createElement} from '../../utils/elementUtils'; import {EVENTS} from '../../consts'; +import tokenApi from '../../api/TokenAPI'; export const NAV_MENU = [ { title: 'Главная', - url: '/' + url: '/', }, { title: 'Список хранилищ', - url: '/api' + url: '/api', }, { title: 'Журнал', - url: '/logs' + url: '/logs', + }, + { + title: 'Личный кабинет', + url: '/profile', + className: 'MainMenu__profileButton' }, ]; @@ -27,12 +33,20 @@ class MainMenu extends Component { super('#main-menu', document.body); this.buttonsContainer = this.mainNode.querySelector('.navbar-nav'); - this.logoBox = this.mainNode.querySelector('.Logo__box'); + this.logoBox = this.mainNode.querySelector('.MainMenu__logoBox'); + this.avatar = this.mainNode.querySelector('.MainMenu__avatar'); + this.exitButton = this.mainNode.querySelector('.MainMenu__exitButton'); + + this.addEventListener(this.exitButton, 'click', this.exitApp); this.addEventListener(this.logoBox, 'click', () => { routeService.goTo('/'); }); + this.addEventListener(this.avatar, 'click', () => { + routeService.goTo('/profile'); + }); + this.addSubscribe(routeService, EVENTS.ROUTE_CHANGE, (route) => { this.menuItems.forEach(({url, link}) => { if (route.url === url) { @@ -44,13 +58,22 @@ class MainMenu extends Component { }); } + exitApp = () => { + tokenApi.clearTokents(); + location.reload(); + } + + setAvatar = (url) => { + this.avatar.style.backgroundImage = `url('${url}')`; + } + render = () => { - this.menuItems = NAV_MENU.map(({url, title}) => { + this.menuItems = NAV_MENU.map(({url, title, className = ''}) => { const li = createElement({ tagName: 'li', parentNode: this.buttonsContainer, options: { - className: 'nav-item', + className: `nav-item ${className}`, }, }); const link = createElement({ diff --git a/src/consts.js b/src/consts.js index b0a2125..6cc31c3 100644 --- a/src/consts.js +++ b/src/consts.js @@ -35,6 +35,7 @@ export const EVENTS = { ROUTE_SEARCH_CHANGE: 'routeSearchChange', ROW_CLICK: 'rowClick', ROW_DOUBLE_CLICK: 'rowDoubleClick', + CHANGE_USER_INFO: 'changeUserInfo', }; export const FORM_TYPES = { diff --git a/src/services/UserInfoService.js b/src/services/UserInfoService.js new file mode 100644 index 0000000..c5a2776 --- /dev/null +++ b/src/services/UserInfoService.js @@ -0,0 +1,26 @@ +import usersServiceApi from '../api/UsersServiceAPI'; +import tokenApi from '../api/TokenAPI'; +import {parseJwt} from '../utils/jwtDecode'; +import {EVENTS} from '../consts'; +import EmitService from './EmitService'; + +class UserInfoService extends EmitService { + constructor () { + super(); + + this.userInfo = { + login: 'not_user', + avatar: 'https://d5qmjlya0ygtg.cloudfront.net/569/c5295/f9ad/47c8/96a0/66a65609b38d/original/331698.jpg', + }; + } + + setUserLogin = async () => { + const {login} = parseJwt(tokenApi.getAccessToken()); + this.userInfo = await usersServiceApi.find(login); + this.next(EVENTS.CHANGE_USER_INFO, {...this.userInfo}); + } +} + +const userInfoService = new UserInfoService(); + +export default userInfoService; diff --git a/src/utils/jwtDecode.js b/src/utils/jwtDecode.js new file mode 100644 index 0000000..ff07d02 --- /dev/null +++ b/src/utils/jwtDecode.js @@ -0,0 +1,27 @@ +/** + * Функция для декодирования юникод текста + */ +export const base64DecodeUnicode = (string) => { + return decodeURIComponent( + Array.prototype.map + .call(atob(string), (c) => { + return `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`; + }) + .join('') + ); +}; + +/** + * Функция декодирования jwt токена для получения времени смерти + * accessToken'a + */ +export const parseJwt = (token) => { + return JSON.parse( + base64DecodeUnicode( + token + .split('.')[1] + .replace(/-/g, '+') + .replace(/_/g, '/') + ) + ); +};