diff options
Diffstat (limited to 'vnext/src')
-rw-r--r-- | vnext/src/app.js | 62 | ||||
-rw-r--r-- | vnext/src/components/Discover.js | 35 | ||||
-rw-r--r-- | vnext/src/components/Icon.js | 41 | ||||
-rw-r--r-- | vnext/src/components/LoginButton.js | 18 | ||||
-rw-r--r-- | vnext/src/components/Message.js | 20 | ||||
-rw-r--r-- | vnext/src/components/Post.js | 33 | ||||
-rw-r--r-- | vnext/src/components/Thread.js | 10 | ||||
-rw-r--r-- | vnext/src/index.js | 70 | ||||
-rw-r--r-- | vnext/src/style/main.css | 81 | ||||
-rw-r--r-- | vnext/src/views/index.html | 5 |
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> |