aboutsummaryrefslogtreecommitdiff
path: root/vnext/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'vnext/src/ui')
-rw-r--r--vnext/src/ui/Avatar.js10
-rw-r--r--vnext/src/ui/Button.js6
-rw-r--r--vnext/src/ui/Chat.js70
-rw-r--r--vnext/src/ui/Comment.js34
-rw-r--r--vnext/src/ui/Contact.js8
-rw-r--r--vnext/src/ui/Contacts.js24
-rw-r--r--vnext/src/ui/Feeds.js118
-rw-r--r--vnext/src/ui/Header.js22
-rw-r--r--vnext/src/ui/Icon.js28
-rw-r--r--vnext/src/ui/Login.js48
-rw-r--r--vnext/src/ui/Message.js66
-rw-r--r--vnext/src/ui/MessageInput.js90
-rw-r--r--vnext/src/ui/PM.js18
-rw-r--r--vnext/src/ui/Post.js52
-rw-r--r--vnext/src/ui/SearchBox.js12
-rw-r--r--vnext/src/ui/Settings.js100
-rw-r--r--vnext/src/ui/Spinner.js10
-rw-r--r--vnext/src/ui/Thread.js130
-rw-r--r--vnext/src/ui/UploadButton.js20
-rw-r--r--vnext/src/ui/UserInfo.js70
-rw-r--r--vnext/src/ui/Users.js22
-rw-r--r--vnext/src/ui/VisitorContext.js16
-rw-r--r--vnext/src/ui/__tests__/Avatar.test.js14
-rw-r--r--vnext/src/ui/__tests__/MessageInput.test.js48
-rw-r--r--vnext/src/ui/__tests__/UserLink.test.js18
-rw-r--r--vnext/src/ui/helpers/BubbleStyle.js8
26 files changed, 531 insertions, 531 deletions
diff --git a/vnext/src/ui/Avatar.js b/vnext/src/ui/Avatar.js
index 9d93521f..1a8db0c3 100644
--- a/vnext/src/ui/Avatar.js
+++ b/vnext/src/ui/Avatar.js
@@ -1,7 +1,7 @@
-import { memo } from 'react';
-import { Link } from 'react-router-dom';
+import { memo } from 'react'
+import { Link } from 'react-router-dom'
-import Icon from './Icon';
+import Icon from './Icon'
/**
* @typedef {object} AvatarProps
@@ -38,7 +38,7 @@ function Avatar({ user, style, link, children }) {
{children}
</div>
</div>
- );
+ )
}
-export default memo(Avatar);
+export default memo(Avatar)
diff --git a/vnext/src/ui/Button.js b/vnext/src/ui/Button.js
index 2c315e46..dd425021 100644
--- a/vnext/src/ui/Button.js
+++ b/vnext/src/ui/Button.js
@@ -1,4 +1,4 @@
-import { memo } from 'react';
+import { memo } from 'react'
/**
* @param {import('react').ClassAttributes<HTMLButtonElement> & import('react').ButtonHTMLAttributes<HTMLButtonElement>} props
@@ -6,7 +6,7 @@ import { memo } from 'react';
function Button(props) {
return (
<button className="Button" {...props} />
- );
+ )
}
-export default memo(Button);
+export default memo(Button)
diff --git a/vnext/src/ui/Chat.js b/vnext/src/ui/Chat.js
index 4fdeaac7..55b89d09 100644
--- a/vnext/src/ui/Chat.js
+++ b/vnext/src/ui/Chat.js
@@ -1,17 +1,17 @@
-import { useEffect, useState, useCallback } from 'react';
-import { useParams } from 'react-router-dom';
-import dayjs from 'dayjs';
-import utc from 'dayjs/plugin/utc';
-dayjs.extend(utc);
+import { useEffect, useState, useCallback } from 'react'
+import { useParams } from 'react-router-dom'
+import dayjs from 'dayjs'
+import utc from 'dayjs/plugin/utc'
+dayjs.extend(utc)
-import PM from './PM';
-import MessageInput from './MessageInput';
-import UserInfo from './UserInfo';
+import PM from './PM'
+import MessageInput from './MessageInput'
+import UserInfo from './UserInfo'
-import { getChat, pm } from '../api';
+import { getChat, pm } from '../api'
-import { useVisitor } from './VisitorContext';
-import { Helmet } from 'react-helmet';
+import { useVisitor } from './VisitorContext'
+import { Helmet } from 'react-helmet'
/**
*
@@ -24,48 +24,48 @@ import { Helmet } from 'react-helmet';
* @param {ChatProps} props
*/
export default function Chat(props) {
- const [visitor] = useVisitor();
- const [messages, setMessages] = useState([]);
- const params = useParams();
+ const [visitor] = useVisitor()
+ const [messages, setMessages] = useState([])
+ const params = useParams()
let loadChat = useCallback((uname) => {
- const { hash } = visitor;
+ const { hash } = visitor
if (hash && uname) {
getChat(uname)
.then(response => {
- setMessages(response.data);
- }).catch(console.log);
+ setMessages(response.data)
+ }).catch(console.log)
}
- }, [visitor]);
+ }, [visitor])
let onMessage = useCallback((json) => {
- const msg = JSON.parse(json.data);
+ const msg = JSON.parse(json.data)
if (msg.user.uname === params.user) {
setMessages((oldChat) => {
- return [msg, ...oldChat];
- });
+ return [msg, ...oldChat]
+ })
}
- }, [params.user]);
+ }, [params.user])
let onSend = async ({ body }) => {
- let result = false;
- let res = await pm(params.user, body).catch(console.error);
- result = res.status == 200;
- return result;
- };
+ let result = false
+ let res = await pm(params.user, body).catch(console.error)
+ result = res.status == 200
+ return result
+ }
useEffect(() => {
if (props.connection.addEventListener) {
- props.connection.addEventListener('msg', onMessage);
+ props.connection.addEventListener('msg', onMessage)
}
- loadChat(params.user);
- console.log(props.connection);
+ loadChat(params.user)
+ console.log(props.connection)
return () => {
if (props.connection.removeEventListener) {
- props.connection.removeEventListener('msg', onMessage);
+ props.connection.removeEventListener('msg', onMessage)
}
- };
- }, [props.connection, onMessage, loadChat, params.user]);
- const uname = params.user;
+ }
+ }, [props.connection, onMessage, loadChat, params.user])
+ const uname = params.user
return (
<div className="msg-cont">
<Helmet>
@@ -90,5 +90,5 @@ export default function Chat(props) {
)
}
</div>
- );
+ )
}
diff --git a/vnext/src/ui/Comment.js b/vnext/src/ui/Comment.js
index e8fb2afb..6ba34ff0 100644
--- a/vnext/src/ui/Comment.js
+++ b/vnext/src/ui/Comment.js
@@ -1,14 +1,14 @@
-import { useEffect, useRef, useState } from 'react';
+import { useEffect, useRef, useState } from 'react'
-import MessageInput from './MessageInput';
-import Avatar from './Avatar';
-import { UserLink } from './UserInfo';
-import Button from './Button';
+import MessageInput from './MessageInput'
+import Avatar from './Avatar'
+import { UserLink } from './UserInfo'
+import Button from './Button'
-import { format, embedUrls } from '../utils/embed';
+import { format, embedUrls } from '../utils/embed'
-import { chatItemStyle } from './helpers/BubbleStyle';
-import { useVisitor } from './VisitorContext';
+import { chatItemStyle } from './helpers/BubbleStyle'
+import { useVisitor } from './VisitorContext'
/**
* @param {{
@@ -23,20 +23,20 @@ import { useVisitor } from './VisitorContext';
* @returns import('react').ReactElement
*/
export default function Comment({ msg, draft, active, setActive, onStartEditing, postComment }) {
- const [visitor] = useVisitor();
+ const [visitor] = useVisitor()
/** @type {import('react').MutableRefObject<HTMLDivElement?>} */
- const embedRef = useRef(null);
+ const embedRef = useRef(null)
/** @type {import('react').MutableRefObject<HTMLDivElement?>} */
- const msgRef = useRef(null);
- const [author] = useState(msg.user);
+ const msgRef = useRef(null)
+ const [author] = useState(msg.user)
useEffect(() => {
if (msgRef.current && embedRef.current) {
- embedUrls(msgRef.current.querySelectorAll('a'), embedRef.current);
+ embedUrls(msgRef.current.querySelectorAll('a'), embedRef.current)
if (!embedRef.current.hasChildNodes()) {
- embedRef.current.style.display = 'none';
+ embedRef.current.style.display = 'none'
}
}
- }, []);
+ }, [])
return (
<div style={chatItemStyle(visitor, msg)}>
<div className="msg-header">
@@ -94,11 +94,11 @@ export default function Comment({ msg, draft, active, setActive, onStartEditing,
</div>
}
</div>
- );
+ )
}
/**
* @type {import('react').CSSProperties}
*/
const linkStyle = {
cursor: 'pointer'
-};
+}
diff --git a/vnext/src/ui/Contact.js b/vnext/src/ui/Contact.js
index 75c80332..d7bb12c8 100644
--- a/vnext/src/ui/Contact.js
+++ b/vnext/src/ui/Contact.js
@@ -1,6 +1,6 @@
-import { memo } from 'react';
+import { memo } from 'react'
-import Avatar from './Avatar';
+import Avatar from './Avatar'
/**
* @typedef {object} ContactProps
@@ -18,7 +18,7 @@ function Contact({ user, style }) {
{user.unreadCount && <span className="badge">{user.unreadCount}</span>}
<div className="msg-ts">{user.lastMessageText}</div>
</Avatar>
- );
+ )
}
-export default memo(Contact);
+export default memo(Contact)
diff --git a/vnext/src/ui/Contacts.js b/vnext/src/ui/Contacts.js
index b1f87723..1c23f042 100644
--- a/vnext/src/ui/Contacts.js
+++ b/vnext/src/ui/Contacts.js
@@ -1,23 +1,23 @@
-import { useEffect, useState } from 'react';
-import { Helmet } from 'react-helmet';
+import { useEffect, useState } from 'react'
+import { Helmet } from 'react-helmet'
-import { getChats } from '../api';
+import { getChats } from '../api'
-import Contact from './Contact.js';
-import { ChatSpinner } from './Spinner';
+import Contact from './Contact.js'
+import { ChatSpinner } from './Spinner'
/**
*
*/
export default function Contacts() {
- const [pms, setPms] = useState([]);
+ const [pms, setPms] = useState([])
useEffect(() => {
getChats()
.then(response => {
- setPms(response.data.pms);
- }).catch(console.log);
- }, []);
+ setPms(response.data.pms)
+ }).catch(console.log)
+ }, [])
return (
<div className="msg-cont">
<Helmet>
@@ -31,7 +31,7 @@ export default function Contacts() {
}
</div>
</div>
- );
+ )
}
const chatListStyle = {
@@ -39,7 +39,7 @@ const chatListStyle = {
flexDirection: 'column',
width: '100%',
padding: '12px'
-};
+}
const chatTitleStyle = {
width: '100%',
@@ -48,4 +48,4 @@ const chatTitleStyle = {
background: 'var(--main-background-color)',
color: 'var(--text-color)',
borderBottom: '1px solid var(--border-color)'
-};
+}
diff --git a/vnext/src/ui/Feeds.js b/vnext/src/ui/Feeds.js
index 48da52e3..086a910e 100644
--- a/vnext/src/ui/Feeds.js
+++ b/vnext/src/ui/Feeds.js
@@ -1,18 +1,18 @@
-import { useState, useEffect } from 'react';
-import { Link, useLocation, useParams, Navigate, useSearchParams } from 'react-router-dom';
+import { useState, useEffect } from 'react'
+import { Link, useLocation, useParams, Navigate, useSearchParams } from 'react-router-dom'
-import dayjs from 'dayjs';
-import utc from 'dayjs/plugin/utc';
-dayjs.extend(utc);
+import dayjs from 'dayjs'
+import utc from 'dayjs/plugin/utc'
+dayjs.extend(utc)
-import Message from './Message';
-import Spinner from './Spinner';
+import Message from './Message'
+import Spinner from './Spinner'
-import UserInfo from './UserInfo';
+import UserInfo from './UserInfo'
-import { getMessages } from '../api';
-import { useVisitor } from './VisitorContext';
-import { Helmet } from 'react-helmet';
+import { getMessages } from '../api'
+import { useVisitor } from './VisitorContext'
+import { Helmet } from 'react-helmet'
/**
* @typedef {object} Query
@@ -28,29 +28,29 @@ import { Helmet } from 'react-helmet';
*/
function RequireAuth({ children }) {
- let location = useLocation();
- let [visitor] = useVisitor();
+ let location = useLocation()
+ let [visitor] = useVisitor()
if (!visitor.hash) {
// Redirect them to the /login page, but save the current location they were
// trying to go to when they were redirected. This allows us to send them
// along to that page after they login, which is a nicer user experience
// than dropping them off on the home page.
- return <Navigate to="/login" state={{ from: location }} />;
+ return <Navigate to="/login" state={{ from: location }} />
}
- return children;
+ return children
}
/**
*
*/
export function Discover() {
- const [search] = useSearchParams();
+ const [search] = useSearchParams()
const query = {
baseUrl: '/api/messages',
search: search,
pageParam: search.search ? 'page' : 'before_mid'
- };
+ }
return (
<>
<Helmet>
@@ -58,7 +58,7 @@ export function Discover() {
</Helmet>
<Feed query={query} />
</>
- );
+ )
}
/**
@@ -68,7 +68,7 @@ export function Discussions() {
const query = {
baseUrl: '/api/messages/discussions',
pageParam: 'to'
- };
+ }
return (
<>
<Helmet>
@@ -76,26 +76,26 @@ export function Discussions() {
</Helmet>
<Feed query={query} />
</>
- );
+ )
}
/**
*
*/
export function Blog() {
- const { user } = useParams();
- const [params] = useSearchParams();
+ const { user } = useParams()
+ const [params] = useSearchParams()
const search = {
...params,
uname: user
- };
+ }
const query = {
baseUrl: '/api/messages',
search: search,
pageParam: search.search ? 'page' : 'before_mid'
- };
- const blogTitle = `${user} blog`;
- const pageTitle = search.tag ? `${blogTitle}: #${search.tag}` : blogTitle;
+ }
+ const blogTitle = `${user} blog`
+ const pageTitle = search.tag ? `${blogTitle}: #${search.tag}` : blogTitle
return (
<>
<Helmet>
@@ -106,22 +106,22 @@ export function Blog() {
</div>
<Feed query={query} />
</>
- );
+ )
}
/**
*
*/
export function Tag() {
- const params = useParams();
- const { tag } = params;
+ const params = useParams()
+ const { tag } = params
const query = {
baseUrl: '/api/messages',
search: {
tag: tag
},
pageParam: 'before_mid'
- };
+ }
return (
<>
<Helmet>
@@ -129,7 +129,7 @@ export function Tag() {
</Helmet>
<Feed query={query} />
</>
- );
+ )
}
/**
@@ -139,12 +139,12 @@ export function Home() {
const query = {
baseUrl: '/api/home',
pageParam: 'before_mid'
- };
+ }
return (
<RequireAuth>
<Feed query={query} />
</RequireAuth>
- );
+ )
}
/**
@@ -157,52 +157,52 @@ export function Home() {
* @param {FeedState} props
*/
function Feed({ query }) {
- const location = useLocation();
- const [visitor] = useVisitor();
+ const location = useLocation()
+ const [visitor] = useVisitor()
const [state, setState] = useState({
hash: visitor.hash,
msgs: [],
nextpage: null,
error: false,
tag: ''
- });
- const [loading, setLoading] = useState(true);
- const [filter] = useSearchParams();
+ })
+ const [loading, setLoading] = useState(true)
+ const [filter] = useSearchParams()
useEffect(() => {
- setLoading(true);
+ setLoading(true)
let getPageParam = (pageParam, lastMessage, /** @type { URLSearchParams } */ filterParams) => {
- const pageValue = pageParam === 'before_mid' ? lastMessage.mid : pageParam === 'page' ? (Number(filterParams.page) || 0) + 1 : dayjs.utc(lastMessage.updated).valueOf();
- filterParams.append(pageParam, pageValue);
- return `?${filterParams.toString()}`;
- };
- let params = { ...Object.fromEntries(filter), ...query.search };
- let url = query.baseUrl;
+ const pageValue = pageParam === 'before_mid' ? lastMessage.mid : pageParam === 'page' ? (Number(filterParams.page) || 0) + 1 : dayjs.utc(lastMessage.updated).valueOf()
+ filterParams.append(pageParam, pageValue)
+ return `?${filterParams.toString()}`
+ }
+ let params = { ...Object.fromEntries(filter), ...query.search }
+ let url = query.baseUrl
getMessages(url, params)
.then(response => {
- const { data } = response;
- const { pageParam } = query;
- const lastMessage = data.slice(-1)[0] || {};
- const nextpage = getPageParam(pageParam, lastMessage, new URLSearchParams(params));
- document.body.scrollTop = 0;
- document.documentElement.scrollTop = 0;
+ const { data } = response
+ const { pageParam } = query
+ const lastMessage = data.slice(-1)[0] || {}
+ const nextpage = getPageParam(pageParam, lastMessage, new URLSearchParams(params))
+ document.body.scrollTop = 0
+ document.documentElement.scrollTop = 0
setState((prevState) => {
return {
...prevState,
msgs: data,
nextpage: nextpage,
tag: filter['tag'] || ''
- };
- });
- setLoading(false);
+ }
+ })
+ setLoading(false)
}).catch(() => {
setState((prevState) => {
return {
...prevState,
error: true
- };
- });
- });
- }, [query, filter]);
+ }
+ })
+ })
+ }, [query, filter])
return (state.msgs.length > 0 ? (
<div className="msgs">
{
@@ -227,5 +227,5 @@ function Feed({ query }) {
}
</div>
) : state.error ? <div>error</div> : loading ? <div className="msgs"><Spinner /><Spinner /><Spinner /><Spinner /></div> : <div>No more messages</div>
- );
+ )
}
diff --git a/vnext/src/ui/Header.js b/vnext/src/ui/Header.js
index 6cf07844..cee24c67 100644
--- a/vnext/src/ui/Header.js
+++ b/vnext/src/ui/Header.js
@@ -1,19 +1,19 @@
-import { memo, useCallback } from 'react';
-import { Link, useNavigate } from 'react-router-dom';
+import { memo, useCallback } from 'react'
+import { Link, useNavigate } from 'react-router-dom'
-import SearchBox from './SearchBox';
+import SearchBox from './SearchBox'
function Header() {
- const navigate = useNavigate();
+ const navigate = useNavigate()
/**
* @param {string} searchString
*/
let searchAll = useCallback((searchString) => {
- let location = {};
- location.pathname = '/discover';
- location.search = `?search=${searchString}`;
- navigate(location);
- }, [navigate]);
+ let location = {}
+ location.pathname = '/discover'
+ location.search = `?search=${searchString}`
+ navigate(location)
+ }, [navigate])
return (
<div id="header">
<div id="header_wrapper">
@@ -25,7 +25,7 @@ function Header() {
</div>
</div>
</div>
- );
+ )
}
-export default memo(Header);
+export default memo(Header)
diff --git a/vnext/src/ui/Icon.js b/vnext/src/ui/Icon.js
index 6d10df16..bc5ce08a 100644
--- a/vnext/src/ui/Icon.js
+++ b/vnext/src/ui/Icon.js
@@ -1,7 +1,7 @@
-import { createElement, memo } from 'react';
-import PropTypes from 'prop-types';
+import { createElement, memo } from 'react'
+import PropTypes from 'prop-types'
-import evilIcons from 'evil-icons/assets/sprite.svg';
+import evilIcons from 'evil-icons/assets/sprite.svg'
/**
* @typedef {object} IconProps
@@ -16,18 +16,18 @@ import evilIcons from 'evil-icons/assets/sprite.svg';
* @param {IconProps} props - icon props
*/
function IconElement(props) {
- var size = props.size ? ' icon--' + props.size : '';
- var className = props.className ? ' ' + props.className : '';
- var klass = 'icon' + (!props.noFill ? ' icon--' + props.name : '') + size + className;
+ var size = props.size ? ' icon--' + props.size : ''
+ var className = props.className ? ' ' + props.className : ''
+ var klass = 'icon' + (!props.noFill ? ' icon--' + props.name : '') + size + className
- var name = '#' + props.name + '-icon';
- var useTag = `<use xlink:href='${evilIcons}${name}' />`;
- var Icon = createElement('svg', { className: 'icon__cnt', dangerouslySetInnerHTML: { __html: useTag } });
+ var name = '#' + props.name + '-icon'
+ var useTag = `<use xlink:href='${evilIcons}${name}' />`
+ var Icon = createElement('svg', { className: 'icon__cnt', dangerouslySetInnerHTML: { __html: useTag } })
return createElement(
'div',
{ className: klass },
wrapSpinner(Icon, klass)
- );
+ )
}
/**
@@ -40,17 +40,17 @@ function wrapSpinner(Html, klass) {
'div',
{ className: 'icon__spinner' },
Html
- );
+ )
} else {
- return Html;
+ return Html
}
}
-export default memo(IconElement);
+export default memo(IconElement)
IconElement.propTypes = {
size: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
className: PropTypes.string,
noFill: PropTypes.bool
-};
+}
diff --git a/vnext/src/ui/Login.js b/vnext/src/ui/Login.js
index 73da49c8..5d9908cb 100644
--- a/vnext/src/ui/Login.js
+++ b/vnext/src/ui/Login.js
@@ -1,13 +1,13 @@
-import { useEffect } from 'react';
-import { useLocation, useNavigate } from 'react-router-dom';
+import { useEffect } from 'react'
+import { useLocation, useNavigate } from 'react-router-dom'
-import Icon from './Icon';
-import Button from './Button';
-import { useForm } from 'react-hook-form';
+import Icon from './Icon'
+import Button from './Button'
+import { useForm } from 'react-hook-form'
-import { me, facebookLink, vkLink, appleLink } from '../api';
+import { me, facebookLink, vkLink, appleLink } from '../api'
-import { useVisitor } from './VisitorContext';
+import { useVisitor } from './VisitorContext'
/**
* @typedef {object} LoginProps
@@ -19,29 +19,29 @@ import { useVisitor } from './VisitorContext';
* @param {LoginProps} props
*/
function Login({ onAuth }) {
- const location = useLocation();
- const navigate = useNavigate();
- const [visitor] = useVisitor();
+ const location = useLocation()
+ const navigate = useNavigate()
+ const [visitor] = useVisitor()
useEffect(() => {
if (visitor.hash) {
- const {retpath } = location.state || '/';
- console.log(retpath);
- navigate(retpath);
+ const {retpath } = location.state || '/'
+ console.log(retpath)
+ navigate(retpath)
}
- }, [navigate, location.state, visitor]);
+ }, [navigate, location.state, visitor])
- const { register, handleSubmit } = useForm();
+ const { register, handleSubmit } = useForm()
/** @type { import('react-hook-form').SubmitHandler<import('react-hook-form').FieldValues> } */
let onSubmit = (values) => {
me(values.username, values.password)
.then(response => {
- onAuth(response);
+ onAuth(response)
}
).catch(ex => {
- console.log(ex);
- });
- };
+ console.log(ex)
+ })
+ }
return (
<div className="msg-cont">
<div className="dialoglogin">
@@ -64,25 +64,25 @@ function Login({ onAuth }) {
</form>
</div>
</div>
- );
+ )
}
-export default Login;
+export default Login
const socialButtonsStyle = {
display: 'flex',
justifyContent: 'space-evenly',
padding: '4px'
-};
+}
const facebookButtonStyle = {
color: '#fff',
padding: '2px 14px',
background: '#3b5998'
-};
+}
const vkButtonStyle = {
color: '#fff',
padding: '2px 14px',
background: '#4c75a3'
-};
+}
diff --git a/vnext/src/ui/Message.js b/vnext/src/ui/Message.js
index c5ad175d..e4135700 100644
--- a/vnext/src/ui/Message.js
+++ b/vnext/src/ui/Message.js
@@ -1,18 +1,18 @@
-import React, { Fragment, memo, useEffect, useRef } from 'react';
+import React, { Fragment, memo, useEffect, useRef } from 'react'
-import dayjs from 'dayjs';
-import utc from 'dayjs/plugin/utc';
-dayjs.extend(utc);
-import relativeTime from 'dayjs/plugin/relativeTime';
-dayjs.extend(relativeTime);
+import dayjs from 'dayjs'
+import utc from 'dayjs/plugin/utc'
+dayjs.extend(utc)
+import relativeTime from 'dayjs/plugin/relativeTime'
+dayjs.extend(relativeTime)
-import { Link } from 'react-router-dom';
-import Icon from './Icon';
-import Avatar from './Avatar';
-import { UserLink } from './UserInfo';
+import { Link } from 'react-router-dom'
+import Icon from './Icon'
+import Avatar from './Avatar'
+import { UserLink } from './UserInfo'
-import { format, embedUrls } from '../utils/embed';
-import { useVisitor } from './VisitorContext';
+import { format, embedUrls } from '../utils/embed'
+import { useVisitor } from './VisitorContext'
/**
* @callback ToggleSubscriptionCallback
@@ -31,30 +31,30 @@ import { useVisitor } from './VisitorContext';
* @param {React.PropsWithChildren<{}> & MessageProps} props props
*/
export default function Message({ data, isThread, onToggleSubscription, children }) {
- const [visitor] = useVisitor();
- const isCode = (data.tags || []).indexOf('code') >= 0;
- const likesSummary = data.likes ? `${data.likes}` : 'Recommend';
- const commentsSummary = data.replies ? `${data.replies}` : 'Comment';
+ const [visitor] = useVisitor()
+ const isCode = (data.tags || []).indexOf('code') >= 0
+ const likesSummary = data.likes ? `${data.likes}` : 'Recommend'
+ const commentsSummary = data.replies ? `${data.replies}` : 'Comment'
/**
* @type {React.MutableRefObject<HTMLDivElement?>}
*/
- const embedRef = useRef(null);
+ const embedRef = useRef(null)
/**
* @type {React.MutableRefObject<HTMLDivElement?>}
*/
- const msgRef = useRef(null);
+ const msgRef = useRef(null)
useEffect(() => {
- const msg = msgRef.current;
- const embed = embedRef.current;
+ const msg = msgRef.current
+ const embed = embedRef.current
if (msg && embed) {
- embedUrls(msg.querySelectorAll('a'), embed);
+ embedUrls(msg.querySelectorAll('a'), embed)
if (!embed.hasChildNodes()) {
- embed.style.display = 'none';
+ embed.style.display = 'none'
}
}
- }, []);
+ }, [])
const canComment = data.user && visitor.uid === data.user.uid || !data.ReadOnly && visitor.uid > 0
- || !data.ReadOnly && !isThread;
+ || !data.ReadOnly && !isThread
return (
<div className="msg-cont">
<Recommendations forMessage={data} />
@@ -120,7 +120,7 @@ export default function Message({ data, isThread, onToggleSubscription, children
{
data.user && canComment && ((
isThread ? (
- <a className="msg-button" onClick={() => { onToggleSubscription(data); }}>
+ <a className="msg-button" onClick={() => { onToggleSubscription(data) }}>
{
data.subscribed ? (<>
<Icon name="ei-check" size="s" />
@@ -143,14 +143,14 @@ export default function Message({ data, isThread, onToggleSubscription, children
}
{children}
</div >
- );
+ )
}
/**
* @param {{isCode: boolean, data: {__html: string}}} props props
*/
function MessageContainer({ isCode, data }) {
- return isCode ? (<pre dangerouslySetInnerHTML={data} />) : (<span dangerouslySetInnerHTML={data} />);
+ return isCode ? (<pre dangerouslySetInnerHTML={data} />) : (<span dangerouslySetInnerHTML={data} />)
}
/**
@@ -171,18 +171,18 @@ function Tags({ data, user }) {
))
}
</span>
- ) : null;
+ ) : null
}
-const TagsList = memo(Tags);
+const TagsList = memo(Tags)
/**
*
* @param {{forMessage: import('../client').Message}} props props
*/
function Recommends({ forMessage }) {
- const { recommendations } = forMessage;
- const likes = forMessage.likes || 0;
+ const { recommendations } = forMessage
+ const likes = forMessage.likes || 0
return recommendations && recommendations.length > 0 && (
<div className="msg-recomms">{'♡ by '}
{
@@ -197,7 +197,7 @@ function Recommends({ forMessage }) {
likes > recommendations.length && (<span>&nbsp;and {likes - recommendations.length} others</span>)
}
</div>
- ) || null;
+ ) || null
}
-const Recommendations = memo(Recommends);
+const Recommendations = memo(Recommends)
diff --git a/vnext/src/ui/MessageInput.js b/vnext/src/ui/MessageInput.js
index 3d24e728..6ebf4361 100644
--- a/vnext/src/ui/MessageInput.js
+++ b/vnext/src/ui/MessageInput.js
@@ -1,10 +1,10 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect, useRef } from 'react'
-import Icon from './Icon';
-import Button from './Button';
+import Icon from './Icon'
+import Button from './Button'
-import UploadButton from './UploadButton';
-import toast from 'react-hot-toast';
+import UploadButton from './UploadButton'
+import toast from 'react-hot-toast'
/**
@@ -13,13 +13,13 @@ import toast from 'react-hot-toast';
*/
function moveCaretToEnd(el) {
if (typeof el.selectionStart == 'number') {
- el.selectionStart = el.selectionEnd = el.value.length;
+ el.selectionStart = el.selectionEnd = el.value.length
} else if (typeof el.createTextRange != 'undefined') {
// Internet Explorer
- el.focus();
- var range = el.createTextRange();
- range.collapse(false);
- range.select();
+ el.focus()
+ var range = el.createTextRange()
+ range.collapse(false)
+ range.select()
}
}
@@ -42,74 +42,74 @@ export default function MessageInput({ text, rows, placeholder, onSend }) {
/**
* @type {React.MutableRefObject<HTMLTextAreaElement?>}
*/
- let textareaRef = useRef(null);
+ let textareaRef = useRef(null)
/**
* @type {React.MutableRefObject<HTMLInputElement?>}
*/
- let fileinput = useRef(null);
+ let fileinput = useRef(null)
let updateFocus = () => {
- const isDesktop = window.matchMedia('(min-width: 62.5rem)');
+ const isDesktop = window.matchMedia('(min-width: 62.5rem)')
if (isDesktop.matches) {
- const textarea = textareaRef.current;
+ const textarea = textareaRef.current
if (textarea) {
- textarea.focus();
- moveCaretToEnd(textarea);
+ textarea.focus()
+ moveCaretToEnd(textarea)
}
}
- };
+ }
useEffect(() => {
textChanged({
target: {
value: text
}
- });
- updateFocus();
- }, [text]);
+ })
+ updateFocus()
+ }, [text])
- let [body, setBody] = useState(text);
+ let [body, setBody] = useState(text)
let handleCtrlEnter = (event) => {
if (event.ctrlKey && (event.charCode == 10 || event.charCode == 13)) {
- onSubmit({});
+ onSubmit({})
}
- };
+ }
const textChanged = (event) => {
- setBody(event.target.value);
- const el = textareaRef.current;
+ setBody(event.target.value)
+ const el = textareaRef.current
if (el) {
- const offset = el.offsetHeight - el.clientHeight;
- const height = el.scrollHeight + offset;
- el.style.height = `${height + offset}px`;
+ const offset = el.offsetHeight - el.clientHeight
+ const height = el.scrollHeight + offset
+ el.style.height = `${height + offset}px`
}
- };
- const [attach, setAttach] = useState('');
+ }
+ const [attach, setAttach] = useState('')
let uploadValueChanged = (attach) => {
- setAttach(attach);
- };
+ setAttach(attach)
+ }
let onSubmit = (event) => {
if (event.preventDefault) {
- event.preventDefault();
+ event.preventDefault()
}
- const input = fileinput.current;
+ const input = fileinput.current
if (input && input.files) {
onSend({
body: body,
attach: attach ? input.files[0] : ''
}).then((success) => {
if (success) {
- setAttach('');
- setBody('');
+ setAttach('')
+ setBody('')
if (textareaRef.current) {
- textareaRef.current.style.height = '';
+ textareaRef.current.style.height = ''
}
- updateFocus();
+ updateFocus()
} else {
- toast('Can not update this message');
+ toast('Can not update this message')
}
- }).catch(console.log);
+ }).catch(console.log)
}
- };
+ }
return (
<form className="msg-comment-target" style={{ padding: '12px', width: '100%' }} onSubmit={onSubmit}>
<div style={commentStyle}>
@@ -122,7 +122,7 @@ export default function MessageInput({ text, rows, placeholder, onSend }) {
</div>
</div>
</form>
- );
+ )
}
/**
@@ -133,7 +133,7 @@ const commentStyle = {
flexDirection: 'column',
width: '100%',
marginTop: '10px'
-};
+}
/**
* @type {React.CSSProperties}
@@ -143,7 +143,7 @@ const inputBarStyle = {
alignItems: 'center',
justifyContent: 'space-between',
padding: '3px'
-};
+}
/**
* @type {React.CSSProperties}
@@ -156,4 +156,4 @@ const textInputStyle = {
border: 0,
outline: 'none',
padding: '4px'
-};
+}
diff --git a/vnext/src/ui/PM.js b/vnext/src/ui/PM.js
index d5a7eff1..3aa877b1 100644
--- a/vnext/src/ui/PM.js
+++ b/vnext/src/ui/PM.js
@@ -1,13 +1,13 @@
-import { memo } from 'react';
+import { memo } from 'react'
-import Avatar from './Avatar';
-import { format } from '../utils/embed';
-import { chatItemStyle } from './helpers/BubbleStyle';
-import { useVisitor } from './VisitorContext';
+import Avatar from './Avatar'
+import { format } from '../utils/embed'
+import { chatItemStyle } from './helpers/BubbleStyle'
+import { useVisitor } from './VisitorContext'
function PM(props) {
- const { chat } = props;
- const [visitor] = useVisitor();
+ const { chat } = props
+ const [visitor] = useVisitor()
return (
<li>
<div style={chatItemStyle(visitor, chat)}>
@@ -17,10 +17,10 @@ function PM(props) {
</div>
</div>
</li>
- );
+ )
}
-export default memo(PM);
+export default memo(PM)
/*
PM.propTypes = {
chat: MessageType.isRequired
diff --git a/vnext/src/ui/Post.js b/vnext/src/ui/Post.js
index 45eceb35..6f7d1e93 100644
--- a/vnext/src/ui/Post.js
+++ b/vnext/src/ui/Post.js
@@ -1,42 +1,42 @@
-import { useState } from 'react';
-import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
+import { useState } from 'react'
+import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
-import Button from './Button';
-import MessageInput from './MessageInput';
+import Button from './Button'
+import MessageInput from './MessageInput'
-import { post, update } from '../api';
-import { useVisitor } from './VisitorContext';
-import { Helmet } from 'react-helmet';
+import { post, update } from '../api'
+import { useVisitor } from './VisitorContext'
+import { Helmet } from 'react-helmet'
/**
*
*/
export default function Post() {
- const location = useLocation();
- const navigate = useNavigate();
- const [visitor] = useVisitor();
- let draftMessage = (location.state || {}).data || {};
- let [draft, setDraft] = useState(draftMessage.body);
- let [params] = useSearchParams();
+ const location = useLocation()
+ const navigate = useNavigate()
+ const [visitor] = useVisitor()
+ let draftMessage = (location.state || {}).data || {}
+ let [draft, setDraft] = useState(draftMessage.body)
+ let [params] = useSearchParams()
let postMessage = async ({ attach, body }) => {
try {
- const res = draftMessage.mid ? await update(draftMessage.mid, 0, body) : await post(body, attach);
- let result = res.status == 200;
+ const res = draftMessage.mid ? await update(draftMessage.mid, 0, body) : await post(body, attach)
+ let result = res.status == 200
if (result) {
- const msg = res.data.newMessage;
- navigate(`/${visitor.uname}/${msg.mid}`);
+ const msg = res.data.newMessage
+ navigate(`/${visitor.uname}/${msg.mid}`)
}
- return result;
+ return result
} catch (e) {
- console.log(e);
+ console.log(e)
}
- return false;
- };
+ return false
+ }
let appendTag = (tag) => {
setDraft(prevDraft => {
- return `${prevDraft || ''} *${tag} `;
- });
- };
+ return `${prevDraft || ''} *${tag} `
+ })
+ }
return (
<div className="msg-cont">
<Helmet>
@@ -51,12 +51,12 @@ export default function Post() {
<p>Tags:</p>
{
visitor.tagStats.map(t => {
- return (<Button key={t.tag} onClick={() => { appendTag(t.tag); }}>{t.tag}</Button>);
+ return (<Button key={t.tag} onClick={() => { appendTag(t.tag) }}>{t.tag}</Button>)
})
}
</div>
}
</div>
- );
+ )
}
diff --git a/vnext/src/ui/SearchBox.js b/vnext/src/ui/SearchBox.js
index 636967b1..e63a19ee 100644
--- a/vnext/src/ui/SearchBox.js
+++ b/vnext/src/ui/SearchBox.js
@@ -1,4 +1,4 @@
-import { useForm } from 'react-hook-form';
+import { useForm } from 'react-hook-form'
/**
* @typedef {object} SearchBoxPropsFields
@@ -13,18 +13,18 @@ import { useForm } from 'react-hook-form';
* @param {SearchBoxProps} props
*/
function SearchBox({ onSearch }) {
- const { register, handleSubmit } = useForm();
+ const { register, handleSubmit } = useForm()
/** @type { import('react-hook-form').SubmitHandler<import('react-hook-form').FieldValues> } */
let onSubmit = ( values ) => {
- onSearch(values.search);
- };
+ onSearch(values.search)
+ }
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input className="text" type="text"
placeholder="Search..." {...register('search')} />
<input data-testid="submit" type="submit" hidden />
</form>
- );
+ )
}
-export default SearchBox;
+export default SearchBox
diff --git a/vnext/src/ui/Settings.js b/vnext/src/ui/Settings.js
index 380d8ff6..1205cd49 100644
--- a/vnext/src/ui/Settings.js
+++ b/vnext/src/ui/Settings.js
@@ -1,46 +1,46 @@
-import { Fragment, useState, useRef } from 'react';
+import { Fragment, useState, useRef } from 'react'
-import { me, updateAvatar } from '../api';
+import { me, updateAvatar } from '../api'
-import Button from './Button';
-import Icon from './Icon';
-import UploadButton from './UploadButton';
-import Avatar from './Avatar';
-import { useVisitor } from './VisitorContext';
-import { Helmet } from 'react-helmet';
+import Button from './Button'
+import Icon from './Icon'
+import UploadButton from './UploadButton'
+import Avatar from './Avatar'
+import { useVisitor } from './VisitorContext'
+import { Helmet } from 'react-helmet'
/**
* @param {{ onChange: Function }} props
*/
function ChangeAvatarForm({ onChange }) {
- const [visitor] = useVisitor();
- const [avatar, setAvatar] = useState('');
- const [preview, setPreview] = useState();
- const avatarInput = useRef();
+ const [visitor] = useVisitor()
+ const [avatar, setAvatar] = useState('')
+ const [preview, setPreview] = useState()
+ const avatarInput = useRef()
let avatarChanged = (newAvatar) => {
- setAvatar(newAvatar);
- setPreview('');
+ setAvatar(newAvatar)
+ setPreview('')
if (newAvatar) {
- let reader = new FileReader();
+ let reader = new FileReader()
reader.onloadend = (preview) => {
- setPreview(preview.target.result);
- };
- reader.readAsDataURL(avatarInput.current.files[0]);
+ setPreview(preview.target.result)
+ }
+ reader.readAsDataURL(avatarInput.current.files[0])
}
- };
- let previewUser = { ...visitor, uname: '<preview>' };
+ }
+ let previewUser = { ...visitor, uname: '<preview>' }
if (preview) {
- previewUser = { ...visitor, avatar: preview, uname: '<preview>' };
+ previewUser = { ...visitor, avatar: preview, uname: '<preview>' }
}
let onSubmitAvatar = async (event) => {
if (event.preventDefault) {
- event.preventDefault();
+ event.preventDefault()
}
- await updateAvatar(avatarInput.current.files[0]);
- avatarChanged('');
- let visitor = await me();
- onChange(visitor);
- };
+ await updateAvatar(avatarInput.current.files[0])
+ avatarChanged('')
+ let visitor = await me()
+ onChange(visitor)
+ }
return (
<form>
<small>Recommendations: PNG, 96x96, &lt;50Kb. Also, JPG and GIF supported.</small>
@@ -50,7 +50,7 @@ function ChangeAvatarForm({ onChange }) {
<Avatar user={previewUser} />
<Button onClick={onSubmitAvatar}>Update</Button>
</form>
- );
+ )
}
/**
@@ -58,47 +58,47 @@ function ChangeAvatarForm({ onChange }) {
*/
export default function Settings({ onChange }) {
- const [visitor] = useVisitor();
+ const [visitor] = useVisitor()
let passwordChanged = () => {
- console.log('password changed');
- };
+ console.log('password changed')
+ }
let onSubmitPassword = (event) => {
if (event.preventDefault) {
- event.preventDefault();
+ event.preventDefault()
}
- console.log('password update');
- };
+ console.log('password update')
+ }
let emailChanged = () => {
- console.log('email update');
- };
+ console.log('email update')
+ }
let disableTelegram = () => {
- console.log('telegram disable');
- };
+ console.log('telegram disable')
+ }
let disableFacebook = (event) => {
if (event.preventDefault) {
- event.preventDefault();
+ event.preventDefault()
}
- console.log('facebook disable');
- };
+ console.log('facebook disable')
+ }
let enableFacebook = (event) => {
if (event.preventDefault) {
- event.preventDefault();
+ event.preventDefault()
}
- console.log('facebook enable');
- };
+ console.log('facebook enable')
+ }
let disableTwitter = () => {
- console.log('twitter disable');
- };
+ console.log('twitter disable')
+ }
let deleteJid = () => {
// TODO
- };
+ }
let addEmail = () => {
// TODO
- };
+ }
let deleteEmail = () => {
// TODO
- };
+ }
return (
<div className="msg-cont">
<Helmet>
@@ -249,7 +249,7 @@ export default function Settings({ onChange }) {
</fieldset>
</div>
- );
+ )
}
diff --git a/vnext/src/ui/Spinner.js b/vnext/src/ui/Spinner.js
index 3e38571e..b611abb8 100644
--- a/vnext/src/ui/Spinner.js
+++ b/vnext/src/ui/Spinner.js
@@ -1,5 +1,5 @@
-import { memo } from 'react';
-import ContentLoader from 'react-content-loader';
+import { memo } from 'react'
+import ContentLoader from 'react-content-loader'
function Spinner(props) {
return (
@@ -20,10 +20,10 @@ function Spinner(props) {
</ContentLoader>
</div>
</div>
- );
+ )
}
-export default memo(Spinner);
+export default memo(Spinner)
/**
*
@@ -42,5 +42,5 @@ export function ChatSpinner(props) {
<rect x="56" y="20" rx="0" ry="0" width="85" height="6.4" />
<rect x="0" y="0" rx="0" ry="0" width="48" height="48" />
</ContentLoader>
- );
+ )
}
diff --git a/vnext/src/ui/Thread.js b/vnext/src/ui/Thread.js
index a3136c35..b727b73d 100644
--- a/vnext/src/ui/Thread.js
+++ b/vnext/src/ui/Thread.js
@@ -1,128 +1,128 @@
-import { useEffect, useState, useCallback } from 'react';
-import { useLocation, useParams } from 'react-router-dom';
+import { useEffect, useState, useCallback } from 'react'
+import { useLocation, useParams } from 'react-router-dom'
-import Comment from './Comment';
-import Message from './Message';
-import MessageInput from './MessageInput';
-import Spinner from './Spinner';
+import Comment from './Comment'
+import Message from './Message'
+import MessageInput from './MessageInput'
+import Spinner from './Spinner'
-import { getMessages, comment, update, post } from '../api';
-import { useVisitor } from './VisitorContext';
-import { Helmet } from 'react-helmet';
+import { getMessages, comment, update, post } from '../api'
+import { useVisitor } from './VisitorContext'
+import { Helmet } from 'react-helmet'
/**
* @type { import('../api').Message }
*/
-const emptyMessage = {};
+const emptyMessage = {}
/**
* Thread component
* @param {import('react').PropsWithChildren<{}>} props
*/
export default function Thread(props) {
- const location = useLocation();
- const params = useParams();
- const [message, setMessage] = useState((location.state || {}).data || {});
- const [replies, setReplies] = useState([]);
- const [loading, setLoading] = useState(false);
- const [active, setActive] = useState(0);
+ const location = useLocation()
+ const params = useParams()
+ const [message, setMessage] = useState((location.state || {}).data || {})
+ const [replies, setReplies] = useState([])
+ const [loading, setLoading] = useState(false)
+ const [active, setActive] = useState(0)
- const [editing, setEditing] = useState(emptyMessage);
- const [visitor] = useVisitor();
- const [hash] = useState(visitor.hash);
- const { mid } = params;
+ const [editing, setEditing] = useState(emptyMessage)
+ const [visitor] = useVisitor()
+ const [hash] = useState(visitor.hash)
+ const { mid } = params
let loadReplies = useCallback(() => {
- document.body.scrollTop = 0;
- document.documentElement.scrollTop = 0;
- setReplies([]);
- setLoading(true);
+ document.body.scrollTop = 0
+ document.documentElement.scrollTop = 0
+ setReplies([])
+ setLoading(true)
let params = {
mid: mid
- };
- params.hash = hash;
+ }
+ params.hash = hash
getMessages('/api/thread', params)
.then(response => {
- let updatedMessage = response.data.shift();
+ let updatedMessage = response.data.shift()
if (!message.mid) {
- setMessage(updatedMessage);
+ setMessage(updatedMessage)
}
- setReplies(response.data);
- setLoading(false);
- setActive(0);
+ setReplies(response.data)
+ setLoading(false)
+ setActive(0)
}
).catch(ex => {
- console.log(ex);
- });
- }, [hash, message.mid, mid]);
+ console.log(ex)
+ })
+ }, [hash, message.mid, mid])
let postComment = async ({ body, attach }) => {
try {
let res = editing.rid ? await update(mid, editing.rid, body)
- : await comment(mid, active, body, attach);
- let result = res.status == 200;
+ : await comment(mid, active, body, attach)
+ let result = res.status == 200
if (result) {
- setEditing(emptyMessage);
+ setEditing(emptyMessage)
}
- return result;
+ return result
} catch (e) {
- console.error(e);
+ console.error(e)
}
- return false;
- };
+ return false
+ }
let startEditing = (reply) => {
- setActive(reply.to.rid || 0);
- setEditing(reply);
- };
+ setActive(reply.to.rid || 0)
+ setEditing(reply)
+ }
useEffect(() => {
- setActive(0);
- loadReplies();
- }, [loadReplies]);
+ setActive(0)
+ loadReplies()
+ }, [loadReplies])
useEffect(() => {
let onReply = (json) => {
- const msg = JSON.parse(json.data);
+ const msg = JSON.parse(json.data)
if (msg.mid == message.mid) {
setReplies(oldReplies => {
- return [...oldReplies, msg];
- });
+ return [...oldReplies, msg]
+ })
setActive(prev => {
- return prev + 1;
- });
+ return prev + 1
+ })
}
- };
+ }
if (props.connection.addEventListener && message.mid) {
- props.connection.addEventListener('msg', onReply);
+ props.connection.addEventListener('msg', onReply)
}
return () => {
if (props.connection.removeEventListener && message.mid) {
- props.connection.removeEventListener('msg', onReply);
+ props.connection.removeEventListener('msg', onReply)
}
- };
- }, [props.connection, message.mid]);
+ }
+ }, [props.connection, message.mid])
- const loaders = Math.min(message.replies || 0, 10);
- const pageTitle = `${params.user} ${message && message.tags || 'thread'}`;
+ const loaders = Math.min(message.replies || 0, 10)
+ const pageTitle = `${params.user} ${message && message.tags || 'thread'}`
/** @type { import('./Message').ToggleSubscriptionCallback } */
const handleSubsciptionToggle = (message) => {
if (message.subscribed) {
if (confirm('Unsubscribe?')) {
post(`U #${message.mid}`).then((response) => {
if (response.status === 200) {
- setMessage({...message, subscribed: false});
+ setMessage({...message, subscribed: false})
}
- }).catch(console.error);
+ }).catch(console.error)
}
} else {
if (confirm('Subscribe?')) {
post(`S #${message.mid}`).then((response) => {
if (response.status === 200) {
- setMessage({...message, subscribed: true});
+ setMessage({...message, subscribed: true})
}
- }).catch(console.error);
+ }).catch(console.error)
}
}
- };
+ }
return (
<>
<Helmet>
@@ -155,5 +155,5 @@ export default function Thread(props) {
</ul>
}
</>
- );
+ )
}
diff --git a/vnext/src/ui/UploadButton.js b/vnext/src/ui/UploadButton.js
index b652e522..5ef2fd94 100644
--- a/vnext/src/ui/UploadButton.js
+++ b/vnext/src/ui/UploadButton.js
@@ -1,4 +1,4 @@
-import Icon from './Icon';
+import Icon from './Icon'
/**
* @typedef {object} UploadButtonProps
@@ -13,20 +13,20 @@ import Icon from './Icon';
*/
export default function UploadButton(props) {
let openfile = () => {
- const input = props.inputRef.current;
+ const input = props.inputRef.current
if (props.value) {
- props.onChange('');
+ props.onChange('')
} else {
- input.click();
+ input.click()
}
- };
+ }
/**
* @param {import('react').ChangeEvent<HTMLInputElement>} event
*/
let attachmentChanged = (event) => {
- props.onChange(event.target.value);
- };
+ props.onChange(event.target.value)
+ }
return (
<div style={props.value ? activeStyle : inactiveStyle}
onClick={openfile}>
@@ -35,14 +35,14 @@ export default function UploadButton(props) {
style={{ display: 'none' }} ref={props.inputRef} value={props.value}
onChange={attachmentChanged} />
</div>
- );
+ )
}
const inactiveStyle = {
cursor: 'pointer',
color: '#888'
-};
+}
const activeStyle = {
cursor: 'pointer',
color: 'green'
-};
+}
diff --git a/vnext/src/ui/UserInfo.js b/vnext/src/ui/UserInfo.js
index 2ca8c431..f71dfcdc 100644
--- a/vnext/src/ui/UserInfo.js
+++ b/vnext/src/ui/UserInfo.js
@@ -1,13 +1,13 @@
-import { memo, useState, useEffect, useRef } from 'react';
-import { Link } from 'react-router-dom';
+import { memo, useState, useEffect, useRef } from 'react'
+import { Link } from 'react-router-dom'
-import { info, fetchUserUri } from '../api';
+import { info, fetchUserUri } from '../api'
-import Avatar from './Avatar';
-import Icon from './Icon';
-import defaultAvatar from '../assets/av-96.png';
+import Avatar from './Avatar'
+import Icon from './Icon'
+import defaultAvatar from '../assets/av-96.png'
-let isMounted;
+let isMounted
/**
* User info component
@@ -17,19 +17,19 @@ export default function UserInfo({ uname, onUpdate, children }) {
const [user, setUser] = useState({
uname: uname,
uid: 0
- });
+ })
useEffect(() => {
- isMounted = true;
+ isMounted = true
info(uname).then(response => {
if (isMounted) {
- onUpdate && onUpdate(response.data);
- setUser(response.data);
+ onUpdate && onUpdate(response.data)
+ setUser(response.data)
}
- }).catch(console.log);
+ }).catch(console.log)
return () => {
- isMounted = false;
- };
- }, [onUpdate, uname]);
+ isMounted = false
+ }
+ }, [onUpdate, uname])
return (
<>
<div className="userinfo">
@@ -59,7 +59,7 @@ export default function UserInfo({ uname, onUpdate, children }) {
</div>
{children}
</>
- );
+ )
}
/**
@@ -67,22 +67,22 @@ export default function UserInfo({ uname, onUpdate, children }) {
* @param {{user: import('../api').User}} props
*/
function Summary({ user }) {
- const readUrl = `/${user.uname}/friends`;
- const readersUrl = `/${user.uname}/readers`;
- const blUrl = `/${user.uname}/bl`;
- let read = user.read && <Link key={readUrl} to={readUrl}>I read: {user.read.length}</Link>;
- let readers = user.readers && <Link key={readersUrl} to={readersUrl}>My readers: {user.readers.length}</Link>;
- let mybl = user.statsMyBL && <Link key={blUrl} to={blUrl}>My blacklist: {user.statsMyBL}</Link>;
- let presentItems = [read, readers, mybl].filter(Boolean);
+ const readUrl = `/${user.uname}/friends`
+ const readersUrl = `/${user.uname}/readers`
+ const blUrl = `/${user.uname}/bl`
+ let read = user.read && <Link key={readUrl} to={readUrl}>I read: {user.read.length}</Link>
+ let readers = user.readers && <Link key={readersUrl} to={readersUrl}>My readers: {user.readers.length}</Link>
+ let mybl = user.statsMyBL && <Link key={blUrl} to={blUrl}>My blacklist: {user.statsMyBL}</Link>
+ let presentItems = [read, readers, mybl].filter(Boolean)
return (
<div className="msg-summary">
{presentItems.length > 0 && presentItems.reduce((prev, curr) =>
[prev, ' ', curr])}
</div>
- );
+ )
}
-const UserSummary = memo(Summary);
+const UserSummary = memo(Summary)
/**
@@ -90,10 +90,10 @@ const UserSummary = memo(Summary);
* @param {{ user: import('../api').User}} props
*/
export function UserLink(props) {
- const [user, setUser] = useState(props.user);
- const userRef = useRef(user);
+ const [user, setUser] = useState(props.user)
+ const userRef = useRef(user)
useEffect(() => {
- isMounted = true;
+ isMounted = true
if (userRef.current.uri) {
fetchUserUri(userRef.current.uri).then(remote_user => {
if (isMounted) {
@@ -102,7 +102,7 @@ export function UserLink(props) {
uname: remote_user.preferredUsername,
avatar: remote_user.icon && remote_user.icon.url,
uri: userRef.current.uri
- });
+ })
}
}).catch(() => {
setUser({
@@ -110,13 +110,13 @@ export function UserLink(props) {
uname: userRef.current.uri,
uri: userRef.current.uri,
avatar: defaultAvatar
- });
- });
+ })
+ })
}
return () => {
- isMounted = false;
- };
- }, [props.user]);
+ isMounted = false
+ }
+ }, [props.user])
return (
user.uid ?
<Link key={user.uid} to={`/${user.uname}/`} className="info-avatar">
@@ -125,5 +125,5 @@ export function UserLink(props) {
: <a href={user.uri} className="info-avatar">
<img src={user.avatar || defaultAvatar} />{user.uname}
</a>
- );
+ )
}
diff --git a/vnext/src/ui/Users.js b/vnext/src/ui/Users.js
index 32ff2d03..e4fcba1f 100644
--- a/vnext/src/ui/Users.js
+++ b/vnext/src/ui/Users.js
@@ -1,15 +1,15 @@
-import { useState } from 'react';
-import { useParams } from 'react-router-dom';
+import { useState } from 'react'
+import { useParams } from 'react-router-dom'
-import UserInfo from './UserInfo';
-import Avatar from './Avatar';
-import { Helmet } from 'react-helmet';
+import UserInfo from './UserInfo'
+import Avatar from './Avatar'
+import { Helmet } from 'react-helmet'
/**
* Friends feed
*/
export function Friends() {
- const params = useParams();
+ const params = useParams()
return (
<>
<Helmet>
@@ -17,14 +17,14 @@ export function Friends() {
</Helmet>
<Users uname={params.user} prop='read' />
</>
- );
+ )
}
/**
* Readers feed
*/
export function Readers() {
- const params = useParams();
+ const params = useParams()
return (
<>
<Helmet>
@@ -32,7 +32,7 @@ export function Readers() {
</Helmet>
<Users uname={params.user} prop='readers' />
</>
- );
+ )
}
/**
@@ -40,7 +40,7 @@ export function Readers() {
* @param {{uname: string, prop: string}} props
*/
function Users({ uname, prop }) {
- const [user, setUser] = useState({ uid: 0, uname: uname });
+ const [user, setUser] = useState({ uid: 0, uname: uname })
return (
<UserInfo uname={uname} onUpdate={setUser}>
<div style={{ display: 'flex', flexWrap: 'wrap', flexDirection: 'row' }}>
@@ -52,5 +52,5 @@ function Users({ uname, prop }) {
}
</div>
</UserInfo>
- );
+ )
}
diff --git a/vnext/src/ui/VisitorContext.js b/vnext/src/ui/VisitorContext.js
index 240b709b..9740f9ca 100644
--- a/vnext/src/ui/VisitorContext.js
+++ b/vnext/src/ui/VisitorContext.js
@@ -1,18 +1,18 @@
-import { createContext, useContext, useState } from 'react';
+import { createContext, useContext, useState } from 'react'
-const Visitor = createContext();
+const Visitor = createContext()
/** @type {import('../api').SecureUser} */
const unknownUser = {
uid: -1
-};
+}
/**
* @param { import('react').PropsWithChildren<{}> } props
*/
export function VisitorProvider({ children }) {
- const state = useState(unknownUser);
- return <Visitor.Provider value={state}>{children}</Visitor.Provider>;
+ const state = useState(unknownUser)
+ return <Visitor.Provider value={state}>{children}</Visitor.Provider>
}
/**
@@ -23,9 +23,9 @@ export function VisitorProvider({ children }) {
* ]} visitor hook
*/
export function useVisitor() {
- const visitor = useContext(Visitor);
+ const visitor = useContext(Visitor)
if (visitor === undefined) {
- throw new Error('useVisitor must be used within a VisitorProvider');
+ throw new Error('useVisitor must be used within a VisitorProvider')
}
- return visitor;
+ return visitor
} \ No newline at end of file
diff --git a/vnext/src/ui/__tests__/Avatar.test.js b/vnext/src/ui/__tests__/Avatar.test.js
index f454f6c7..7aea804c 100644
--- a/vnext/src/ui/__tests__/Avatar.test.js
+++ b/vnext/src/ui/__tests__/Avatar.test.js
@@ -1,14 +1,14 @@
-import { MemoryRouter } from 'react-router-dom';
+import { MemoryRouter } from 'react-router-dom'
-import Avatar from '../Avatar';
-import renderer from 'react-test-renderer';
+import Avatar from '../Avatar'
+import renderer from 'react-test-renderer'
test('Avatar renders correctly', () => {
const component = renderer.create(
<MemoryRouter>
<Avatar user={{ uid: 1, uname: 'ugnich', avatar: 'https://juick.com/i/a/1-deadbeef.png' }} />
</MemoryRouter>
- );
- let tree = component.toJSON();
- expect(tree).toMatchSnapshot();
-});
+ )
+ let tree = component.toJSON()
+ expect(tree).toMatchSnapshot()
+})
diff --git a/vnext/src/ui/__tests__/MessageInput.test.js b/vnext/src/ui/__tests__/MessageInput.test.js
index 0bfe2569..4af36b71 100644
--- a/vnext/src/ui/__tests__/MessageInput.test.js
+++ b/vnext/src/ui/__tests__/MessageInput.test.js
@@ -1,46 +1,46 @@
-import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react'
-import MessageInput from '../MessageInput';
+import MessageInput from '../MessageInput'
test('Gives immediate focus on to textarea on load', async () => {
- let draft = 'draft';
- render(<MessageInput text={draft} onSend={async () => { return true; }} />);
- expect(screen.getByText(draft)).toHaveFocus();
-});
+ let draft = 'draft'
+ render(<MessageInput text={draft} onSend={async () => { return true }} />)
+ expect(screen.getByText(draft)).toHaveFocus()
+})
test('Submits on ctrl-enter and pass validation', async () => {
- let result = false;
- const onSend = jest.fn(async ({ body }) => { result = body === 'YO'; return result; });
- let draft = 'draft';
- render(<MessageInput onSend={onSend} text={draft} />);
- let textarea = screen.getByText(draft);
- fireEvent.change(textarea, { target: { value: 'HI' } });
+ let result = false
+ const onSend = jest.fn(async ({ body }) => { result = body === 'YO'; return result })
+ let draft = 'draft'
+ render(<MessageInput onSend={onSend} text={draft} />)
+ let textarea = screen.getByText(draft)
+ fireEvent.change(textarea, { target: { value: 'HI' } })
// this event should not submit
fireEvent.keyPress(textarea, {
charCode: 13,
which: 13,
keyCode: 13
- });
+ })
// this event should submit
fireEvent.keyPress(textarea, {
charCode: 13,
which: 13,
keyCode: 13,
ctrlKey: true
- });
- expect(onSend).toHaveBeenCalledTimes(1);
- expect(result).toBe(false);
- fireEvent.change(textarea, { target: { value: 'YO' } });
+ })
+ expect(onSend).toHaveBeenCalledTimes(1)
+ expect(result).toBe(false)
+ fireEvent.change(textarea, { target: { value: 'YO' } })
fireEvent.keyPress(textarea, {
charCode: 13,
which: 13,
keyCode: 13,
ctrlKey: true
- });
- expect(onSend).toHaveBeenCalledTimes(2);
- expect(result).toBe(true);
- await waitFor(() => expect(textarea).toHaveTextContent(''));
- textarea.focus();
- expect(textarea).toHaveFocus();
-});
+ })
+ expect(onSend).toHaveBeenCalledTimes(2)
+ expect(result).toBe(true)
+ await waitFor(() => expect(textarea).toHaveTextContent(''))
+ textarea.focus()
+ expect(textarea).toHaveFocus()
+})
diff --git a/vnext/src/ui/__tests__/UserLink.test.js b/vnext/src/ui/__tests__/UserLink.test.js
index 6bb4da29..99ca42ce 100644
--- a/vnext/src/ui/__tests__/UserLink.test.js
+++ b/vnext/src/ui/__tests__/UserLink.test.js
@@ -1,10 +1,10 @@
-import { MemoryRouter } from 'react-router-dom';
+import { MemoryRouter } from 'react-router-dom'
-import { UserLink } from '../UserInfo';
-import renderer, { act } from 'react-test-renderer';
+import { UserLink } from '../UserInfo'
+import renderer, { act } from 'react-test-renderer'
test('UserLink renders correctly', async () => {
- let component = null;
+ let component = null
act(() => {
component = renderer.create(
<MemoryRouter>
@@ -14,8 +14,8 @@ test('UserLink renders correctly', async () => {
<UserLink user={{ uid: 0, uname: '', uri: 'https://example.com/u/test' }} />
</>
</MemoryRouter>
- );
- });
- let tree = component.toJSON();
- expect(tree).toMatchSnapshot();
-});
+ )
+ })
+ let tree = component.toJSON()
+ expect(tree).toMatchSnapshot()
+})
diff --git a/vnext/src/ui/helpers/BubbleStyle.js b/vnext/src/ui/helpers/BubbleStyle.js
index d2886e1e..def60b62 100644
--- a/vnext/src/ui/helpers/BubbleStyle.js
+++ b/vnext/src/ui/helpers/BubbleStyle.js
@@ -4,9 +4,9 @@
* @returns { import('react').CSSProperties} CSS properties
*/
export function chatItemStyle(me, msg) {
- const user = msg.user;
- const isMe = me.uid === user.uid;
- const alignment = isMe ? 'flex-end' : 'flex-start';
+ const user = msg.user
+ const isMe = me.uid === user.uid
+ const alignment = isMe ? 'flex-end' : 'flex-start'
return {
padding: '3px 6px',
listStyle: 'none',
@@ -14,5 +14,5 @@ export function chatItemStyle(me, msg) {
display: 'flex',
flexDirection: 'column',
alignItems: alignment
- };
+ }
}