Create app namespace for all absolute requires of app modules. Move all packages under packages yarn workspace

This commit is contained in:
SleepWalker
2019-12-07 21:02:00 +02:00
parent d8d2df0702
commit f9d3bb4e20
404 changed files with 758 additions and 742 deletions

View File

@@ -0,0 +1,24 @@
{
"title": "Site rules",
"mainProvisions": "Main provisions",
"mainProvision1": "{name} service was created for the organization of safety access to Ely.by's users accounts, his partners and any side project that wish to use one of the our's services.",
"mainProvision2": "We (here and in the next points) — Ely.by project developers team that make creating qualitative services for Minecraft community.",
"mainProvision3": "Ely.by is side project, that has nothing to do with Mojang and Microsoft companies. We don't provide support to Minecraft premium accounts, and we have nothing to do with servers that use or don't use our services.",
"mainProvision4": "The registration of the users account at server is free. Account creation Ely.by is only possible at that page {link}.",
"emailAndNickname": "Email and nickname",
"emailAndNickname1": "Account registration with usage of temporary mail services is prohibited. We speak about services that gives random Email in any quantity.",
"emailAndNickname2": "We try to counteract it, but if you succesed in registration of account with usage of temporary mail services, there wont be any technical support for it and later, during of update of ours filters, account will be blocked with your nickname.",
"emailAndNickname3": "There are no any moral restrictions for users nickname that will be used in game.",
"emailAndNickname4": "Nicknames, belonging to famous persons, can be released at their favor for requirement and proves of that persons.",
"emailAndNickname5": "Minecraft premium account owner has right to require a control restore of his nickname an if it happened you have to change your nickname in 3 days or it will be done automatically.",
"emailAndNickname6": "If there is no any activity at your account during last 3 month, your nickname can be occupied by any user.",
"emailAndNickname7": "We aren't responsible for losing your game progress at servers if it was result of nickname changing, including changes on our demand.",
"elyAccountsAsService": "{name} as service",
"elyAccountsAsServiceDesc1": "{name} has free providing to any project, that interested in it usage for Minecraft.",
"elyAccountsAsServiceDesc2": "Despite we do our utmost to provide fast and stable work of service, we are not saved from DDOS-attack, hosters links work interruptions, electricity disorders or any cases, that impossible to be predicted. For avoiding possible incomprehension, we obliged to discuss next agreements, that will work in case of situations mentioned before:",
"elyAccountsAsService1": "We don't have any guarantee about fault free work time of this service.",
"elyAccountsAsService2": "We are not responsible for delays and lost income as the result of ours service inoperability."
}

View File

@@ -0,0 +1,81 @@
import React from 'react';
import sinon from 'sinon';
import expect from 'app/test/unexpected';
import { shallow } from 'enzyme';
import RulesPage from './RulesPage';
describe('RulesPage', () => {
describe('#onRuleClick()', () => {
const id = 'rule-1-2';
const pathname = '/foo';
const search = '?bar';
let page;
let replace;
beforeEach(() => {
replace = sinon.stub().named('history.replace');
page = shallow(
<RulesPage
location={{ pathname, search } as any}
history={{ replace }}
/>,
);
});
it('should update location on rule click', () => {
const expectedUrl = `/foo?bar#${id}`;
page.find(`#${id}`).simulate('click', {
target: document.createElement('li'),
currentTarget: {
id,
},
});
expect(replace, 'to have a call satisfying', [expectedUrl]);
});
it('should not update location if link was clicked', () => {
page.find(`#${id}`).simulate('click', {
target: document.createElement('a'),
currentTarget: {
id,
},
});
expect(replace, 'was not called');
});
it('should not update location if defaultPrevented', () => {
page.find(`#${id}`).simulate('click', {
defaultPrevented: true,
target: {
tagName: 'li',
},
currentTarget: {
id,
},
});
expect(replace, 'was not called');
});
it('should not update location if no id', () => {
page.find(`#${id}`).simulate('click', {
target: {
tagName: 'li',
},
currentTarget: {},
});
expect(replace, 'was not called');
});
});
});

View File

@@ -0,0 +1,175 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { FormattedMessage as Message } from 'react-intl';
import Helmet from 'react-helmet';
import { FooterMenu } from 'app/components/footerMenu';
import appInfo from 'app/components/auth/appInfo/AppInfo.intl.json';
import styles from './rules.scss';
import messages from './RulesPage.intl.json';
const projectName = <Message {...appInfo.appName} />;
import classNames from 'classnames';
const rules = [
{
title: <Message {...messages.mainProvisions} />,
items: [
<Message
key="0"
{...messages.mainProvision1}
values={{
name: <b>{projectName}</b>,
}}
/>,
<Message key="1" {...messages.mainProvision2} />,
<Message key="2" {...messages.mainProvision3} />,
<Message
key="3"
{...messages.mainProvision4}
values={{
link: <Link to="/register">https://account.ely.by/register</Link>,
}}
/>,
],
},
{
title: <Message {...messages.emailAndNickname} />,
items: [
<Message key="0" {...messages.emailAndNickname1} />,
<Message key="1" {...messages.emailAndNickname2} />,
<Message key="2" {...messages.emailAndNickname3} />,
<Message key="3" {...messages.emailAndNickname4} />,
<Message key="4" {...messages.emailAndNickname5} />,
<Message key="5" {...messages.emailAndNickname6} />,
<Message key="6" {...messages.emailAndNickname7} />,
],
},
{
title: (
<Message
{...messages.elyAccountsAsService}
values={{
name: projectName,
}}
/>
),
description: (
<div>
<p>
<Message
{...messages.elyAccountsAsServiceDesc1}
values={{
name: <b>{projectName}</b>,
}}
/>
</p>
<p>
<Message {...messages.elyAccountsAsServiceDesc2} />
</p>
</div>
),
items: [
<Message key="0" {...messages.elyAccountsAsService1} />,
<Message key="1" {...messages.elyAccountsAsService2} />,
],
},
];
export default class RulesPage extends Component<{
location: {
pathname: string;
search: string;
hash: string;
};
history: {
replace: Function;
};
}> {
render() {
let { hash } = this.props.location;
if (hash) {
hash = hash.substring(1);
}
return (
<div>
<Message {...messages.title}>
{pageTitle => <Helmet title={pageTitle} />}
</Message>
<div className={styles.rules}>
{rules.map((block, sectionIndex) => (
<div className={styles.rulesSection} key={sectionIndex}>
<h2
className={classNames(styles.rulesSectionTitle, {
[styles.target]:
RulesPage.getTitleHash(sectionIndex) === hash,
})}
id={RulesPage.getTitleHash(sectionIndex)}
>
{block.title}
</h2>
<div className={styles.rulesBody}>
{block.description ? (
<div className={styles.blockDescription}>
{block.description}
</div>
) : (
''
)}
<ol className={styles.rulesList}>
{block.items.map((item, ruleIndex) => (
<li
className={classNames(styles.rulesItem, {
[styles.target]:
RulesPage.getRuleHash(sectionIndex, ruleIndex) ===
hash,
})}
key={ruleIndex}
id={RulesPage.getRuleHash(sectionIndex, ruleIndex)}
onClick={this.onRuleClick.bind(this)}
>
{item}
</li>
))}
</ol>
</div>
</div>
))}
</div>
<div className={styles.footer}>
<FooterMenu />
</div>
</div>
);
}
onRuleClick(event: React.SyntheticEvent<HTMLElement>) {
if (
event.defaultPrevented ||
!event.currentTarget.id ||
event.target instanceof HTMLAnchorElement
) {
// some-one have already processed this event or it is a link
return;
}
const { id } = event.currentTarget;
const newPath = `${this.props.location.pathname}${this.props.location.search}#${id}`;
this.props.history.replace(newPath);
}
static getTitleHash(sectionIndex: number) {
return `rule-${sectionIndex + 1}`;
}
static getRuleHash(sectionIndex: number, ruleIndex: number) {
return `${RulesPage.getTitleHash(sectionIndex)}-${ruleIndex + 1}`;
}
}

View File

@@ -0,0 +1,98 @@
@import '~app/components/ui/colors.scss';
@import '~app/components/ui/fonts.scss';
.rules {
max-width: 500px;
margin: 30px auto 0;
padding: 0 20px;
}
.rulesSection {
margin-bottom: 30px;
}
.rulesSectionTitle {
line-height: 50px;
font-family: $font-family-title;
font-size: 20px;
color: #fff;
padding: 0;
margin: 0;
text-align: center;
background: $blue;
}
.rulesBody {
position: relative;
// z-index, чтобы положить :before ниже текста, но выше фона блока
z-index: 0;
padding: 20px;
background: #fff;
font-size: 14px;
}
%rulesTextFormat {
line-height: 1.4;
margin-bottom: 10px;
}
.blockDescription {
@extend %rulesTextFormat;
p {
@extend %rulesTextFormat;
}
}
.rulesList {
padding: 0;
margin: 0;
padding-left: 20px;
}
.rulesItem {
@extend %rulesTextFormat;
list-style: decimal;
position: relative;
cursor: pointer;
&:last-of-type {
margin-bottom: 0;
}
&.target {
&:before {
cursor: default;
$border: 8px solid #ddd8ce;
content: '';
position: absolute;
top: -10px;
left: -40px;
width: calc(100% + 60px);
height: calc(100% + 20px);
background: $white;
border-left: $border;
border-right: $border;
box-sizing: border-box;
z-index: -1;
}
}
a {
color: #444;
border-bottom-color: #aaa;
&:hover {
border-bottom-color: #444;
}
}
}
.footer {
text-align: center;
margin-bottom: 20px;
}