HM-105. Подтянуть загрузку данных пользователя. (#47)
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import LocalStorageAPI from './LocalStorageAPI';
|
import LocalStorageAPI from './LocalStorageAPI';
|
||||||
import {LOCAL_STORAGE_TYPE} from './consts';
|
import {LOCAL_STORAGE_TYPE} from './consts';
|
||||||
|
import userInfoService from '../services/UserInfoService';
|
||||||
|
|
||||||
const API_NAME = 'storageServiceUITokenApi';
|
const API_NAME = 'storageServiceUITokenApi';
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ class TokenApi {
|
|||||||
saveTokenPair = (tokens) => {
|
saveTokenPair = (tokens) => {
|
||||||
this.localApi.createOrUpdate(tokens.refresh_token);
|
this.localApi.createOrUpdate(tokens.refresh_token);
|
||||||
this.sessionApi.createOrUpdate(tokens.access_token);
|
this.sessionApi.createOrUpdate(tokens.access_token);
|
||||||
|
userInfoService.setUserLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
20
src/app.html
20
src/app.html
@ -13,19 +13,23 @@
|
|||||||
<template id="main-menu">
|
<template id="main-menu">
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
<div class="container-fluid d-flex">
|
<div class="container-fluid d-flex">
|
||||||
<div class="Logo__box d-flex align-items-center">
|
<div class="MainMenu__logoBox d-flex align-items-center">
|
||||||
<img src="./img/logo.svg" alt="logo" class="Logo mr-2">
|
<img src="./img/logo.svg" alt="logo" class="MainMenu__logo mr-2">
|
||||||
<a class="h3 text-light navbar-brand text-wrap m-0">Storage Service</a>
|
<a class="h3 text-light navbar-brand text-wrap m-0">Storage Service</a>
|
||||||
</div>
|
</div>
|
||||||
<button class="navbar-toggler ml-auto" type="button" data-toggle="collapse" data-target="#navbarNavDropdown"
|
<button class="navbar-toggler ml-auto" type="button" data-toggle="collapse"
|
||||||
aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
|
data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse ml-5" id="navbarNavDropdown">
|
<div class="collapse navbar-collapse ml-5" id="navbarNavDropdown">
|
||||||
<ul class="navbar-nav"></ul>
|
<ul class="navbar-nav mr-auto"></ul>
|
||||||
|
<div>
|
||||||
|
<div class="MainMenu__avatar"></div>
|
||||||
|
</div>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-outline-dark MainMenu__exitButton">Выйти</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="Avatar"></div>
|
|
||||||
<button type="submit" class="btn btn-outline-dark text-light ml-1">Выйти</button>
|
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -405,4 +409,4 @@
|
|||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -11,10 +11,15 @@ import RouterPagesContainer from './components/router-pages-container/index';
|
|||||||
import LogsPage from './components/logs-page/index';
|
import LogsPage from './components/logs-page/index';
|
||||||
import LoginPage from './components/login-page';
|
import LoginPage from './components/login-page';
|
||||||
import authServiceApi from './api/AuthServiceAPI';
|
import authServiceApi from './api/AuthServiceAPI';
|
||||||
|
import userInfoService from './services/UserInfoService';
|
||||||
|
import {EVENTS} from './consts';
|
||||||
|
|
||||||
const initAppComponents = () => {
|
const initAppComponents = () => {
|
||||||
const mainMenu = new MainMenu();
|
const mainMenu = new MainMenu();
|
||||||
mainMenu.render();
|
mainMenu.render();
|
||||||
|
userInfoService.subscribe(EVENTS.CHANGE_USER_INFO, ({avatar}) => {
|
||||||
|
mainMenu.setAvatar(avatar);
|
||||||
|
});
|
||||||
|
|
||||||
const routerPagesContainer = new RouterPagesContainer(mainMenu);
|
const routerPagesContainer = new RouterPagesContainer(mainMenu);
|
||||||
|
|
||||||
|
|||||||
@ -1,40 +1,52 @@
|
|||||||
.Logo {
|
.MainMenu__logo {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
margin: 0 20px;
|
margin: 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Logo__box {
|
.MainMenu__logoBox {
|
||||||
width: 170px;
|
width: 170px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Buttons__container {
|
|
||||||
margin: 0 20px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.NavButton {
|
|
||||||
margin: 5px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px){
|
.MainMenu__avatar {
|
||||||
.Buttons__container {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.Avatar {
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-image: url('https://d5qmjlya0ygtg.cloudfront.net/569/c5295/f9ad/47c8/96a0/66a65609b38d/original/331698.jpg');
|
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-origin: border-box;
|
background-origin: border-box;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background-size: cover;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,19 +4,25 @@ import routeService from '../../services/RouteService';
|
|||||||
import './MainMenu.css';
|
import './MainMenu.css';
|
||||||
import {createElement} from '../../utils/elementUtils';
|
import {createElement} from '../../utils/elementUtils';
|
||||||
import {EVENTS} from '../../consts';
|
import {EVENTS} from '../../consts';
|
||||||
|
import tokenApi from '../../api/TokenAPI';
|
||||||
|
|
||||||
export const NAV_MENU = [
|
export const NAV_MENU = [
|
||||||
{
|
{
|
||||||
title: 'Главная',
|
title: 'Главная',
|
||||||
url: '/'
|
url: '/',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Список хранилищ',
|
title: 'Список хранилищ',
|
||||||
url: '/api'
|
url: '/api',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Журнал',
|
title: 'Журнал',
|
||||||
url: '/logs'
|
url: '/logs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Личный кабинет',
|
||||||
|
url: '/profile',
|
||||||
|
className: 'MainMenu__profileButton'
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -27,12 +33,20 @@ class MainMenu extends Component {
|
|||||||
super('#main-menu', document.body);
|
super('#main-menu', document.body);
|
||||||
|
|
||||||
this.buttonsContainer = this.mainNode.querySelector('.navbar-nav');
|
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', () => {
|
this.addEventListener(this.logoBox, 'click', () => {
|
||||||
routeService.goTo('/');
|
routeService.goTo('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.addEventListener(this.avatar, 'click', () => {
|
||||||
|
routeService.goTo('/profile');
|
||||||
|
});
|
||||||
|
|
||||||
this.addSubscribe(routeService, EVENTS.ROUTE_CHANGE, (route) => {
|
this.addSubscribe(routeService, EVENTS.ROUTE_CHANGE, (route) => {
|
||||||
this.menuItems.forEach(({url, link}) => {
|
this.menuItems.forEach(({url, link}) => {
|
||||||
if (route.url === url) {
|
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 = () => {
|
render = () => {
|
||||||
this.menuItems = NAV_MENU.map(({url, title}) => {
|
this.menuItems = NAV_MENU.map(({url, title, className = ''}) => {
|
||||||
const li = createElement({
|
const li = createElement({
|
||||||
tagName: 'li',
|
tagName: 'li',
|
||||||
parentNode: this.buttonsContainer,
|
parentNode: this.buttonsContainer,
|
||||||
options: {
|
options: {
|
||||||
className: 'nav-item',
|
className: `nav-item ${className}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const link = createElement({
|
const link = createElement({
|
||||||
|
|||||||
@ -35,6 +35,7 @@ export const EVENTS = {
|
|||||||
ROUTE_SEARCH_CHANGE: 'routeSearchChange',
|
ROUTE_SEARCH_CHANGE: 'routeSearchChange',
|
||||||
ROW_CLICK: 'rowClick',
|
ROW_CLICK: 'rowClick',
|
||||||
ROW_DOUBLE_CLICK: 'rowDoubleClick',
|
ROW_DOUBLE_CLICK: 'rowDoubleClick',
|
||||||
|
CHANGE_USER_INFO: 'changeUserInfo',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FORM_TYPES = {
|
export const FORM_TYPES = {
|
||||||
|
|||||||
26
src/services/UserInfoService.js
Normal file
26
src/services/UserInfoService.js
Normal file
@ -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;
|
||||||
27
src/utils/jwtDecode.js
Normal file
27
src/utils/jwtDecode.js
Normal file
@ -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, '/')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user