mirror of
https://github.com/muerwre/orchidmap-front.git
synced 2025-04-25 19:16:41 +07:00
changelog: dialog for changelog
This commit is contained in:
parent
b719b3f1a2
commit
65e549a5af
12 changed files with 218 additions and 34 deletions
74
src/components/dialogs/AppInfoDialog.jsx
Normal file
74
src/components/dialogs/AppInfoDialog.jsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { Scroll } from '$components/Scroll';
|
||||||
|
import { APP_INFO } from '$constants/app_info';
|
||||||
|
|
||||||
|
export const AppInfoDialog = () => (
|
||||||
|
<div className="dialog-content">
|
||||||
|
<div className="dialog-head">
|
||||||
|
<div className="dialog-head-title">
|
||||||
|
Orchid Map
|
||||||
|
</div>
|
||||||
|
<div className="small gray">
|
||||||
|
версия{' '}
|
||||||
|
{(APP_INFO.VERSION || 1)}.
|
||||||
|
{(APP_INFO.CHANGELOG[APP_INFO.VERSION].length || 0)}
|
||||||
|
</div>
|
||||||
|
<div className="small app-info-list">
|
||||||
|
<div>
|
||||||
|
Исходный код:{' '}
|
||||||
|
<a href="//github.com/muerwre/orchidMap" target="_blank">github.com/muerwre/orchidMap</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="small app-info-list">
|
||||||
|
<div>
|
||||||
|
Frontend:{' '}
|
||||||
|
<a href="//reactjs.org/" target="_blank">ReactJS</a>,{' '}
|
||||||
|
<a href="//leafletjs.com" target="_blank">Leaflet</a>,{' '}
|
||||||
|
<a href="//www.liedman.net/leaflet-routing-machine/" target="_blank">Leaflet Routing Machine</a>{' '}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Backend:{' '}
|
||||||
|
<a href="//project-osrm.org/" target="_blank">OSRM</a>,{' '}
|
||||||
|
<a href="//nodejs.org/" target="_blank">NodeJS</a>,{' '}
|
||||||
|
<a href="//expressjs.com/" target="_blank">ExpressJS</a>,{' '}
|
||||||
|
<a href="//mongodb.com/" target="_blank">MongoDB</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Scroll className="dialog-shader">
|
||||||
|
<div>
|
||||||
|
<div className="app-info-changelog">
|
||||||
|
<h2>История изменений</h2>
|
||||||
|
{
|
||||||
|
[...Object.keys(APP_INFO.CHANGELOG)].reverse().map((version, i) => (
|
||||||
|
<div className="app-info-changelog-item" key={version}>
|
||||||
|
<div className="app-info-number">{version}.</div>
|
||||||
|
<div className="app-info-version">
|
||||||
|
{
|
||||||
|
APP_INFO.CHANGELOG[version].map((release, y) => (
|
||||||
|
<div className="app-info-release" key={release}>
|
||||||
|
<div className="app-info-number">{APP_INFO.CHANGELOG[version].length - y}.</div>
|
||||||
|
<div className="app-info-build">
|
||||||
|
{
|
||||||
|
APP_INFO.CHANGELOG[version][y].map((build, z) => (
|
||||||
|
<div className="app-info-change" key={build}>
|
||||||
|
<div className="app-info-number">{(z)}.</div>
|
||||||
|
<span>{APP_INFO.CHANGELOG[version][y][z]}</span>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Scroll>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { GuestButton } from '$components/user/GuestButton';
|
||||||
import { DEFAULT_USER, ROLES } from '$constants/auth';
|
import { DEFAULT_USER, ROLES } from '$constants/auth';
|
||||||
import { UserButton } from '$components/user/UserButton';
|
import { UserButton } from '$components/user/UserButton';
|
||||||
import { UserMenu } from '$components/user/UserMenu';
|
import { UserMenu } from '$components/user/UserMenu';
|
||||||
import { setUser, userLogout, takeAShot, setDialog, gotVkUser } from '$redux/user/actions';
|
import { setUser, userLogout, takeAShot, setDialog, gotVkUser, setDialogActive } from '$redux/user/actions';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import type { UserType } from '$constants/types';
|
import type { UserType } from '$constants/types';
|
||||||
|
@ -21,6 +21,7 @@ type Props = {
|
||||||
|
|
||||||
userLogout: Function,
|
userLogout: Function,
|
||||||
setDialog: Function,
|
setDialog: Function,
|
||||||
|
setDialogActive: Function,
|
||||||
gotVkUser: Function,
|
gotVkUser: Function,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,7 +60,14 @@ export class Component extends React.PureComponent<Props, void> {
|
||||||
|
|
||||||
setMenuOpened = () => this.setState({ menuOpened: !this.state.menuOpened });
|
setMenuOpened = () => this.setState({ menuOpened: !this.state.menuOpened });
|
||||||
openMapsDialog = () => {
|
openMapsDialog = () => {
|
||||||
this.props.setDialog({ dialog: DIALOGS.MAP_LIST });
|
this.props.setDialog(DIALOGS.MAP_LIST);
|
||||||
|
this.props.setDialogActive(this.props.dialog !== DIALOGS.MAP_LIST);
|
||||||
|
};
|
||||||
|
|
||||||
|
openAppInfoDialog = () => {
|
||||||
|
this.setMenuOpened();
|
||||||
|
this.props.setDialog(DIALOGS.APP_INFO);
|
||||||
|
this.props.setDialogActive(this.props.dialog !== DIALOGS.APP_INFO);
|
||||||
};
|
};
|
||||||
|
|
||||||
openOauthFrame = () => {
|
openOauthFrame = () => {
|
||||||
|
@ -94,7 +102,7 @@ export class Component extends React.PureComponent<Props, void> {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
(user && user.role && user.role !== 'guest' && menuOpened) &&
|
(user && user.role && user.role !== 'guest' && menuOpened) &&
|
||||||
<UserMenu user={user} userLogout={this.props.userLogout} />
|
<UserMenu user={user} userLogout={this.props.userLogout} openAppInfoDialog={this.openAppInfoDialog} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -125,6 +133,7 @@ const mapDispatchToProps = dispatch => bindActionCreators({
|
||||||
takeAShot,
|
takeAShot,
|
||||||
setDialog,
|
setDialog,
|
||||||
gotVkUser,
|
gotVkUser,
|
||||||
|
setDialogActive,
|
||||||
}, dispatch);
|
}, dispatch);
|
||||||
|
|
||||||
export const UserPanel = connect(mapStateToProps, mapDispatchToProps)(Component);
|
export const UserPanel = connect(mapStateToProps, mapDispatchToProps)(Component);
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CLIENT } from '$config/frontend';
|
import { APP_INFO } from '$constants/app_info';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
userLogout: Function,
|
userLogout: Function,
|
||||||
|
openAppInfoDialog: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UserMenu = ({ userLogout }: Props) => (
|
export const UserMenu = ({ userLogout, openAppInfoDialog }: Props) => (
|
||||||
<div className="user-panel-menu">
|
<div className="user-panel-menu">
|
||||||
<div className="user-panel-title">
|
<div className="user-panel-title">
|
||||||
ORCHID
|
ORCHID
|
||||||
<br />
|
<br />
|
||||||
MAP
|
MAP
|
||||||
<span className="user-panel-ver">
|
<span className="user-panel-ver">
|
||||||
- { CLIENT.VER }
|
- {(APP_INFO.VERSION || 1)}.{(APP_INFO.RELEASE.length || 0)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="user-panel-item" onClick={openAppInfoDialog}>
|
||||||
|
О редакторе карт
|
||||||
|
</div>
|
||||||
<a className="user-panel-item" href="https://github.com/muerwre/orchidMap" target="_blank" rel="noopener noreferrer">
|
<a className="user-panel-item" href="https://github.com/muerwre/orchidMap" target="_blank" rel="noopener noreferrer">
|
||||||
Проект на github
|
Проект на github
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,28 +3,28 @@ export const APP_INFO = {
|
||||||
RELEASE: 1,
|
RELEASE: 1,
|
||||||
|
|
||||||
CHANGELOG: {
|
CHANGELOG: {
|
||||||
2: {
|
2: [
|
||||||
0: [
|
[
|
||||||
'15.08.18 Первый коммит',
|
'Redux, redux-saga', // [26.11.18]
|
||||||
'15.08.18 ReactJS для управления интерфейсом',
|
'Рисование карт на стороне клиента', // [28.11.18]
|
||||||
'16.08.18 Карта, роутер, стикеры, панели редактора',
|
'Backend на expressjs + mongoose', // [30.11.18]
|
||||||
'27.08.18 Выбор логотипа и стиля карты',
|
'Импорт данных из старых версий карт', // [06.12.18]
|
||||||
'29.08.18 Переключение режимов, сохранение',
|
'Диалог со списком карт пользователя', // [07.12.18]
|
||||||
'04.09.18 Загрузка карт, перерисовка данных, маршруты',
|
'Мобильный интерфейс', // [07.12.18]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Первый коммит', // [15.08.18]
|
||||||
|
'ReactJS для управления интерфейсом', // [15.08.18]
|
||||||
|
'Карта, роутер, стикеры, панели редактора', // [16.08.18]
|
||||||
|
'Выбор логотипа и стиля карты', // [27.08.18]
|
||||||
|
'Переключение режимов, сохранение', // [29.08.18]
|
||||||
|
'Загрузка карт, перерисовка данных, маршруты', // [04.09.18]
|
||||||
|
],
|
||||||
],
|
],
|
||||||
1: [
|
1: [
|
||||||
'26.11.18 Redux, redux-saga',
|
[
|
||||||
'28.11.18 Рисование карт на стороне клиента',
|
|
||||||
'30.11.18 Backend на expressjs + mongoose',
|
|
||||||
'04.12.18 Backend на expressjs + mongoose',
|
|
||||||
'06.12.18 Импорт данных из старых версий карт',
|
|
||||||
'07.12.18 Диалог со списком карт пользователя',
|
|
||||||
]
|
|
||||||
},
|
|
||||||
1: {
|
|
||||||
0: [
|
|
||||||
'Первый работающий редактор карт'
|
'Первый работающий редактор карт'
|
||||||
]
|
]
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
export const DIALOGS = ({
|
export const DIALOGS = ({
|
||||||
NONE: 'NONE',
|
NONE: 'NONE',
|
||||||
MAP_LIST: 'MAP_LIST',
|
MAP_LIST: 'MAP_LIST',
|
||||||
|
APP_INFO: 'APP_INFO',
|
||||||
}: { [key: String]: String });
|
}: { [key: String]: String });
|
||||||
|
|
|
@ -3,14 +3,20 @@ import React from 'react';
|
||||||
import { DIALOGS } from '$constants/dialogs';
|
import { DIALOGS } from '$constants/dialogs';
|
||||||
import { MapListDialog } from '$components/dialogs/MapListDialog';
|
import { MapListDialog } from '$components/dialogs/MapListDialog';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { AppInfoDialog } from '$components/dialogs/AppInfoDialog';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
dialog: String,
|
dialog: String,
|
||||||
dialog_active: Boolean,
|
dialog_active: Boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LEFT_DIALOGS = {
|
||||||
|
[DIALOGS.MAP_LIST]: MapListDialog,
|
||||||
|
[DIALOGS.APP_INFO]: AppInfoDialog,
|
||||||
|
};
|
||||||
|
|
||||||
export const LeftDialog = ({ dialog, dialog_active }: Props) => (
|
export const LeftDialog = ({ dialog, dialog_active }: Props) => (
|
||||||
<div className={classnames('dialog', { active: dialog_active })}>
|
<div className={classnames('dialog', { active: dialog_active })}>
|
||||||
{ dialog === DIALOGS.MAP_LIST && <MapListDialog /> }
|
{ dialog && LEFT_DIALOGS[dialog] && React.createElement(LEFT_DIALOGS[dialog]) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -40,9 +40,10 @@ export const cropAShot = payload => ({ type: ACTIONS.CROP_A_SHOT, ...payload });
|
||||||
|
|
||||||
export const setProvider = provider => ({ type: ACTIONS.SET_PROVIDER, provider });
|
export const setProvider = provider => ({ type: ACTIONS.SET_PROVIDER, provider });
|
||||||
|
|
||||||
export const setDialog = ({ dialog, dialog_active }) => ({ type: ACTIONS.SET_DIALOG, dialog });
|
export const setDialog = dialog => ({ type: ACTIONS.SET_DIALOG, dialog });
|
||||||
|
export const setDialogActive = dialog_active => ({ type: ACTIONS.SET_DIALOG_ACTIVE, dialog_active });
|
||||||
|
|
||||||
export const locationChanged = location => ({ type: ACTIONS.LOCATION_CHANGED, location });
|
export const locationChanged = location => ({ type: ACTIONS.LOCATION_CHANGED, location });
|
||||||
export const setReady = ready => ({ type: ACTIONS.SET_READY, ready });
|
export const setReady = ready => ({ type: ACTIONS.SET_READY, ready });
|
||||||
|
|
||||||
|
|
||||||
export const gotVkUser = user => ({ type: ACTIONS.GOT_VK_USER, user });
|
export const gotVkUser = user => ({ type: ACTIONS.GOT_VK_USER, user });
|
||||||
|
|
|
@ -42,6 +42,7 @@ export const ACTIONS = ({
|
||||||
SET_PROVIDER: 'SET_PROVIDER',
|
SET_PROVIDER: 'SET_PROVIDER',
|
||||||
|
|
||||||
SET_DIALOG: 'SET_DIALOG',
|
SET_DIALOG: 'SET_DIALOG',
|
||||||
|
SET_DIALOG_ACTIVE: 'SET_DIALOG_ACTIVE',
|
||||||
LOCATION_CHANGED: 'LOCATION_CHANGED',
|
LOCATION_CHANGED: 'LOCATION_CHANGED',
|
||||||
SET_READY: 'SET_READY',
|
SET_READY: 'SET_READY',
|
||||||
|
|
||||||
|
|
|
@ -78,10 +78,16 @@ const setRenderer = (state, { payload }) => ({
|
||||||
|
|
||||||
const setProvider = (state, { provider }) => ({ ...state, provider });
|
const setProvider = (state, { provider }) => ({ ...state, provider });
|
||||||
|
|
||||||
const setDialog = (state, { dialog, dialog_active }) => ({
|
const setDialog = (state, { dialog }) => ({
|
||||||
...state,
|
...state,
|
||||||
dialog: dialog || state.dialog,
|
dialog,
|
||||||
dialog_active: typeof dialog_active !== 'undefined' ? dialog_active : !state.dialog_active,
|
// dialog_active: typeof dialog_active !== 'undefined' ? dialog_active : !state.dialog_active,
|
||||||
|
// dialog_active,
|
||||||
|
});
|
||||||
|
|
||||||
|
const setDialogActive = (state, { dialog_active }) => ({
|
||||||
|
...state,
|
||||||
|
dialog_active: dialog_active || !state.dialog_active,
|
||||||
});
|
});
|
||||||
|
|
||||||
const setReady = (state, { ready = true }) => ({
|
const setReady = (state, { ready = true }) => ({
|
||||||
|
@ -114,6 +120,7 @@ const HANDLERS = ({
|
||||||
[ACTIONS.SET_PROVIDER]: setProvider,
|
[ACTIONS.SET_PROVIDER]: setProvider,
|
||||||
|
|
||||||
[ACTIONS.SET_DIALOG]: setDialog,
|
[ACTIONS.SET_DIALOG]: setDialog,
|
||||||
|
[ACTIONS.SET_DIALOG_ACTIVE]: setDialogActive,
|
||||||
[ACTIONS.SET_READY]: setReady,
|
[ACTIONS.SET_READY]: setReady,
|
||||||
}: { [key: String]: Function });
|
}: { [key: String]: Function });
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,11 @@
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-shader {
|
.dialog-shader {
|
||||||
|
@ -125,3 +130,71 @@
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-info-changelog {
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
|
||||||
|
div {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-info-number {
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-info-changelog-item {
|
||||||
|
text-transform: uppercase;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.app-info-current {
|
||||||
|
font-size: 0.9em;
|
||||||
|
opacity: 0.3;
|
||||||
|
display: inline;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-info-version {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-info-release {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-info-build {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-info-change {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
|
||||||
|
.app-info-number {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-info-list {
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
div {
|
||||||
|
padding: 2.5px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -117,3 +117,12 @@ body {
|
||||||
@media (max-width: @mobile_breakpoint) {
|
@media (max-width: @mobile_breakpoint) {
|
||||||
.desktop-only { display: none; }
|
.desktop-only { display: none; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font: inherit;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
|
@ -164,9 +164,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-user {
|
.panel-user {
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
@media (max-width: @mobile_breakpoint) {
|
@media (max-width: @mobile_breakpoint) {
|
||||||
|
z-index: 1;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
|
|
||||||
.control-sep {
|
.control-sep {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue