aboutsummaryrefslogtreecommitdiff
path: root/vnext/src
diff options
context:
space:
mode:
Diffstat (limited to 'vnext/src')
-rw-r--r--vnext/src/app.js62
-rw-r--r--vnext/src/components/Discover.js35
-rw-r--r--vnext/src/components/Icon.js41
-rw-r--r--vnext/src/components/LoginButton.js18
-rw-r--r--vnext/src/components/Message.js20
-rw-r--r--vnext/src/components/Post.js33
-rw-r--r--vnext/src/components/Thread.js10
-rw-r--r--vnext/src/index.js70
-rw-r--r--vnext/src/style/main.css81
-rw-r--r--vnext/src/views/index.html5
10 files changed, 228 insertions, 147 deletions
diff --git a/vnext/src/app.js b/vnext/src/app.js
deleted file mode 100644
index 1f323b51..00000000
--- a/vnext/src/app.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import React from "react"
-import ReactDOM from "react-dom"
-import { BrowserRouter as Router, Route, Link } from "react-router-dom"
-
-import Discover from "./components/Discover"
-import Post from "./components/Post"
-import Thread from "./components/Thread"
-import LoginButton from "./components/LoginButton"
-
-class App extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- visitor: { uid: 0 }
- }
- }
- render() {
- return (
- <Router>
- <div className="wrapper">
- <header>
- <div id="header_wrapper">
- <div id="logo"><Link to="/">Juick</Link></div>
- <nav id="global">
- <ul>
- {this.state.visitor.uid ?
- <li><Link to={{ pathname: "/", search: "?show=discuss"}}><i data-icon="ei-comment" data-size="s"></i>Discuss</Link></li>
- :
- <li><Link to={{ pathname: "/", search: "?media=1"}} rel="nofollow"><i data-icon="ei-camera" data-size="s"></i>Photos</Link></li>
- }
- <li><Link to="/" rel="nofollow"><i data-icon="ei-search" data-size="s"></i>Discover</Link></li>
- <li>
- {this.state.visitor.uid ?
- <Link to="post" href="/post"><i data-icon="ei-pencil" data-size="s"></i>Post</Link>
- :
- <LoginButton title="Login" onAuth={this.auth.bind(this)} />
- }
- </li>
- </ul>
- </nav>
- <div id="search">
- <form action="/">
- <input name="search" className="text"
- placeholder="Search..." />
- </form>
- </div>
- </div>
- </header>
- <Route exact path="/" component={Discover} />
- <Route path="/:user/:mid" component={Thread} />
- <Route path="/post" component={Post} />
- </div>
- </Router>
- )
- }
- auth(data) {
- console.log(data)
- }
-}
-
-
-ReactDOM.render(<App />, document.getElementById("wrapper"));
diff --git a/vnext/src/components/Discover.js b/vnext/src/components/Discover.js
index 7fde3d88..63a0cad0 100644
--- a/vnext/src/components/Discover.js
+++ b/vnext/src/components/Discover.js
@@ -1,30 +1,25 @@
-import "whatwg-fetch"
-import React from "react"
-import PropTypes from "prop-types"
-import queryString from "query-string"
+import 'whatwg-fetch';
+import React from 'react';
+import PropTypes from 'prop-types';
+import queryString from 'query-string';
-import Message from "./Message"
+import Message from './Message';
export default class Discover extends React.Component {
constructor(props) {
- super(props)
+ super(props);
this.state = {
msgs: [],
loading: false,
search: this.props.location.search
- }
+ };
this.loadMessages = this.loadMessages.bind(this);
}
componentDidMount() {
this.loadMessages();
}
- componentWillReceiveProps(props) {
- if (props.params != this.props.params) {
- this.loadMessages();
- }
- }
loadMessages() {
- const url = "https://api.juick.com/messages" + this.state.search;
+ const url = 'https://api.juick.com/messages' + this.state.search;
fetch(url)
.then(response => {
return response.json()
@@ -32,20 +27,20 @@ export default class Discover extends React.Component {
.then(data =>
this.setState({ msgs: data })
).catch(ex => {
- console.log(ex)
+ console.log(ex);
});
}
- render() {
+ render() {
var nodes = this.state.msgs.map(msg => {
- return (<Message key={msg.mid} data={msg}/>)
- });
+ return (<Message key={msg.mid} data={msg}/>);
+ });
return (
<div className="msgs" id="content">{nodes}</div>
- )
+ );
}
-};
+}
Discover.propTypes = {
msgs: PropTypes.array
-}
+};
diff --git a/vnext/src/components/Icon.js b/vnext/src/components/Icon.js
new file mode 100644
index 00000000..b4e8804c
--- /dev/null
+++ b/vnext/src/components/Icon.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+export default class Icon extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ var size = this.props.size ? ' icon--' + this.props.size : '';
+ var className = this.props.className ? ' ' + this.props.className : '';
+ var klass = 'icon icon--' + this.props.name + size + className;
+
+ var name = '#' + this.props.name + '-icon';
+ var useTag = '<use xlink:href=' + name + ' />';
+ var Icon = React.createElement('svg', { className: 'icon__cnt', dangerouslySetInnerHTML: { __html: useTag } });
+ return React.createElement(
+ 'div',
+ { className: klass },
+ this.wrapSpinner(Icon, klass)
+ );
+ }
+
+ wrapSpinner(Html, klass) {
+ if (klass.indexOf('spinner') > -1) {
+ return React.createElement(
+ 'div',
+ { className: 'icon__spinner' },
+ Html
+ );
+ } else {
+ return Html;
+ }
+ }
+}
+
+Icon.propTypes = {
+ size: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ className: PropTypes.string
+}
diff --git a/vnext/src/components/LoginButton.js b/vnext/src/components/LoginButton.js
index 8d298fa2..f35a932d 100644
--- a/vnext/src/components/LoginButton.js
+++ b/vnext/src/components/LoginButton.js
@@ -1,27 +1,29 @@
-import React from 'react'
-import PropTypes from 'prop-types'
+import React from 'react';
+import PropTypes from 'prop-types';
+import Icon from './Icon';
+
export default class LoginButton extends React.Component {
constructor(props) {
super(props);
- window.addEventListener("message", (event) => {
+ window.addEventListener('message', (event) => {
this.props.onAuth(event.data);
}, false);
}
login(event) {
event.preventDefault();
- let loginWindow = window.open("https://juick.com/login?redirect=false", "Login to Juick", "width=400,height=300,resizeable=no,menubar=no,toolbar=no,scrollbars=no");
+ let loginWindow = window.open('https://juick.com/login?redirect=false', 'Login to Juick', 'width=400,height=300,resizeable=no,menubar=no,toolbar=no,scrollbars=no');
loginWindow.window.focus();
}
render() {
return (
- <a href="/login" onClick={this.login}>{this.props.title}</a>
- )
+ <a href="/login" onClick={this.login}><Icon name="ei-user" size="s"/>{this.props.title}</a>
+ );
}
-};
+}
LoginButton.propTypes = {
title: PropTypes.string.isRequired,
onAuth: PropTypes.func.isRequired
-} \ No newline at end of file
+};
diff --git a/vnext/src/components/Message.js b/vnext/src/components/Message.js
index ba853f5c..d3148141 100644
--- a/vnext/src/components/Message.js
+++ b/vnext/src/components/Message.js
@@ -1,9 +1,9 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import ReactMarkdown from 'react-markdown'
+import React from 'react';
+import PropTypes from 'prop-types';
+import ReactMarkdown from 'react-markdown';
export default class Message extends React.Component {
- render() {
+ render() {
const msg = this.props.data;
return (
<article itemProp="blogPost" itemScope="" itemType="http://schema.org/BlogPosting" itemRef="org">
@@ -27,21 +27,22 @@ export default class Message extends React.Component {
</div>
</header>
<ReactMarkdown source={msg.body}/>
- { msg.photo &&
+ { msg.photo &&
<p className="ir"><a href={`//i.juick.com/p/${msg.mid}.${msg.attach}`} data-fname={`${msg.mid}.${msg.attach}`}>
<img itemProp="image" src={`//i.juick.com/p/${msg.mid}.${msg.attach}`} alt=""/></a>
</p>
}
</article>
- )}
-};
+ );
+ }
+}
function Tags(props) {
return props.data && props.data.map(tag => { return (<a key={tag} href={`/tag/${ tag}`} title={ tag }>{ tag }</a>) })
}
Message.propTypes = {
- msg: PropTypes.shape({
+ data: PropTypes.shape({
mid: PropTypes.number.isRequired,
user: PropTypes.shape({
uid: PropTypes.number.isRequired,
@@ -50,4 +51,5 @@ Message.propTypes = {
timestamp: PropTypes.string.isRequired,
body: PropTypes.string
})
-}
+};
+
diff --git a/vnext/src/components/Post.js b/vnext/src/components/Post.js
index 54be77df..3256bbf6 100644
--- a/vnext/src/components/Post.js
+++ b/vnext/src/components/Post.js
@@ -1,20 +1,21 @@
-import React from 'react'
+import React from 'react';
export default class Post extends React.Component {
- render() {
+ render() {
return (
<article>
-<form action="/" method="post" id="postmsg" encType="multipart/form-data">
- <p style={{ textAlign: 'left' }}>
- <b>Фото:</b> <span id="attachmentfile">
- <input style={{ width: '100%' }} type="file" name="attach"/> <i>(JPG/PNG)</i></span>
- </p>
- <p>
- <textarea name="body" className="newmessage" rows="7" cols="10" placeholder="*weather It's very cold today!"></textarea>
- <br/>
- <input type="submit" className="subm" value=" POST "/>
- </p>
-</form>
-</article>
- )}
-}; \ No newline at end of file
+ <form action="/" method="post" id="postmsg" encType="multipart/form-data">
+ <p style={{ textAlign: 'left' }}>
+ <b>Фото:</b> <span id="attachmentfile">
+ <input style={{ width: '100%' }} type="file" name="attach"/> <i>(JPG/PNG)</i></span>
+ </p>
+ <p>
+ <textarea name="body" className="newmessage" rows="7" cols="10" placeholder="*weather It's very cold today!"></textarea>
+ <br/>
+ <input type="submit" className="subm" value=" POST "/>
+ </p>
+ </form>
+ </article>
+ );
+ }
+}
diff --git a/vnext/src/components/Thread.js b/vnext/src/components/Thread.js
index 49d9ea4d..a17a2022 100644
--- a/vnext/src/components/Thread.js
+++ b/vnext/src/components/Thread.js
@@ -1,5 +1,5 @@
-import "whatwg-fetch"
-import React from "react"
+import 'whatwg-fetch';
+import React from 'react';
export default class Thread extends React.Component {
constructor(props) {
@@ -7,7 +7,7 @@ export default class Thread extends React.Component {
this.state = {
replies: [],
loading: false
- }
+ };
}
componentDidMount() {
}
@@ -18,6 +18,6 @@ export default class Thread extends React.Component {
<h1>{user}</h1>
<p>{mid}</p>
</div>
- )
+ );
}
-} \ No newline at end of file
+}
diff --git a/vnext/src/index.js b/vnext/src/index.js
new file mode 100644
index 00000000..97431188
--- /dev/null
+++ b/vnext/src/index.js
@@ -0,0 +1,70 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
+import Icon from './components/Icon';
+import Discover from './components/Discover';
+import Post from './components/Post';
+import Thread from './components/Thread';
+import LoginButton from './components/LoginButton';
+
+class App extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ visitor: { uid: 0 }
+ };
+ }
+ render() {
+ return (
+ <Router>
+ <div className="wrapper">
+ <header>
+ <div id="header_wrapper">
+ {this.state.visitor.uid > 0 ?
+ <div id="ctitle">
+ <a href="/{this.state.visitor.name}">
+ <img src="//i.juick.com/a/{this.state.visitor.uid}.png" alt=""/>{this.state.visitor.name}
+ </a>
+ </div>
+ :
+ <div id="logo"><Link to="/">Juick</Link></div>
+ }
+ <div id="search">
+ <form action="/">
+ <input name="search" className="text"
+ placeholder="Search..." />
+ </form>
+ </div>
+ <nav id="global">
+ <ul>
+ {this.state.visitor.uid > 0 ?
+ <li><Link to={{ pathname: '/', search: '?show=discuss'}}><Icon name="ei-comment" size="s"/>Discuss</Link></li>
+ :
+ <li><Link to={{ pathname: '/', search: '?media=1'}} rel="nofollow"><Icon name="ei-camera" size="s"/>Photos</Link></li>
+ }
+ <li><Link to="/" rel="nofollow"><Icon name="ei-search" size="s"/>Discover</Link></li>
+ <li>
+ {this.state.visitor.uid > 0 ?
+ <Link to="post" href="/post"><Icon name="ei-pencil" size="s"/>Post</Link>
+ :
+ <LoginButton title="Login" onAuth={this.auth.bind(this)} />
+ }
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <Route exact path="/" component={Discover} />
+ <Route path="/:user/:mid" component={Thread} />
+ <Route path="/post" component={Post} />
+ </div>
+ </Router>
+ )
+ }
+ auth(data) {
+ console.log(data)
+ }
+}
+
+
+ReactDOM.render(<App />, document.getElementById('wrapper'));
diff --git a/vnext/src/style/main.css b/vnext/src/style/main.css
index 24108030..69c29f07 100644
--- a/vnext/src/style/main.css
+++ b/vnext/src/style/main.css
@@ -85,6 +85,11 @@ html {
margin: 12px 0 0 0;
width: 728px;
}
+#minimal_content {
+ margin: 0 auto;
+ min-width: 310px;
+ width: auto;
+}
body > header {
box-shadow: 0 0 3px rgba(0, 0, 0, 0.28);
background: #fff;
@@ -92,11 +97,26 @@ body > header {
top: 0;
width: 100%;
z-index: 10;
+ -webkit-transition-duration: 0.5s;
+ transition-duration: 0.5s;
+ -webkit-transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
+ transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
+ -webkit-transition-property: -webkit-transform;
+ transition-property: transform;
}
#header_wrapper {
margin: 0 auto;
width: 1000px;
display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ padding: 4px;
+}
+.header--hidden {
+ -webkit-transform: translateY(-100%);
+ -ms-transform: translateY(-100%);
+ transform: translateY(-100%);
}
#footer {
clear: both;
@@ -142,7 +162,6 @@ body > header {
#logo {
height: 36px;
- margin: 7px 25px 0 20px;
width: 110px;
}
#logo a {
@@ -157,7 +176,6 @@ body > header {
width: 110px;
}
#global {
- flex-grow: 1;
display: flex;
}
#global a {
@@ -169,31 +187,24 @@ body > header {
#global li {
display: inline-block;
}
+#ctitle a {
+ padding: 14px;
+}
#global li:hover,
+#ctitle a:hover,
.l a:hover {
background-color: #fff;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.16);
cursor: pointer;
transition: box-shadow 0.2s ease-in;
}
-#search {
- margin: 12px 20px 12px 0;
-}
#search input {
background: #FFF;
- border: 1px solid #DDDDD5;
+ border: 1px solid #ccc;
outline: none !important;
padding: 4px;
-}
-@media screen and (max-width: 850px) {
- #logo {
- display: none;
- }
- #search {
- display: inline-block;
- float: none;
- margin: 10px 10px;
- }
+ -webkit-appearance: none;
+ border-radius: 0;
}
/* #endregion */
@@ -262,10 +273,12 @@ body > header {
/* #endregion */
/* #region main content */
-
#content > p,
#content > h1,
-#content > h2 {
+#content > h2,
+#minimal_content > p,
+#minimal_content > h1,
+#minimal_content > h2 {
margin: 1em 0;
}
.page {
@@ -331,10 +344,11 @@ article .tags {
margin-top: 3px;
}
.msg-tags {
- margin-top: 5px;
- min-height: 30px;
+ margin-top: 12px;
+ min-height: 1px;
}
article .tags > a,
+.badge,
.msg-tags > a {
background: #eee;
border: 1px solid #eee;
@@ -382,7 +396,6 @@ article .tags > a,
border-right: 5px solid #0C0;
}
.msg-ts {
- float: right;
font-size: small;
vertical-align: top;
}
@@ -478,6 +491,16 @@ article .tags > a,
}
@media screen and (max-width: 850px) {
+ #header_wrapper {
+ width: auto;
+ }
+ #global {
+ justify-content: space-around;
+ flex-grow: 1;
+ }
+ #search {
+ padding: 4px;
+ }
article {
overflow: auto;
}
@@ -509,12 +532,18 @@ article .tags > a,
}
@media screen and (max-width: 480px) {
- .msg-ts {
- float: none;
+ #wrapper {
+ margin-top: 104px;
+ }
+ #search {
+ display: none;
+ }
+ #global a {
+ padding: 14px 2px;
}
- .msg-tags {
- margin-top: 10px;
- min-height: 1px;
+ .msg-cont > nav.l,
+ article > nav.l {
+ font-size: 9pt;
}
.msg-txt {
padding-top: 5px;
diff --git a/vnext/src/views/index.html b/vnext/src/views/index.html
index 661634d6..74583a13 100644
--- a/vnext/src/views/index.html
+++ b/vnext/src/views/index.html
@@ -13,6 +13,9 @@
<meta name="msapplication-TileImage" content="//i.juick.com/ms-icon-144x144.png"/>
<meta name="theme-color" content="#ffffff"/>
<meta name="apple-mobile-web-app-capable" content="yes" />
+ <link rel="stylesheet" href="/Juick.css"/>
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/evil-icons@1.9.0/assets/evil-icons.min.css"/>
+ <script src="https://cdn.jsdelivr.net/npm/evil-icons@1.9.0/assets/evil-icons.min.js"></script>
<link rel="apple-touch-icon" sizes="57x57" href="//i.juick.com/apple-icon-57x57.png"/>
<link rel="apple-touch-icon" sizes="60x60" href="//i.juick.com/apple-icon-60x60.png"/>
<link rel="apple-touch-icon" sizes="72x72" href="//i.juick.com/apple-icon-72x72.png"/>
@@ -40,6 +43,6 @@
</head>
<body id="body">
<div id="wrapper"></div>
-<script type="text/javascript" src="/App.js"></script>
+<script type="text/javascript" src="/Juick.js"></script>
</body>
</html>