aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--vnext/src/App.js282
-rw-r--r--vnext/src/components/Chat.js98
-rw-r--r--vnext/src/components/Thread.js12
3 files changed, 180 insertions, 212 deletions
diff --git a/vnext/src/App.js b/vnext/src/App.js
index 7978abe5..6fcf3d9c 100644
--- a/vnext/src/App.js
+++ b/vnext/src/App.js
@@ -1,6 +1,6 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom';
-import * as qs from 'qs';
+import qs from 'qs';
import Icon from './components/Icon';
import { Discover, Discussions, Blog, Tag, Home } from './components/Feeds';
@@ -11,179 +11,149 @@ import Chat from './components/Chat';
import Post from './components/Post';
import Thread from './components/Thread';
import LoginButton from './components/LoginButton';
-import Avatar from './components/Avatar';
import { UserLink } from './components/UserInfo';
import Header from './components/Header';
import SearchBox from './components/SearchBox';
-import NavigationIcon from './components/NavigationIcon';
import cookies from 'react-cookies';
import { me } from './api';
-const app = document.getElementById('app');
-
-export default class App extends React.Component {
- constructor(props) {
- super(props);
- let params = qs.parse(window.location.search.substring(1));
- if (params.hash) {
- cookies.save('hash', params.hash, { path: '/' });
- window.history.replaceState({}, document.title, `${window.location.protocol}//${window.location.host}${window.location.pathname}`);
- }
- this.state = {
- visitor: {
- uid: 0,
- hash: cookies.load('hash')
- },
- appMarginLeft: 'inherit'
- };
- this.pm = React.createRef();
- this.thread = React.createRef();
- this.sidebar = React.createRef();
+export default function App(props) {
+ let params = qs.parse(window.location.search.substring(1));
+ if (params.hash) {
+ cookies.save('hash', params.hash, { path: '/' });
+ window.history.replaceState({}, document.title, `${window.location.protocol}//${window.location.host}${window.location.pathname}`);
}
+ const [visitor, setVisitor] = useState({
+ uid: 0,
+ hash: cookies.load('hash')
+ });
+
+ let updateStatus = () => {
+ // refresh server visitor state (unread counters)
+ me().then(visitor => {
+ setVisitor(visitor);
+ });
+ };
- initES = () => {
- if (!('EventSource' in window)) {
- return;
+ const [es, setEs] = useState();
+ useEffect(() => {
+ const { hash } = visitor;
+ if (hash) {
+ me().then(visitor => auth(visitor));
}
- const params = { hash: this.state.visitor.hash };
- let url = new URL(`https://api.juick.com/events?${qs.stringify(params)}`);
- this.es = new EventSource(url);
- this.es.onopen = () => {
+ const eventParams = { hash: visitor.hash };
+ let url = new URL(`https://api.juick.com/events?${qs.stringify(eventParams)}`);
+ let es = new EventSource(url);
+ es.onopen = () => {
console.log('online');
+ es.addEventListener('read', updateStatus);
};
- this.es.addEventListener('msg', msg => {
- try {
- var jsonMsg = JSON.parse(msg.data);
- console.log('data: ' + msg.data);
- // refresh server visitor state (unread counters)
- me().then(visitor => {
- this.setState({
- visitor: visitor
- });
- });
- if (jsonMsg.service) {
- return;
- }
- if (!jsonMsg.mid) {
- this.pm.current.onMessage(jsonMsg);
- }
- } catch (err) {
- console.log(err);
- }
- });
- }
-
- componentDidMount() {
- const { hash } = this.state.visitor;
- this.initES();
- if (hash) {
- me().then(visitor => this.auth(visitor));
+ es.onerror = () => {
+ es.removeEventListener('read', updateStatus);
}
- }
- search = (history, pathname, searchString) => {
+ setEs(es);
+ }, []);
+
+
+ let search = (history, pathname, searchString) => {
let location = {};
location.pathname = pathname;
location.search = `?search=${searchString}`;
history.push(location);
}
- render() {
- const user = this.state.visitor;
- return (
- <Router>
- <>
- <Header>
- <div id="header_wrapper">
- {
- user.uid > 0 ?
- <UserLink user={user} />
- : <div id="logo">
- <Link to="/">Juick</Link>
- </div>
- }
- <div id="search" className="desktop">
- <SearchBox pathname="/discover" onSearch={this.search} {...this.props} />
- </div>
- <nav id="global">
- {user.uid > 0 ?
- <Link to={{ pathname: '/' }}>
- <Icon name="ei-bell" size="s" /><span className="desktop">Discuss</span>
- {
- user.unreadCount &&
- <span className="badge">{user.unreadCount}</span>
- }
- </Link>
- :
- <Link to='/?media=1' rel="nofollow">
- <Icon name="ei-camera" size="s" />
- <span className="desktop">Photos</span>
- </Link>
- }
- <Link to={{ pathname: '/discover' }} rel="nofollow">
- <Icon name="ei-search" size="s" />
- <span className="desktop">Discover</span>
- </Link>
-
- {user.uid > 0 ?
- <Link to={{ pathname: '/post' }}>
- <Icon name="ei-pencil" size="s" />
- <span className="desktop">Post</span>
- </Link>
- :
- <LoginButton title="Login" onAuth={this.auth} />
- }
- </nav>
+ let auth = (visitor) => {
+ setVisitor(visitor);
+ }
+ return (
+ <Router>
+ <>
+ <Header>
+ <div id="header_wrapper">
+ {
+ visitor.uid > 0 ?
+ <UserLink user={visitor} />
+ : <div id="logo">
+ <Link to="/">Juick</Link>
+ </div>
+ }
+ <div id="search" className="desktop">
+ <SearchBox pathname="/discover" onSearch={search} {...props} />
</div>
- </Header>
- <section id="content">
- <Switch>
- <Route exact path="/" render={(props) => <Discussions visitor={user} {...props} />} />
- <Route exact path="/home" render={(props) => <Home visitor={user} {...props} />} />
- <Route exact path="/discover" render={(props) =>
- <Discover visitor={user} {...props} />
- } />
- <Route exact path="/settings" render={(props) =>
- <Settings visitor={user} {...props} onChange={this.auth} />
- } />
- <Route exact path="/post" render={(props) => <Post visitor={user} {...props} />} />
- <Route exact path="/pm" render={(props) => <Contacts visitor={user} {...props} />} />
- <Route exact path="/pm/:user" render={(props) => <Chat connection={this.es} visitor={user} {...props} />} />
- <Route exact path="/:user/friends" render={(props) => <Friends user={props.match.params.user} {...props} />} />
- <Route exact path="/:user/readers" render={(props) => <Readers user={props.match.params.user} {...props} />} />
- <Route exact path="/:user" render={(props) => <Blog key={props.match.params.user} visitor={user} {...props} />} />
- <Route exact path="/tag/:tag" render={(props) => <Tag visitor={user} {...props} />} />
- <Route exact path="/:user/:mid" render={(props) => <Thread connection={this.es} visitor={user} {...props} />} />
- </Switch>
- </section>
- {
- user.uid > 0 &&
- <aside id="sidebar" ref={this.sidebar}>
- <Link to="/?show=my" onClick={this.toggleSidebar}>
- <Icon name="ei-clock" size="s" />
- <span className="desktop">My feed</span>
- </Link>
- <Link to="/pm" onClick={this.toggleSidebar}>
- <Icon name="ei-envelope" size="s" />
- <span className="desktop">Messages</span>
- </Link>
- <Link to="/?show=discuss" onClick={this.toggleSidebar}>
- <Icon name="ei-bell" size="s" />
- <span className="desktop">Discussions</span>
- </Link>
- <Link to="/settings" rel="nofollow" onClick={this.toggleSidebar}>
- <Icon name="ei-gear" size="s" />
- <span className="desktop">Settings</span>
+ <nav id="global">
+ {visitor.uid > 0 ?
+ <Link to={{ pathname: '/' }}>
+ <Icon name="ei-bell" size="s" /><span className="desktop">Discuss</span>
+ {
+ visitor.unreadCount &&
+ <span className="badge">{visitor.unreadCount}</span>
+ }
+ </Link>
+ :
+ <Link to='/?media=1' rel="nofollow">
+ <Icon name="ei-camera" size="s" />
+ <span className="desktop">Photos</span>
+ </Link>
+ }
+ <Link to={{ pathname: '/discover' }} rel="nofollow">
+ <Icon name="ei-search" size="s" />
+ <span className="desktop">Discover</span>
</Link>
- </aside>
- }
- </>
- </Router>
- );
- }
- auth = (visitor) => {
- this.setState({
- visitor: visitor
- });
- }
+
+ {visitor.uid > 0 ?
+ <Link to={{ pathname: '/post' }}>
+ <Icon name="ei-pencil" size="s" />
+ <span className="desktop">Post</span>
+ </Link>
+ :
+ <LoginButton title="Login" onAuth={auth} />
+ }
+ </nav>
+ </div>
+ </Header>
+ <section id="content">
+ <Switch>
+ <Route exact path="/" render={(props) => <Discussions visitor={visitor} {...props} />} />
+ <Route exact path="/home" render={(props) => <Home visitor={visitor} {...props} />} />
+ <Route exact path="/discover" render={(props) =>
+ <Discover visitor={visitor} {...props} />
+ } />
+ <Route exact path="/settings" render={(props) =>
+ <Settings visitor={visitor} {...props} onChange={auth} />
+ } />
+ <Route exact path="/post" render={(props) => <Post visitor={visitor} {...props} />} />
+ <Route exact path="/pm" render={(props) => <Contacts visitor={visitor} {...props} />} />
+ <Route exact path="/pm/:user" render={(props) => <Chat connection={es} visitor={visitor} {...props} />} />
+ <Route exact path="/:user/friends" render={(props) => <Friends user={props.match.params.user} {...props} />} />
+ <Route exact path="/:user/readers" render={(props) => <Readers user={props.match.params.user} {...props} />} />
+ <Route exact path="/:user" render={(props) => <Blog key={props.match.params.user} visitor={visitor} {...props} />} />
+ <Route exact path="/tag/:tag" render={(props) => <Tag visitor={visitor} {...props} />} />
+ <Route exact path="/:user/:mid" render={(props) => <Thread connection={es} visitor={visitor} {...props} />} />
+ </Switch>
+ </section>
+ {
+ visitor.uid > 0 &&
+ <aside id="sidebar">
+ <Link to="/?show=my">
+ <Icon name="ei-clock" size="s" />
+ <span className="desktop">My feed</span>
+ </Link>
+ <Link to="/pm">
+ <Icon name="ei-envelope" size="s" />
+ <span className="desktop">Messages</span>
+ </Link>
+ <Link to="/?show=discuss">
+ <Icon name="ei-bell" size="s" />
+ <span className="desktop">Discussions</span>
+ </Link>
+ <Link to="/settings" rel="nofollow">
+ <Icon name="ei-gear" size="s" />
+ <span className="desktop">Settings</span>
+ </Link>
+ </aside>
+ }
+ </>
+ </Router>
+ );
}
diff --git a/vnext/src/components/Chat.js b/vnext/src/components/Chat.js
index 57415d29..e2b8d2f1 100644
--- a/vnext/src/components/Chat.js
+++ b/vnext/src/components/Chat.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import ReactRouterPropTypes from 'react-router-prop-types';
import { UserType } from './Types';
import moment from 'moment';
@@ -11,74 +11,68 @@ import { getChat, pm } from '../api';
import './Chat.css';
-export default class Chat extends React.Component {
- constructor(props) {
- super(props);
-
- this.state = {
- chats: []
+export default function Chat(props) {
+ const [chats, setChats] = useState([]);
+ useEffect(() => {
+ console.log(props.connection);
+ if (props.connection) {
+ props.connection.addEventListener('msg', onMessage);
+ }
+ loadChat(props.match.params.user);
+ return () => {
+ if (props.connection) {
+ props.connection.removeEventListener('msg', onMessage);
+ }
};
- }
- componentDidMount() {
- this.loadChat(this.props.match.params.user);
- }
+ }, [props.connection]);
- loadChat = (uname) => {
- const { hash } = this.props.visitor;
- this.setState({
- chats: []
- });
+ let loadChat = (uname) => {
+ const { hash } = props.visitor;
+ setChats([]);
if (hash && uname) {
getChat(uname)
.then(response => {
- this.setState({
- chats: response.data
- });
+ setChats(response.data);
});
}
}
- onMessage = (msg) => {
- if (msg.user.uname === this.props.match.params.user) {
- this.setState({
- chats: [msg, ...this.state.chats]
- });
+ let onMessage = (json) => {
+ const msg = JSON.parse(json.data);
+ if (msg.user.uname === props.match.params.user) {
+ setChats([msg, ...chats]);
}
}
- onSend = (template) => {
+ let onSend = (template) => {
pm(template.to.uname, template.body)
.then(res => {
- this.loadChat(this.props.match.params.user);
+ loadChat(props.match.params.user);
}).catch(console.log);
}
-
- render() {
- const { chats } = this.state;
- const uname = this.props.match.params.user;
- return (
- <div className="msg-cont">
- <UserInfo user={uname} />
- {uname ? (
- <div className="chatroom">
- <ul className="Chat_messages">
- {
- chats.map((chat) =>
- <PM key={moment.utc(chat.timestamp).valueOf()} chat={chat} {...this.props} />
- )
- }
- </ul>
- <MessageInput data={{ mid: 0, timestamp: '0', to: { uname: uname } }} onSend={this.onSend}>
- Reply...
+ const uname = props.match.params.user;
+ return (
+ <div className="msg-cont">
+ <UserInfo user={uname} />
+ {uname ? (
+ <div className="chatroom">
+ <ul className="Chat_messages">
+ {
+ chats.map((chat) =>
+ <PM key={moment.utc(chat.timestamp).valueOf()} chat={chat} {...props} />
+ )
+ }
+ </ul>
+ <MessageInput data={{ mid: 0, timestamp: '0', to: { uname: uname } }} onSend={onSend}>
+ Reply...
</MessageInput>
- </div>
- ) : (
- <div className="chatroom no-selection"><p>No chat selected</p></div>
- )
- }
- </div>
- );
- }
+ </div>
+ ) : (
+ <div className="chatroom no-selection"><p>No chat selected</p></div>
+ )
+ }
+ </div>
+ );
}
Chat.propTypes = {
diff --git a/vnext/src/components/Thread.js b/vnext/src/components/Thread.js
index d61fd87f..c1dc915b 100644
--- a/vnext/src/components/Thread.js
+++ b/vnext/src/components/Thread.js
@@ -22,13 +22,17 @@ export default function Thread(props) {
const [loading, setLoading] = useState(false);
const [active, setActive] = useState(0);
useEffect(() => {
- props.connection.addEventListener('msg', onReply);
+ if (props.connection) {
+ props.connection.addEventListener('msg', onReply);
+ }
setActive(0);
loadReplies();
return () => {
- props.connection.removeEventListener('msg', onReply);
+ if (props.connection) {
+ props.connection.removeEventListener('msg', onReply);
+ }
}
- }, []);
+ }, [props.connection]);
let loadReplies = () => {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
@@ -54,7 +58,7 @@ export default function Thread(props) {
});
}
let onReply = (json) => {
- const msg = JSON.parse(json);
+ const msg = JSON.parse(json.data);
if (msg.mid == message.mid) {
setReplies([...this.state.replies, msg]);
}