Commit 9ae7812c by Иван Кубота

pages

parent de81c8eb
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -6,9 +6,13 @@
"@material-ui/core": "latest",
"@types/react": "latest",
"@types/react-dom": "latest",
"@types/react-redux": "^7.1.1",
"react": "latest",
"react-dom": "latest",
"react-redux": "^7.1.0",
"react-scripts": "latest",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0",
"typescript": "latest"
},
"scripts": {
......
......@@ -2,14 +2,13 @@ import React from 'react';
import Container from '@material-ui/core/Container';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import Button from '@material-ui/core/Button';
import Link from '@material-ui/core/Link';
import ProTip from './ProTip';
import {Grid} from "@material-ui/core";
import store from "./store";
import {MainAsideMenu} from "./component/MainAsideMenu";
import { Provider } from "react-redux";
import {Content} from "./component/Content";
function MadeWithLove() {
return (
......@@ -24,9 +23,6 @@ function MadeWithLove() {
}
export class App extends React.Component<{}, {}> {
render() {
return (
......@@ -38,15 +34,17 @@ export class App extends React.Component<{}, {}> {
</Box>
<Box my={4}>
<Typography variant="h4" component="h1" gutterBottom>
<Content/>
{/*<Typography variant="h4" component="h1" gutterBottom>
Create React App v4-beta example with TypeScript
</Typography>
<ProTip/>
*/}
{/*<ProTip/>
<MadeWithLove/>
<Button variant="contained">12345</Button>
<Button variant="contained" color="primary">12345</Button>
<Button variant="contained" color="secondary">12345</Button>
<Button variant="contained" color="secondary">12345</Button>*/}
</Box>
</Grid>
</Container>
......
import {Action, IMainStorage} from './index';
export interface IAddLocale {
payload: {key: string, value: string}
payload: {id: string}
}
export const addLocale = Action('ADD_LOCALE', function (state: IMainStorage, {payload}: IAddLocale) {
return {...state, locale: {...state.locale, [payload.key]: [payload.value]}}
if(payload.id in state.locale)
return state;
else
return {...state, locale: {
...state.locale,
[payload.id]: {
id: payload.id,
values: []
}
}};
});
\ No newline at end of file
import {Action, ILocaleTemplate, IMainStorage} from './index';
export interface IAddLocaleTemplate {
payload: {id: string, template: ILocaleTemplate}
}
export const addLocaleTemplate = Action('ADD_LOCALE_TEMPLATE', function (state: IMainStorage, {payload}: IAddLocaleTemplate) {
if(payload.id in state.locale)
return {...state, locale: {
...state.locale,
[payload.id]: {
id: payload.id,
values: state.locale[payload.id].values.concat(payload.template)
}
}};
else
return {...state, locale: {
...state.locale,
[payload.id]: {
id: payload.id,
values: [payload.template]
}
}};
});
export interface IPredef {
[key: string]: string
}
const predef: IPredef = {
"ban": "Пользователь {{$first_name}} {{$last_name}} был забанен лопатой.",
"warn": "Пользователь {{$first_name}} {{$last_name}} получает {{$count}} предупреждение из {{$maxWarns}}, осталось {{$diff}} {{plural:$diff,предупреждение,предупреждения,предупреждений}}",
"unban": "Пользователя {{$full_name}} пришлось разбанить.",
"unbanGreeting": "Ты разбанены в {{$title}}",
"pidor": "{{$first_name}}, ты — {{$condition?пацак:чатланин}}, а эта планета {{$condition?чатланская:пацакская}}.\n\nСчитай до {{$count}}.",
"pidorDoNotCount1": "Считай, кому говорят",
"pidorDoNotCount2": "Транклюкирую!",
"pidorDoNotCount3": "Последний раз предупреждаю!",
"pidorDoNotCount4": "Пожизненный эцих без гвоздей",
"pidorDoNotCount5": "Пожизненный эцих с гвоздями",
"pidorDoigralsa": "{{$full_name}} уехал в эцихе",
"pidorKletka": "Здесь можно только в клетке выступать.",
"pidorKU": "Говори «Ку»!",
"pidorOK": "Всё, хорошо посчитал. Эцилоп вернётся ночью.",
"pidorCanNotCount": "Правильно считай.",
"pidorCanNotCount2": "Эцилоп, бей его. Считай с начала давай.",
"ban2": "{{$count}} {{plural:$count,рыба,рыбы,рыб}}",
"ku": "Говори «ку»",
"permission": "Не можно",
"newLocale": "Теперь напиши боту лично. У тебя есть на это 15 минут, поторопись.",
"linkToUI": "Секретная ссылка! {{$link}}",
"_plural": "plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)"
};
setTimeout(()=>{
for(const key in predef){
addLocaleTemplate({
id: key,
template: {uuid: key, text: predef[key]}
});
}
}, 100)
\ No newline at end of file
// src/js/reducers/index.js
import {mainMenu} from "../model/mainMenu";
import store from "../store";
export interface ILocaleTemplate {
uuid: string,
text: string
}
export interface ILocale {
id: string,
values: ILocaleTemplate[]
}
export interface ILocaleHash {
[key: string]: ILocale
}
export interface IMainStorage {
activeMenu: string,
locale: {
//string: string
},
locale: ILocaleHash,
chatData: {
}
}
const initialState: IMainStorage = {
activeMenu: mainMenu.filter((el)=>el.active)[0].id,
activeMenu: 'main',
locale: {
},
......@@ -22,25 +32,31 @@ const initialState: IMainStorage = {
const reducers: any = {};
export const Action = function (type: string, reducer: Function<IMainStorage,IAction>) {
export interface IReducer {
(state: IMainStorage, action: IAction): IMainStorage;
}
export const Action = function (type: string, reducer: IReducer) {
reducers[type] = reducer;
return function(payload: any){
return {type, payload};
return store.dispatch({type, payload});
}
};
export interface IAction {
type: string,
payload: any
};
}
function rootReducer(state:IMainStorage = initialState, action: IAction) {
if(action.type in reducers){
reducers[action.type](state, action)
return reducers[action.type](state, action)
}else{
throw new Error('No reducer for type `'+ action.type +'`')
console.warn('No reducer for type `'+ action.type +'`')
}
return state;
}
export default rootReducer;
\ No newline at end of file
export default rootReducer;
import {Action} from './index';
export const setActiveMenu = Action('SET_ACTIVE_MENU', function (state, {payload}) {
return {...state, activeMenu: payload.item};
console.log(state, payload);
return {...state, activeMenu: payload};
});
\ No newline at end of file
import React from "react";
import {connect} from "react-redux";
import {ILocale, IMainStorage} from "../action-reducers";
import {Button, List, ListItem, ListSubheader, TextField} from "@material-ui/core";
import {NewLocaleTemplate} from "./NewLocaleTemplate";
export interface IConstant {
item: ILocale
}
class ConstantView extends React.Component<IConstant, IConstant> {
render(): React.ReactNode {
const {id, values} = this.props.item;
return <List subheader={<ListSubheader>{id}</ListSubheader>}>
{values.map((val)=>
<ListItem key={val.uuid}>
{val.text}
{/*<TextField
value={val.text}
margin="normal"
inputProps={{ 'aria-label': 'bare' }}
/>*/}
</ListItem>
)}
<NewLocaleTemplate id={id}/>
</List>;
}
}
export const Constant = ConstantView;
import React from "react";
import {connect} from "react-redux";
import {IMainStorage} from "../action-reducers";
import {IMainAsideMenu} from "./MainAsideMenu";
import {mainMenu} from "../model/mainMenu";
export interface IContent extends IMainAsideMenu {
}
class ContentView extends React.Component<IContent, IContent> {
render(): React.ReactNode {
const {activeMenu} = this.props;
const item = mainMenu.filter((item)=>item.id===activeMenu)[0];
if(item){
const View = item.view();
return <View/>
}
console.warn('no view', activeMenu);
return null;
}
}
const ContentMap = function (store: IMainStorage): IContent {
return {activeMenu: store.activeMenu};
};
export const Content = connect(ContentMap)(ContentView);
\ No newline at end of file
......@@ -2,17 +2,29 @@ import React from "react";
import {setActiveMenu} from "../action-reducers/setActiveMenu";
import {List, ListItem, ListItemText} from "@material-ui/core";
import {mainMenu} from "../model/mainMenu";
import {connect} from "react-redux";
import {IMainStorage} from "../action-reducers";
export class MainAsideMenu extends React.Component<{}, {}> {
export interface IMainAsideMenu {
activeMenu: string
}
class MainAsideMenuView extends React.Component<IMainAsideMenu, IMainAsideMenu> {
render(): React.ReactNode {
const {activeMenu} = this.state;
const {activeMenu} = this.props;
return <List>
{mainMenu.map((item) => (
<ListItem button selected={(activeMenu === item.id)} key={item.id} onClick={() => setActiveMenu(item.id)}>
<ListItem button selected={(activeMenu === item.id)} key={item.id} onClick={() => {
setActiveMenu(item.id)}}>
<ListItemText color={(activeMenu === item.id) ? "primary" : "secondary"} primary={item.text}/>
</ListItem>
))}
</List>;
}
}
\ No newline at end of file
}
const mapStoreToMenu = function(store:IMainStorage): IMainAsideMenu{
return {activeMenu: store.activeMenu};
};
export const MainAsideMenu = connect(mapStoreToMenu)(MainAsideMenuView);
\ No newline at end of file
import React, {ChangeEvent} from "react";
import {Button, Container, List, ListItem, TextField} from "@material-ui/core";
import {addLocale} from "../action-reducers/addLocale";
export interface INewLocale {
id: string
}
class NewLocaleView extends React.Component<{}, INewLocale> {
state = {
id: ''
};
changed(e: ChangeEvent) {
this.setState({id: (e.target as HTMLInputElement).value});
}
addLocale(){
const {id} = this.state;
addLocale({id})
this.setState({id: ''});
}
render(): React.ReactNode {
const {id} = this.state;
console.log(id);
return <List>
<ListItem>
<TextField
margin="normal"
inputProps={{ 'aria-label': 'bare' }}
onChange={(e)=>this.changed(e)}
value={id}
/>
<Button variant="contained" color="primary" onClick={()=>this.addLocale()}>+ Новая локализационная стринга</Button>
</ListItem>
</List>;
}
}
export const NewLocale = NewLocaleView;
\ No newline at end of file
import React from "react";
import {connect} from "react-redux";
import {IMainStorage} from "../action-reducers";
import {Button, Grid, TextField} from "@material-ui/core";
import {addLocaleTemplate, IAddLocaleTemplate} from "../action-reducers/addLocaleTemplate";
import {getUUID} from "../helpers";
export interface INewLocaleTemplate {
id: string
}
class NewLocaleTemplateView extends React.Component<INewLocaleTemplate, {}> {
state = {
expanded: false,
text: ''
};
addAction() {
addLocaleTemplate({id: this.props.id, template: {uuid: getUUID(), text: this.state.text}});
this.setState({expanded: false, text: ''})
}
render(): React.ReactNode {
return <div>
{this.state.expanded ?
<div>
<Grid spacing={1} container>
<Grid item>
<TextField
value={this.state.text}
onKeyDown={(e) => e.key === 'Enter' && e.ctrlKey && this.addAction()}
onChange={(e) => this.setState({text: e.target.value})}
margin="normal"
inputProps={{'aria-label': 'bare'}}
/>
</Grid>
<Grid item>
<Button variant="contained" color="secondary" onClick={() => this.addAction()}>
+ Сохранить (Ctrl+enter)
</Button>
</Grid>
<Grid item>
<Button variant="contained" color="primary"
onClick={() => this.setState({expanded: false})}>– Передумал</Button>
</Grid>
</Grid>
</div>
:
<Button variant="contained" color="secondary" onClick={() => this.setState({expanded: true})}>
+ Новый шаблон
</Button>
}
</div>;
}
}
export const NewLocaleTemplate = NewLocaleTemplateView;
\ No newline at end of file
export const AZ = function (key: string) {
return function (a: any, b: any) {
return a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0;
}
};
export const ZA = function (key: string) {
return function (a: any, b: any) {
return a[key] < b[key] ? 1 : a[key] > b[key] ? -1 : 0;
}
};
export const getUUID = function () {
return [
Math.random().toString(36).substr(2),
Math.random().toString(36).substr(2,7),
Math.random().toString(36).substr(2)
].join('-')
};
\ No newline at end of file
import {Constants} from "../view/Constants";
import React from "react";
import {Profile} from "../view/Profile";
import {Info} from "../view/Info";
import {setActiveMenu} from "../action-reducers/setActiveMenu";
export interface IComponentLambda {
(): React.ComponentClass
}
export interface menuItem {
id: string,
text: string,
active ?: boolean
active ?: boolean,
view: IComponentLambda
}
export const mainMenu: menuItem[] = [
{
id: 'profile',
text: 'Profile',
active: true
id: 'info',
text: 'Info',
active: true,
view: ()=>Info
},
{
id: 'constants',
text: 'Constants'
}];
\ No newline at end of file
text: 'Constants',
view: ()=>Constants
},
{
id: 'profile',
text: 'Profile',
active: true,
view: ()=>Profile
}];
setActiveMenu(mainMenu.filter((el)=>el.active)[0].id)
import React from "react";
import {connect} from "react-redux";
import {ILocaleHash, IMainStorage} from "../action-reducers";
import {AZ} from "../helpers";
import {Constant} from "../component/Constant";
import {Button, Container, List, ListItem, ListSubheader, TextField} from "@material-ui/core";
import {NewLocale} from "../component/NewLocale";
export interface IConstants {
locale: ILocaleHash
}
class ConstantsView extends React.Component<IConstants, IConstants> {
render(): React.ReactNode {
const {locale} = this.props,
localeList = Object.keys(locale).filter((key)=>key.charAt(0)!=='_').map((key)=>locale[key]).sort(AZ('id'));
return <Container>
<NewLocale/>
{localeList.map((item)=><Constant key={item.id} item={item}/>)}
</Container>;
}
}
const ConstantsMap = function (store: IMainStorage): IConstants {
return {locale: store.locale};
};
export const Constants = connect(ConstantsMap)(ConstantsView);
\ No newline at end of file
import React from "react";
import {connect} from "react-redux";
import {IMainStorage} from "../action-reducers";
class InfoView extends React.Component<{}, {}> {
render(): React.ReactNode {
return <div>
Этот бот может расширять свой лексикон
</div>;
}
}
export const Info = InfoView;
\ No newline at end of file
import React from "react";
import {connect} from "react-redux";
import {IMainStorage} from "../action-reducers";
export interface IProfile {
}
class ProfileView extends React.Component<IProfile, IProfile> {
render(): React.ReactNode {
return null;
}
}
const ProfileMap = function (store: IMainStorage): IProfile {
return {};
};
export const Profile = connect(ProfileMap)(ProfileView);
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment