From f707d3d524d8d16e2bb780764f029d85fc57ecc0 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 26 Jul 2019 13:22:00 +0300 Subject: prop-types -> jsdoc --- vnext/src/ui/Avatar.css | 4 +- vnext/src/ui/Avatar.js | 24 +-- vnext/src/ui/Button.js | 3 + vnext/src/ui/Chat.js | 44 ++--- vnext/src/ui/Contact.js | 19 ++- vnext/src/ui/Feeds.js | 13 +- vnext/src/ui/Header.js | 17 +- vnext/src/ui/Icon.js | 26 ++- vnext/src/ui/Input.js | 18 ++- vnext/src/ui/Login.js | 27 ++-- vnext/src/ui/Message.js | 48 +++--- vnext/src/ui/MessageInput.js | 33 ++-- vnext/src/ui/NavigationIcon.js | 9 ++ vnext/src/ui/PM.js | 7 +- vnext/src/ui/Post.js | 6 +- vnext/src/ui/Settings.js | 310 +++++++++++++++++------------------- vnext/src/ui/Thread.js | 46 +++--- vnext/src/ui/Types.js | 15 -- vnext/src/ui/UploadButton.js | 22 ++- vnext/src/ui/UserInfo.css | 4 +- vnext/src/ui/UserInfo.js | 40 ++--- vnext/src/ui/Users.js | 28 ++-- vnext/src/ui/helpers/BubbleStyle.js | 10 ++ 23 files changed, 409 insertions(+), 364 deletions(-) delete mode 100644 vnext/src/ui/Types.js (limited to 'vnext/src/ui') diff --git a/vnext/src/ui/Avatar.css b/vnext/src/ui/Avatar.css index f48f2b9e..9f7d22e3 100644 --- a/vnext/src/ui/Avatar.css +++ b/vnext/src/ui/Avatar.css @@ -13,8 +13,8 @@ } .info-avatar img { - max-height: 24px; - max-width: 24px; + max-height: 48px; + max-width: 48px; padding: 6px; vertical-align: middle; } diff --git a/vnext/src/ui/Avatar.js b/vnext/src/ui/Avatar.js index ecce4e9f..e08c1ba4 100644 --- a/vnext/src/ui/Avatar.js +++ b/vnext/src/ui/Avatar.js @@ -1,14 +1,23 @@ import React, { memo } from 'react'; -import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; -import { UserType } from './Types'; - import Icon from './Icon'; import './Avatar.css'; -function Avatar({ user, style, link, children}) { +/** + * @typedef {Object} AvatarProps + * @property {import('../api').User} user + * @property {React.CSSProperties=} style + * @property {string=} link + * @property {React.ReactNode=} children + */ + +/** + * Avatar component + * @param {AvatarProps} props + */ +function Avatar({ user, style, link, children }) { return (
@@ -35,10 +44,3 @@ function Avatar({ user, style, link, children}) { } export default memo(Avatar); - -Avatar.propTypes = { - user: UserType, - link: PropTypes.string, - style: PropTypes.object, - children: PropTypes.node -}; diff --git a/vnext/src/ui/Button.js b/vnext/src/ui/Button.js index 18cab0a7..033c972c 100644 --- a/vnext/src/ui/Button.js +++ b/vnext/src/ui/Button.js @@ -2,6 +2,9 @@ import React from 'react'; import './Button.css'; +/** + * @param {React.ClassAttributes & React.ButtonHTMLAttributes} props + */ function Button(props) { return (
+ (max. length - 16 symbols)

+ + +
+ Telegram + {visitor.telegramName ? (
-

Change password: -
- (max. length - 16 symbols)

-
-
-
- Telegram - {me.telegramName ? ( -
-
Telegram: {me.telegramName} — +
Telegram: {visitor.telegramName} -
- - ) : ( -

To connect Telegram account: send any text message to @Juick_bot -

- )} -
- {me.jids && ( -
-
- XMPP accounts - -

Your accounts:

-

- { - me.jids.map(jid => - -
-
- ) - } -

- { - me.jids && me.jids.length > 1 && -

- } -

To add new jabber account: send any text message to juick@juick.com -

-
+
- )} -
- E-mail -
-

Add account:
- - - + ) : ( +

To connect Telegram account: send any text message to @Juick_bot

-
-
+ )} +
+ {me.jids && ( + +
+ XMPP accounts +

Your accounts:

{ - me.emails ? me.emails.map(email => - - -
+ visitor.jids.map(jid => + +
- ) : '-' + ) }

{ - me.emails && me.emails.length > 1 && - + visitor.jids && visitor.jids.length > 1 && +

} - +

To add new jabber account: send any text message to juick@juick.com +

+
+ + )} +
+ E-mail +
+

Add account:
+ + + +

+
+
+

Your accounts:

+

+ { + visitor.emails ? visitor.emails.map(email => + + +
+
+ ) : '-' + } +

{ - me.emails && - <> - {/** email_off **/} - - You can receive notifications to email:
- Sent to -
- {/** /email_off **/} -

 

-

You can post to Juick via e-mail. Send your plain text messages to juick@juick.com. You can attach one photo or video file.

- + visitor.emails && visitor.emails.length > 1 && + } -
-
- Facebook - {me.facebookStatus && me.facebookStatus.connected ? ( - me.facebookStatus.crosspostEnabled ? + + { + visitor.emails && + <> + {/** email_off **/} +
+ You can receive notifications to email:
+ Sent to +
+ {/** /email_off **/} +

 

+

You can post to Juick via e-mail. Send your plain text messages to juick@juick.com. + You can attach one photo or video file.

+ + } +
+
+ Facebook + { + visitor.facebookStatus && visitor.facebookStatus.connected ? ( + visitor.facebookStatus.crosspostEnabled ?
- Facebook: Enabled — - + Facebook: Enabled— +
:
- Facebook: Disabled — - + Facebook: Disabled— +
) : ( @@ -240,29 +224,29 @@ export default class Settings extends React.Component {

)} -
-
- Twitter - {me.twitterName ? -
-
Twitter: {me.twitterName} — +
+
+ Twitter + {visitor.twitterName ? + +
Twitter: {visitor.twitterName} - -
- - : -

Cross-posting to Twitter: Connect to Twitter

- } -
+ +
+ + : +

Cross-posting to Twitter: Connect to Twitter

+ } + - - ); - } + + ); } - +/* Settings.propTypes = { visitor: UserType.isRequired, onChange: PropTypes.func.isRequired }; +*/ diff --git a/vnext/src/ui/Thread.js b/vnext/src/ui/Thread.js index f965e80c..ae5e4f3c 100644 --- a/vnext/src/ui/Thread.js +++ b/vnext/src/ui/Thread.js @@ -1,8 +1,4 @@ import React, { useEffect, useState, useRef, useCallback } from 'react'; -import PropTypes from 'prop-types'; - -import ReactRouterPropTypes from 'react-router-prop-types'; -import { UserType, MessageType } from './Types'; import Message from './Message'; import MessageInput from './MessageInput'; @@ -101,7 +97,7 @@ function Comment({ msg, draft, visitor, active, setActive, onStartEditing, postC ); } - +/* Comment.propTypes = { msg: MessageType.isRequired, draft: PropTypes.string.isRequired, @@ -110,7 +106,7 @@ Comment.propTypes = { setActive: PropTypes.func.isRequired, onStartEditing: PropTypes.func.isRequired, postComment: PropTypes.func.isRequired -}; +};*/ export default function Thread(props) { const [message, setMessage] = useState((props.location.state || {}).msg || {}); @@ -118,22 +114,8 @@ export default function Thread(props) { const [loading, setLoading] = useState(false); const [active, setActive] = useState(0); const [editing, setEditing] = useState({}); - useEffect(() => { - setActive(0); - loadReplies(); - }, [loadReplies]); - useEffect(() => { - if (props.connection.addEventListener && message.mid) { - props.connection.addEventListener('msg', onReply); - } - return () => { - if (props.connection.removeEventListener && message.mid) { - props.connection.removeEventListener('msg', onReply); - } - }; - }, [props.connection, message.mid, onReply]); - let loadReplies = useCallback(() => { + let loadReplies = () => { document.body.scrollTop = 0; document.documentElement.scrollTop = 0; @@ -156,7 +138,7 @@ export default function Thread(props) { ).catch(ex => { console.log(ex); }); - }, [props.visitor, props.match.params]); + }; let onReply = useCallback((json) => { const msg = JSON.parse(json.data); if (msg.mid == message.mid) { @@ -165,6 +147,7 @@ export default function Thread(props) { }); } }, [message]); + let postComment = (template) => { const { mid, rid, body, attach } = template; let commentAction = editing.rid ? update(mid, editing.rid, body) : comment(mid, rid, body, attach); @@ -180,6 +163,21 @@ export default function Thread(props) { setEditing(reply); }; + useEffect(() => { + setActive(0); + loadReplies(); + }, []); + useEffect(() => { + if (props.connection.addEventListener && message.mid) { + props.connection.addEventListener('msg', onReply); + } + return () => { + if (props.connection.removeEventListener && message.mid) { + props.connection.removeEventListener('msg', onReply); + } + }; + }, [props.connection, message.mid, onReply]); + const loaders = Math.min(message.replies || 0, 10); return ( <> @@ -214,11 +212,11 @@ export default function Thread(props) { const linkStyle = { cursor: 'pointer' }; - +/* Thread.propTypes = { location: ReactRouterPropTypes.location, history: ReactRouterPropTypes.history, match: ReactRouterPropTypes.match, visitor: UserType.isRequired, connection: PropTypes.object.isRequired, -}; +};*/ diff --git a/vnext/src/ui/Types.js b/vnext/src/ui/Types.js deleted file mode 100644 index 9bf7b513..00000000 --- a/vnext/src/ui/Types.js +++ /dev/null @@ -1,15 +0,0 @@ -import PropTypes from 'prop-types'; - -export const UserType = PropTypes.shape({ - uid: PropTypes.number.isRequired, - uname: PropTypes.string, - avatar: PropTypes.string, - uri: PropTypes.string -}); - -export const MessageType = PropTypes.shape({ - mid: PropTypes.number, - user: UserType, - timestamp: PropTypes.string.isRequired, - body: PropTypes.string -}); diff --git a/vnext/src/ui/UploadButton.js b/vnext/src/ui/UploadButton.js index 73cbbfcf..28a1f340 100644 --- a/vnext/src/ui/UploadButton.js +++ b/vnext/src/ui/UploadButton.js @@ -1,8 +1,18 @@ import React from 'react'; -import PropTypes from 'prop-types'; import Icon from './Icon'; +/** + * @typedef {Object} UploadButtonProps + * @property {string} value + * @property {React.MutableRefObject} inputRef + * @property {function} onChange + */ + +/** + * Upload button + * @param {UploadButtonProps} props + */ export default function UploadButton(props) { let openfile = () => { const input = props.inputRef.current; @@ -12,6 +22,10 @@ export default function UploadButton(props) { input.click(); } }; + + /** + * @param {React.ChangeEvent} event + */ let attachmentChanged = (event) => { props.onChange(event.target.value); }; @@ -26,12 +40,6 @@ export default function UploadButton(props) { ); } -UploadButton.propTypes = { - value: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - inputRef: PropTypes.shape({ current: PropTypes.instanceOf(Element) }) -}; - const inactiveStyle = { cursor: 'pointer', color: '#888' diff --git a/vnext/src/ui/UserInfo.css b/vnext/src/ui/UserInfo.css index 92cfdb6c..3e692a86 100644 --- a/vnext/src/ui/UserInfo.css +++ b/vnext/src/ui/UserInfo.css @@ -4,8 +4,8 @@ margin: 12px; } .info-avatar img { - max-height: 24px; - max-width: 24px; + max-height: 36px; + max-width: 36px; padding: 6px; vertical-align: middle; } diff --git a/vnext/src/ui/UserInfo.js b/vnext/src/ui/UserInfo.js index 0d06d134..faa2ebd6 100644 --- a/vnext/src/ui/UserInfo.js +++ b/vnext/src/ui/UserInfo.js @@ -1,9 +1,6 @@ import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; -import { UserType } from './Types'; - import { info, fetchUserUri } from '../api'; import Avatar from './Avatar'; @@ -14,8 +11,20 @@ import './UserInfo.css'; let isMounted; +/** + * Wrapper for dumb VSCode + * @param {import('../api').User} user + */ +function useUserState(user) { + return useState(user); +} + +/** + * User info component + * @param {{user: string, onUpdate?: function, children?: Element}} props + */ export default function UserInfo(props) { - const [user, setUser] = useState({ uname: props.user, uid: 0 }); + const [user, setUser] = useUserState({ uname: props.user, uid: 0 }); const { onUpdate } = props; useEffect(() => { isMounted = true; @@ -63,6 +72,10 @@ export default function UserInfo(props) { ); } +/** + * User summary component + * @param {{user: import('../api').User}} props + */ function Summary({ user }) { const readUrl = `/${user.uname}/friends`; const readersUrl = `/${user.uname}/readers`; @@ -78,12 +91,13 @@ function Summary({ user }) { ); } -Summary.propTypes = { - user: UserType.isRequired -}; - const UserSummary = React.memo(Summary); + +/** + * Link to user + * @param {{ user: import('../api').User}} props + */ export function UserLink(props) { const [user, setUser] = useState(props.user); useEffect(() => { @@ -105,13 +119,3 @@ export function UserLink(props) { : {user.uname} ); } - -UserInfo.propTypes = { - user: PropTypes.string.isRequired, - onUpdate: PropTypes.func, - children: PropTypes.node -}; - -UserLink.propTypes = { - user: UserType.isRequired -}; diff --git a/vnext/src/ui/Users.js b/vnext/src/ui/Users.js index a10bba7f..4c09318f 100644 --- a/vnext/src/ui/Users.js +++ b/vnext/src/ui/Users.js @@ -1,18 +1,28 @@ import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import ReactRouterPropTypes from 'react-router-prop-types'; import UserInfo from './UserInfo'; import Avatar from './Avatar'; +/** + * Friends feed + * @param {{match: import('react-router').match }} match + */ export function Friends({ match }) { return ; } +/** + * Readers feed + * @param {{match: import('react-router').match }} match + */ export function Readers({ match }) { return ; } +/** + * UserInfo list component + * @param {{user: import('../api').User, prop: string}} props + */ function Users(props) { const [user, setUser] = useState({ uid: 0, uname: props.user }); return ( @@ -28,17 +38,3 @@ function Users(props) { ); } - - -Friends.propTypes = { - match: ReactRouterPropTypes.match.isRequired -}; - -Readers.propTypes = { - match: ReactRouterPropTypes.match.isRequired -}; - -Users.propTypes = { - user: PropTypes.string.isRequired, - prop: PropTypes.string.isRequired -}; diff --git a/vnext/src/ui/helpers/BubbleStyle.js b/vnext/src/ui/helpers/BubbleStyle.js index f44f726e..f8b0f4eb 100644 --- a/vnext/src/ui/helpers/BubbleStyle.js +++ b/vnext/src/ui/helpers/BubbleStyle.js @@ -1,3 +1,8 @@ +/** + * @param {import('../../api').User} me + * @param {import('../../api').Message} msg + * @returns {React.CSSProperties} + */ export function bubbleStyle(me, msg) { const isMe = me.uid === msg.user.uid; const color = isMe ? '#fff' : '#222'; @@ -10,6 +15,11 @@ export function bubbleStyle(me, msg) { }; } +/** + * @param {import('../../api').User} me + * @param {import('../../api').Message} msg + * @returns {React.CSSProperties} + */ export function chatItemStyle(me, msg) { const isMe = me.uid === msg.user.uid; const alignment = isMe ? 'flex-end' : 'flex-start'; -- cgit v1.2.3