From c344dfbe108c95a18c40ec57c63a3190e23fcd5e Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Wed, 24 Aug 2022 19:11:07 +0430 Subject: [PATCH 01/23] Redesigned and improved Notification page --- config/default.js | 2 +- src/components/fontawesome-icons.jsx | 2 + src/components/notification-body.jsx | 52 ++++++ src/components/notifications.jsx | 239 +++++++++++++++++++++------ src/redux/action-creators.js | 18 ++ src/redux/action-types.js | 2 + src/redux/reducers.js | 10 ++ src/redux/reducers/posts.js | 11 ++ src/services/api.js | 6 + styles/shared/post.scss | 10 +- 10 files changed, 295 insertions(+), 57 deletions(-) create mode 100644 src/components/notification-body.jsx diff --git a/config/default.js b/config/default.js index e10a78296..47d81b636 100644 --- a/config/default.js +++ b/config/default.js @@ -6,7 +6,7 @@ const DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24; export default { api: { - root: 'https://candy.freefeed.net', + root: 'https://freefeed.net', }, siteTitle: 'FreeFeed', diff --git a/src/components/fontawesome-icons.jsx b/src/components/fontawesome-icons.jsx index fe68da783..9cf1d3b7d 100644 --- a/src/components/fontawesome-icons.jsx +++ b/src/components/fontawesome-icons.jsx @@ -6,6 +6,7 @@ import { faGlobeAmericas, faUserFriends, faPaperclip, + faClock, } from '@fortawesome/free-solid-svg-icons'; import { faComment, faHeart as faHeartO, faImage } from '@fortawesome/free-regular-svg-icons'; import { faCommentPlus } from './fontawesome-custom-icons'; @@ -24,6 +25,7 @@ const preloadedIcons = [ faUserFriends, faImage, faPaperclip, + faClock, ]; export const SVGSymbolDeclarations = memo(function SVGSymbolDeclarations() { diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx new file mode 100644 index 000000000..4d3e3be06 --- /dev/null +++ b/src/components/notification-body.jsx @@ -0,0 +1,52 @@ +import { useEffect } from 'react'; +import { useDispatch, useSelector} from 'react-redux'; + +import { getSinglePostBody, getSingleComment} from '../redux/action-creators'; + +/** + * @param {string} id + */ + +export function getCommentBody(id) { + const dispatch = useDispatch(); + useEffect(() => void dispatch(getSingleComment(id)), [dispatch, id]); + return useSelector((state) => { + return { + cmBody: state.comments[id] + }; + }); +} + +export function getPostBody(id) { + const dispatch = useDispatch(); + useEffect(() => void dispatch(getSinglePostBody(id)), [dispatch, id]); + return useSelector((state) => { + return { + psBody: state.posts[id] + }; + }); +} + +export function SingleComment({id = null}) { + if (id) { + const { cmBody } = getCommentBody(id); + var commentBody = {}; + Object.assign(commentBody, cmBody); + return
{commentBody.body?.replace(/@[a-zA-Z0-9_]+/g, '')}
+ } else { + return null + } +} + +export function SinglePost({id = null}) { + if (id) { + const { psBody } = getPostBody(id); + var postBody = {}; + Object.assign(postBody, psBody); + return
{postBody.body?.replace(/@[a-zA-Z0-9_]+/g, '')}
+ } else { + return null + } +} + + diff --git a/src/components/notifications.jsx b/src/components/notifications.jsx index 5c95bbf32..6b228f070 100644 --- a/src/components/notifications.jsx +++ b/src/components/notifications.jsx @@ -2,13 +2,18 @@ import { useMemo } from 'react'; import { connect, useSelector } from 'react-redux'; import { Link } from 'react-router'; +import { + faClock, +} from '@fortawesome/free-solid-svg-icons'; import { Throbber } from './throbber'; import Linkify from './linkify'; import TimeDisplay from './time-display'; import PaginatedView from './paginated-view'; import ErrorBoundary from './error-boundary'; -import UserName from './user-name'; +import { UserPicture } from './user-picture'; +import { Icon } from './fontawesome-icons'; +import { SingleComment, SinglePost } from './notification-body'; import { SignInLink } from './sign-in-link'; const getAuthorName = ({ postAuthor, createdUser, group }) => { @@ -42,6 +47,7 @@ const commentLink = (event, text = 'comment') => ) : ( text ); + const backlinkLink = (event) => event.target_comment_id ? ( comment @@ -53,138 +59,230 @@ const backlinkLink = (event) => const notificationTemplates = { subscription_request_revoked: (event) => ( - {`@${event.createdUser.username} revoked subscription request to you`} +
+ {event.createdUser.screenName} + {' revoked subscription request to you'} +
), mention_in_post: (event) => (
- {`@${event.createdUser.username} mentioned you in the `} + {event.createdUser.screenName} + {' mentioned you in the '} {postLink(event)} {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} +
), mention_in_comment: (event) => (
- {`@${event.createdUser.username} mentioned you in a `} + {event.createdUser.screenName} + {' mentioned you in a '} {commentLink(event, 'comment')} {` to the `} {postLink(event)} - {`${event.group.username ? ` [in @${event.group.username}]` : ''}`} + {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} +
), mention_comment_to: (event) => (
- {`@${event.createdUser.username} `} + {event.createdUser.screenName}{' '} {commentLink(event, 'replied')} {` to you in the `} {postLink(event)} - {` ${event.group.username ? `[in @${event.group.username}]` : ''}`} + {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} +
), backlink_in_comment: (event) => (
- {`@${event.createdUser.username}`} mentioned your {backlinkLink(event)} in - a {commentLink(event, 'comment')} + {event.createdUser.screenName} + {' mentioned your '} + {backlinkLink(event)} in a {commentLink(event, 'comment')} {` to the `} {postLink(event)} - {`${event.group.username ? ` [in @${event.group.username}]` : ''}`} + {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} +
), backlink_in_post: (event) => (
- {`@${event.createdUser.username}`} mentioned your {backlinkLink(event)} in - the {postLink(event)} - {`${event.group.username ? ` [in @${event.group.username}]` : ''}`} + {event.createdUser.screenName} + {' mentioned your '} + {backlinkLink(event)} in the {postLink(event)} + {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} + +
+ ), + banned_user: (event) => ( +
+ {'You blocked '} + {event.affectedUser.screenName} +
+ ), + unbanned_user: (event) => ( +
+ {'You unblocked '} + {event.affectedUser.screenName}
), - banned_user: (event) => {`You blocked @${event.affectedUser.username}`}, - unbanned_user: (event) => {`You unblocked @${event.affectedUser.username}`}, subscription_requested: (event) => (
- @{event.createdUser.username} sent you a - subscription request + {event.createdUser.screenName} + {' sent you a subscription request '} +
), user_subscribed: (event) => ( - {`@${event.createdUser.username} subscribed to your feed`} +
+ {event.createdUser.screenName} + {' subscribed to your feed'} +
), user_unsubscribed: (event) => ( - {`@${event.createdUser.username} unsubscribed from your feed`} +
+ {event.createdUser.screenName} + {' unsubscribed from your feed'} +
), subscription_request_approved: (event) => ( - {`Your subscription request to @${event.createdUser.username} was approved`} +
+ {'Your subscription request to '} + {event.createdUser.screenName} + {' was approved'} +
), subscription_request_rejected: (event) => ( - {`Your subscription request to @${event.createdUser.username} was rejected`} +
+ {'Your subscription request to '} + {event.createdUser.screenName} + {' was rejected'} +
+ ), + group_created: (event) => ( +
+ {'You created a group '} + {event.group.screenName} +
), - group_created: (event) => {`You created a group @${event.group.username}`}, group_subscription_requested: (event) => (
- @{event.createdUser.username} sent a request to - join @{event.group.username} that you admin{' '} - + {event.createdUser.screenName} + {' sent a request to join '} + {event.group.screenName} + {' that you admin'}
), group_admin_promoted: (event) => ( - {`@${event.createdUser.username} promoted @${event.affectedUser.username} to admin in the group @${event.group.username}`} +
+ {event.createdUser.screenName} + {' promoted '} + {event.affectedUser.screenName} + {' to admin in the group '} + {event.group.screenName} +
), group_admin_demoted: (event) => ( - {`@${event.createdUser.username} revoked admin privileges from @${event.affectedUser.username} in group @${event.group.username}`} +
+ {event.createdUser.screenName} + {' revoked admin privileges from '} + {event.affectedUser.screenName} + {' in group '} + {event.group.screenName} +
), managed_group_subscription_approved: (event) => ( - {`@${event.affectedUser.username} request to join @${event.group.username} was approved by @${event.createdUser.username}`} +
+ {event.affectedUser.screenName} + {' request to join '} + {event.group.screenName} + {' was approved by '} + {event.createdUser.screenName} +
), managed_group_subscription_rejected: (event) => ( - {`@${event.affectedUser.username} request to join @${event.group.username} was rejected`} +
+ {event.affectedUser.screenName} + {' request to join '} + {event.group.screenName} + {' was rejected'} +
), group_subscription_approved: (event) => ( - {`Your request to join group @${event.group.username} was approved`} +
+ {'Your request to join group '} + {event.group.screenName} + {' was approved'} +
), group_subscription_request_revoked: (event) => ( - {`@${event.createdUser.username} revoked subscription request to @${event.group.username}`} +
+ {event.createdUser.screenName} + {' revoked subscription request to '} + {event.group.screenName} +
), direct_left: (event) => event.created_user_id === event.receiver.id ? (
- You left a direct message created by {`@${event.postAuthor.username}`} + {'You left a direct message created by '} + {event.postAuthor.screenName}
) : event.post_author_id === event.receiver.id ? (
- {`@${event.createdUser.username}`} left a {directPostLink(event)} created - by you + {event.createdUser.screenName} + {' left a '} {directPostLink(event)} {' created by you'}
) : (
- {`@${event.createdUser.username}`} left a {directPostLink(event)} created - by {`@${event.postAuthor.username}`} + {event.createdUser.screenName} + {' left a '} {directPostLink(event)} {' created by '} + {event.postAuthor.screenName}
), direct: (event) => (
{`You received a `} {directPostLink(event)} - {` from @${event.createdUser.username}`} + {` from `} + {event.createdUser.screenName} +
), direct_comment: (event) => (
{commentLink(event, 'New comment')} - {` was posted to a `} + {' was posted to a '} {directPostLink(event)} - {` from @${event.createdUser.username}`} + {' from '} + {event.createdUser.screenName} +
), group_subscription_rejected: (event) => ( - {`Your request to join group @${event.group.username} was rejected`} +
+ {'Your request to join group '}{' '} + {event.group.screenName} {' was rejected'} +
), group_subscribed: (event) => ( - {`@${event.createdUser.username} subscribed to @${event.group.username}`} +
+ {event.createdUser.screenName} + {' subscribed to '} {event.group.screenName} +
), group_unsubscribed: (event) => ( - {`@${event.createdUser.username} unsubscribed from @${event.group.username}`} +
+ {event.createdUser.screenName} + {' unsubscribed from '} {event.group.screenName} +
), invitation_used: (event) => ( - {`@${event.createdUser.username} has joined ${CONFIG.siteTitle} using your invitation`} +
+ {event.createdUser.screenName} + {' has joined '} ${CONFIG.siteTitle} {' using your invitation '} +
), banned_by_user: () => `Notification shouldn't be shown`, @@ -192,31 +290,54 @@ const notificationTemplates = { comment_moderated: (event) => (
- {`@${event.createdUser.username} has deleted your comment to the `} + {event.createdUser.screenName}{' '} + {' has deleted your comment to the '} {postLink(event)} - {event.group_id ? {` in the group @${event.group.username}`} : null} + {event.group_id ? ( + + {' in the group '} + {event.group.screenName} + + ) : null}
), comment_moderated_by_another_admin: (event) => (
- {`@${event.createdUser.username} has removed a comment from @${event.affectedUser.username} to the `} + {event.createdUser.screenName}{' '} + {' has removed a comment from '}{' '} + {event.affectedUser.screenName}{' '} + {' to the '} {postLink(event)} - {` in the group @${event.group.username}`} + {' in the group '} + {event.group.screenName}
), post_moderated: (event) => (
- {`@${event.createdUser.username} has removed your `} + {event.createdUser.screenName}{' '} + {' has removed your '} {event.post_id ? postLink(event) : 'post'} - {event.group_id ? {` from the group @${event.group.username}`} : null} + {event.group_id ? ( + + {' from the group '} + {event.group.screenName} + + ) : null}
), post_moderated_by_another_admin: (event) => (
- {`@${event.createdUser.username} has removed the `} + {event.createdUser.screenName}{' '} + {' has removed the '} {event.post_id ? postLink(event) : 'post'} - {` from @${event.affectedUser.username} `} - {event.group_id ? {` from the group @${event.group.username}`} : null} + {' from '} + {event.affectedUser.screenName} + {event.group_id ? ( + + {` from the group `} + {event.group.screenName} + + ) : null}
), }; @@ -259,9 +380,17 @@ const nop = () => false; const Notification = ({ event_type, ...props }) => { return ( -
- {(notificationTemplates[event_type] || nop)(props)} - +
+
+ +
+
+
{(notificationTemplates[event_type] || nop)(props)}
+
+
+ {' '} + +
); }; diff --git a/src/redux/action-creators.js b/src/redux/action-creators.js index 91d7a7382..061513613 100644 --- a/src/redux/action-creators.js +++ b/src/redux/action-creators.js @@ -1216,3 +1216,21 @@ export function getCommentByNumber(postId, seqNumber) { payload: { postId, seqNumber }, }; } + +export function getSingleComment(commentId) { + return { + type: ActionTypes.GET_SINGLE_COMMENT, + apiRequest: Api.getSingleComment, + nonAuthRequest: true, + payload: { commentId } + }; +} + +export function getSinglePostBody(postId) { + return { + type: ActionTypes.GET_SINGLE_POST_BODY, + apiRequest: Api.getPost, + nonAuthRequest: true, + payload: { postId }, + }; +} diff --git a/src/redux/action-types.js b/src/redux/action-types.js index a13722378..c3a398b63 100644 --- a/src/redux/action-types.js +++ b/src/redux/action-types.js @@ -66,6 +66,8 @@ export const TOGGLE_HIDDEN_POSTS = 'TOGGLE_HIDDEN_POSTS'; export const SUBSCRIBERS = 'SUBSCRIBERS'; export const SUBSCRIPTIONS = 'SUBSCRIPTIONS'; export const GET_USER_INFO = 'GET_USER_INFO'; +export const GET_SINGLE_COMMENT= 'GET_SINGLE_COMMENT'; +export const GET_SINGLE_POST_BODY= 'GET_SINGLE_POST_BODY'; export const CREATE_GROUP = 'CREATE_GROUP'; export const UPDATE_GROUP = 'UPDATE_GROUP'; export const UPDATE_GROUP_PICTURE = 'UPDATE_GROUP_PICTURE'; diff --git a/src/redux/reducers.js b/src/redux/reducers.js index 5c335a9e2..6b00a746a 100644 --- a/src/redux/reducers.js +++ b/src/redux/reducers.js @@ -725,6 +725,16 @@ export function comments(state = {}, action) { }, }; } + case response(ActionTypes.GET_SINGLE_COMMENT): { + return { + ...state, + [action.payload.comments.id]: { + ...state[action.payload.comments.id], + ...action.payload.comments, + isExpanded: true, + }, + }; + } case response(ActionTypes.SAVE_EDITING_COMMENT): { return { ...state, diff --git a/src/redux/reducers/posts.js b/src/redux/reducers/posts.js index abee0b120..db08feb77 100644 --- a/src/redux/reducers/posts.js +++ b/src/redux/reducers/posts.js @@ -11,6 +11,7 @@ import { DISABLE_COMMENTS, ENABLE_COMMENTS, GET_SINGLE_POST, + GET_SINGLE_POST_BODY, HIDE_POST, LIKE_POST, LIKE_POST_OPTIMISTIC, @@ -54,6 +55,16 @@ export function posts(state = {}, action) { state = savePostStatusesReducer(state, action); switch (action.type) { + case response(GET_SINGLE_POST_BODY): { + return { + ...state, + [action.payload.posts.id]: { + ...state[action.payload.posts.id], + ...action.payload.posts, + isExpanded: true, + }, + }; + } case response(SHOW_MORE_COMMENTS): { const post = state[action.payload.posts.id]; if (!post) { diff --git a/src/services/api.js b/src/services/api.js index eb166c54d..bd0293a78 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -366,6 +366,8 @@ export function getUserInfo({ username }) { return fetch(`${apiRoot}/v1/users/${username}`, getRequestOptions()); } + + export function createGroup(groupSettings) { return fetch(`${apiRoot}/v1/groups`, postRequestOptions('POST', { group: groupSettings })); } @@ -752,3 +754,7 @@ export function sanitizeMedia() { export function getCommentByNumber({ postId, seqNumber }) { return fetch(`${apiRoot}/v2/posts/${postId}/comments/${seqNumber}`, getRequestOptions()); } + +export function getSingleComment({ commentId }) { + return fetch(`${apiRoot}/v1/comments/${commentId}`, getRequestOptions()); +} diff --git a/styles/shared/post.scss b/styles/shared/post.scss index bee6ac0ef..fcc08e9d7 100644 --- a/styles/shared/post.scss +++ b/styles/shared/post.scss @@ -18,7 +18,7 @@ $post-line-height: rem(20px); border-bottom: 1px solid #eee; line-height: rem(19px); position: relative; - + min-height: 80px; --bg-color: white; .dark-theme & { @@ -57,6 +57,10 @@ $post-line-height: rem(20px); .post-recipient { font-weight: 500; } + .post-notif { + margin-top: rem(5px); + margin-bottom: rem(5px); + } } .post-body { @@ -142,6 +146,10 @@ $post-line-height: rem(20px); } } +.notif-time-date{ + margin-top: 12px; +} + .post-footer-icon { width: rem(19px); flex: 0 0 auto; From 17f46243896aaca51c4e4a7de022cf9541f84c81 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 25 Aug 2022 01:19:38 +0430 Subject: [PATCH 02/23] Update notification-body.jsx --- src/components/notification-body.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx index 4d3e3be06..cb766c9d5 100644 --- a/src/components/notification-body.jsx +++ b/src/components/notification-body.jsx @@ -48,5 +48,3 @@ export function SinglePost({id = null}) { return null } } - - From 08b25e0ff080c0b99ac2d769163f90e4965c7f33 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 25 Aug 2022 01:20:46 +0430 Subject: [PATCH 03/23] Redesigned and improved Notification page --- src/components/notification-body.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx index cb766c9d5..67be6a871 100644 --- a/src/components/notification-body.jsx +++ b/src/components/notification-body.jsx @@ -5,7 +5,7 @@ import { getSinglePostBody, getSingleComment} from '../redux/action-creators'; /** * @param {string} id - */ +*/ export function getCommentBody(id) { const dispatch = useDispatch(); From 5614b8c839c91d81b06e7294f3dff2c047cc306f Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 25 Aug 2022 11:46:44 +0430 Subject: [PATCH 04/23] fix mouse hover --- src/components/notification-body.jsx | 560 ++++++++++++++++++++++++--- 1 file changed, 515 insertions(+), 45 deletions(-) diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx index 67be6a871..d4a562283 100644 --- a/src/components/notification-body.jsx +++ b/src/components/notification-body.jsx @@ -1,50 +1,520 @@ -import { useEffect } from 'react'; -import { useDispatch, useSelector} from 'react-redux'; - -import { getSinglePostBody, getSingleComment} from '../redux/action-creators'; - -/** - * @param {string} id -*/ - -export function getCommentBody(id) { - const dispatch = useDispatch(); - useEffect(() => void dispatch(getSingleComment(id)), [dispatch, id]); - return useSelector((state) => { - return { - cmBody: state.comments[id] - }; - }); -} +/* global CONFIG */ +import { useMemo } from 'react'; +import { connect, useSelector } from 'react-redux'; +import { Link } from 'react-router'; +import { + faClock, +} from '@fortawesome/free-solid-svg-icons'; -export function getPostBody(id) { - const dispatch = useDispatch(); - useEffect(() => void dispatch(getSinglePostBody(id)), [dispatch, id]); - return useSelector((state) => { - return { - psBody: state.posts[id] - }; - }); -} +import { Throbber } from './throbber'; +import Linkify from './linkify'; +import TimeDisplay from './time-display'; +import PaginatedView from './paginated-view'; +import ErrorBoundary from './error-boundary'; +import { UserPicture } from './user-picture'; +import UserName from './user-name'; +import { Icon } from './fontawesome-icons'; +import { SingleComment, SinglePost } from './notification-body'; +import { SignInLink } from './sign-in-link'; -export function SingleComment({id = null}) { - if (id) { - const { cmBody } = getCommentBody(id); - var commentBody = {}; - Object.assign(commentBody, cmBody); - return
{commentBody.body?.replace(/@[a-zA-Z0-9_]+/g, '')}
- } else { - return null - } -} +const getAuthorName = ({ postAuthor, createdUser, group }) => { + if (group && group.username) { + return group.username; + } + if (postAuthor && postAuthor.username) { + return postAuthor.username; + } + return createdUser.username; +}; + +const generatePostUrl = ({ post_id, ...event }) => `/${getAuthorName(event)}/${post_id}`; +const generateCommentUrl = ({ post_id, comment_id, ...event }) => + `/${getAuthorName(event)}/${post_id}#comment-${comment_id}`; +const postLink = (event) => + event.post_id ? post : 'deleted post'; +const directPostLink = (event) => + event.post_id ? ( + direct message + ) : ( + 'deleted direct message' + ); +const commentLink = (event, text = 'comment') => + event.comment_id ? ( + {text} + ) : text === 'comment' ? ( + 'deleted comment' + ) : text === 'New comment' ? ( + 'Deleted comment' + ) : ( + text + ); + +const backlinkLink = (event) => + event.target_comment_id ? ( + comment + ) : event.target_post_id ? ( + post + ) : ( + 'deleted entry' + ); + +const notificationTemplates = { + subscription_request_revoked: (event) => ( +
+ + {' revoked subscription request to you'} +
+ ), + + mention_in_post: (event) => ( +
+ + {' mentioned you in the '} + {postLink(event)} + {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} + +
+ ), + mention_in_comment: (event) => ( +
+ + {' mentioned you in a '} + {commentLink(event, 'comment')} + {` to the `} + {postLink(event)} + {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} + +
+ ), + mention_comment_to: (event) => ( +
+ {' '} + {commentLink(event, 'replied')} + {` to you in the `} + {postLink(event)} + {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} + +
+ ), + backlink_in_comment: (event) => ( +
+ + {' mentioned your '} + {backlinkLink(event)} in a {commentLink(event, 'comment')} + {` to the `} + {postLink(event)} + {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} + +
+ ), + backlink_in_post: (event) => ( +
+ + {' mentioned your '} + {backlinkLink(event)} in the {postLink(event)} + {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} + +
+ ), + banned_user: (event) => ( +
+ {'You blocked '} + +
+ ), + unbanned_user: (event) => ( +
+ {'You unblocked '} + +
+ ), + subscription_requested: (event) => ( +
+ + {' sent you a subscription request '} + +
+ ), + user_subscribed: (event) => ( +
+ + {' subscribed to your feed'} +
+ ), + user_unsubscribed: (event) => ( +
+ + {' unsubscribed from your feed'} +
+ ), + subscription_request_approved: (event) => ( +
+ {'Your subscription request to '} + + {' was approved'} +
+ ), + subscription_request_rejected: (event) => ( +
+ {'Your subscription request to '} + + {' was rejected'} +
+ ), + group_created: (event) => ( +
+ {'You created a group '} + +
+ ), + group_subscription_requested: (event) => ( +
+ + {' sent a request to join '} + + {' that you admin'} +
+ ), + group_admin_promoted: (event) => ( +
+ + {' promoted '} + + {' to admin in the group '} + +
+ ), + group_admin_demoted: (event) => ( +
+ + {' revoked admin privileges from '} + + {' in group '} + +
+ ), + managed_group_subscription_approved: (event) => ( +
+ + {' request to join '} + + {' was approved by '} + +
+ ), + managed_group_subscription_rejected: (event) => ( +
+ + {' request to join '} + + {' was rejected'} +
+ ), + group_subscription_approved: (event) => ( +
+ {'Your request to join group '} + + {' was approved'} +
+ ), + group_subscription_request_revoked: (event) => ( +
+ + {' revoked subscription request to '} + +
+ ), + direct_left: (event) => + event.created_user_id === event.receiver.id ? ( +
+ {'You left a direct message created by '} + +
+ ) : event.post_author_id === event.receiver.id ? ( +
+ + {' left a '} {directPostLink(event)} {' created by you'} +
+ ) : ( +
+ + {' left a '} {directPostLink(event)} {' created by '} + +
+ ), + direct: (event) => ( +
+ {`You received a `} + {directPostLink(event)} + {` from `} + + +
+ ), + direct_comment: (event) => ( +
+ {commentLink(event, 'New comment')} + {' was posted to a '} + {directPostLink(event)} + {' from '} + + +
+ ), + group_subscription_rejected: (event) => ( +
+ {'Your request to join group '}{' '} + {' was rejected'} +
+ ), + group_subscribed: (event) => ( +
+ + {' subscribed to '} +
+ ), + group_unsubscribed: (event) => ( +
+ + {' unsubscribed from '} +
+ ), + invitation_used: (event) => ( +
+ + {' has joined '} ${CONFIG.siteTitle} {' using your invitation '} +
+ ), + + banned_by_user: () => `Notification shouldn't be shown`, + unbanned_by_user: () => `Notification shouldn't be shown`, + + comment_moderated: (event) => ( +
+ {' '} + {' has deleted your comment to the '} + {postLink(event)} + {event.group_id ? ( + + {' in the group '} + + + ) : null} +
+ ), + comment_moderated_by_another_admin: (event) => ( +
+ {' '} + {' has removed a comment from '}{' '} + {' '} + {' to the '} + {postLink(event)} + {' in the group '} + +
+ ), + post_moderated: (event) => ( +
+ {' '} + {' has removed your '} + {event.post_id ? postLink(event) : 'post'} + {event.group_id ? ( + + {' from the group '} + + + ) : null} +
+ ), + post_moderated_by_another_admin: (event) => ( +
+ {' '} + {' has removed the '} + {event.post_id ? postLink(event) : 'post'} + {' from '} + + {event.group_id ? ( + + {` from the group `} + + + ) : null} +
+ ), +}; -export function SinglePost({id = null}) { - if (id) { - const { psBody } = getPostBody(id); - var postBody = {}; - Object.assign(postBody, psBody); - return
{postBody.body?.replace(/@[a-zA-Z0-9_]+/g, '')}
- } else { - return null +const notificationClasses = { + mention_in_post: 'mention', + mention_in_comment: 'mention', + mention_comment_to: 'mention', + banned_user: 'ban', + unbanned_user: 'ban', + invitation_used: 'subscription', + subscription_requested: 'subscription', + subscription_request_revoked: 'subscription', + user_subscribed: 'subscription', + user_unsubscribed: 'subscription', + subscription_request_approved: 'subscription', + subscription_request_rejected: 'subscription', + group_created: 'group', + group_subscription_requested: 'group', + group_admin_promoted: 'group', + group_admin_demoted: 'group', + group_subscription_approved: 'group', + group_subscription_request_revoked: 'group', + group_subscription_rejected: 'group', + group_subscribed: 'group', + group_unsubscribed: 'group', + managed_group_subscription_approved: 'group', + managed_group_subscription_rejected: 'group', + direct: 'direct', + direct_comment: 'direct', + banned_by_user: 'ban', + unbanned_by_user: 'ban', + comment_moderated: 'group', + comment_moderated_by_another_admin: 'group', + post_moderated: 'group', + post_moderated_by_another_admin: 'group', +}; + +const nop = () => false; + +const Notification = ({ event_type, ...props }) => { + return ( +
+
+ +
+
+
{(notificationTemplates[event_type] || nop)(props)}
+
+
+ {' '} + +
+
+ ); +}; + +const isFilterActive = (filterName, filter) => filter && filter.includes(filterName); + +const Notifications = (props) => ( +
+ +
+ Notifications + {props.isLoading && ( + + + + )} +
+
+
Show:
+ + Everything + + + Mentions + + + Subscriptions + + + Groups + + + Direct messages + + + Bans + +
+ {props.authenticated ? ( + +
+ {props.loading + ? 'Loading' + : props.events.length > 0 + ? props.events.map(Notification) + : 'No notifications yet'} +
+
+ ) : ( +
+ You must sign in or sign up before + visiting this page. +
+ )} +
+
+); + +const mock = {}; + +const mapStateToProps = (state) => { + return { + isLoading: state.notifications.loading, + filter: state.routing.locationBeforeTransitions.query.filter, + authenticated: state.authenticated, + events: (state.notifications.events || []).map((event) => { + return { + ...event, + createdUser: + state.users[event.created_user_id] || state.subscribers[event.created_user_id] || mock, + affectedUser: + state.users[event.affected_user_id] || state.subscribers[event.affected_user_id] || mock, + group: state.users[event.group_id] || mock, + postAuthor: state.users[event.post_author_id], + post: state.posts[event.post_id] || mock, + comment: state.comments[event.comment_id] || mock, + receiver: state.user, + }; + }), + }; +}; + +export default connect(mapStateToProps)(Notifications); + +function ReviewRequestLink({ from, group = null }) { + const managedGroups = useSelector((state) => state.managedGroups); + const requests = useSelector((state) => state.userRequests); + + const hasRequest = useMemo(() => { + if (group) { + const g = managedGroups.find((g) => g.id === group.id); + return g && g.requests.some((u) => u.id === from.id); + } + return requests.some((u) => u.id === from.id); + }, [requests, managedGroups, group, from]); + + if (!hasRequest) { + return null; } + + return ( + <> + + Review + + + ); } From 4888fa087d6cd7dde56504cc45e7952f0beafde6 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 25 Aug 2022 12:04:00 +0430 Subject: [PATCH 05/23] fix mouse hover --- src/components/notifications.jsx | 105 ++++++++++++++++--------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/src/components/notifications.jsx b/src/components/notifications.jsx index 6b228f070..d4a562283 100644 --- a/src/components/notifications.jsx +++ b/src/components/notifications.jsx @@ -12,6 +12,7 @@ import TimeDisplay from './time-display'; import PaginatedView from './paginated-view'; import ErrorBoundary from './error-boundary'; import { UserPicture } from './user-picture'; +import UserName from './user-name'; import { Icon } from './fontawesome-icons'; import { SingleComment, SinglePost } from './notification-body'; import { SignInLink } from './sign-in-link'; @@ -60,14 +61,14 @@ const backlinkLink = (event) => const notificationTemplates = { subscription_request_revoked: (event) => (
- {event.createdUser.screenName} + {' revoked subscription request to you'}
), mention_in_post: (event) => (
- {event.createdUser.screenName} + {' mentioned you in the '} {postLink(event)} {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} @@ -76,7 +77,7 @@ const notificationTemplates = { ), mention_in_comment: (event) => (
- {event.createdUser.screenName} + {' mentioned you in a '} {commentLink(event, 'comment')} {` to the `} @@ -87,7 +88,7 @@ const notificationTemplates = { ), mention_comment_to: (event) => (
- {event.createdUser.screenName}{' '} + {' '} {commentLink(event, 'replied')} {` to you in the `} {postLink(event)} @@ -97,7 +98,7 @@ const notificationTemplates = { ), backlink_in_comment: (event) => (
- {event.createdUser.screenName} + {' mentioned your '} {backlinkLink(event)} in a {commentLink(event, 'comment')} {` to the `} @@ -108,7 +109,7 @@ const notificationTemplates = { ), backlink_in_post: (event) => (
- {event.createdUser.screenName} + {' mentioned your '} {backlinkLink(event)} in the {postLink(event)} {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} @@ -118,127 +119,127 @@ const notificationTemplates = { banned_user: (event) => (
{'You blocked '} - {event.affectedUser.screenName} +
), unbanned_user: (event) => (
{'You unblocked '} - {event.affectedUser.screenName} +
), subscription_requested: (event) => (
- {event.createdUser.screenName} + {' sent you a subscription request '}
), user_subscribed: (event) => (
- {event.createdUser.screenName} + {' subscribed to your feed'}
), user_unsubscribed: (event) => (
- {event.createdUser.screenName} + {' unsubscribed from your feed'}
), subscription_request_approved: (event) => (
{'Your subscription request to '} - {event.createdUser.screenName} + {' was approved'}
), subscription_request_rejected: (event) => (
{'Your subscription request to '} - {event.createdUser.screenName} + {' was rejected'}
), group_created: (event) => (
{'You created a group '} - {event.group.screenName} +
), group_subscription_requested: (event) => (
- {event.createdUser.screenName} + {' sent a request to join '} - {event.group.screenName} + {' that you admin'}
), group_admin_promoted: (event) => (
- {event.createdUser.screenName} + {' promoted '} - {event.affectedUser.screenName} + {' to admin in the group '} - {event.group.screenName} +
), group_admin_demoted: (event) => (
- {event.createdUser.screenName} + {' revoked admin privileges from '} - {event.affectedUser.screenName} + {' in group '} - {event.group.screenName} +
), managed_group_subscription_approved: (event) => (
- {event.affectedUser.screenName} + {' request to join '} - {event.group.screenName} + {' was approved by '} - {event.createdUser.screenName} +
), managed_group_subscription_rejected: (event) => (
- {event.affectedUser.screenName} + {' request to join '} - {event.group.screenName} + {' was rejected'}
), group_subscription_approved: (event) => (
{'Your request to join group '} - {event.group.screenName} + {' was approved'}
), group_subscription_request_revoked: (event) => (
- {event.createdUser.screenName} + {' revoked subscription request to '} - {event.group.screenName} +
), direct_left: (event) => event.created_user_id === event.receiver.id ? (
{'You left a direct message created by '} - {event.postAuthor.screenName} +
) : event.post_author_id === event.receiver.id ? (
- {event.createdUser.screenName} + {' left a '} {directPostLink(event)} {' created by you'}
) : (
- {event.createdUser.screenName} + {' left a '} {directPostLink(event)} {' created by '} - {event.postAuthor.screenName} +
), direct: (event) => ( @@ -246,7 +247,7 @@ const notificationTemplates = { {`You received a `} {directPostLink(event)} {` from `} - {event.createdUser.screenName} +
), @@ -256,31 +257,31 @@ const notificationTemplates = { {' was posted to a '} {directPostLink(event)} {' from '} - {event.createdUser.screenName} +
), group_subscription_rejected: (event) => (
{'Your request to join group '}{' '} - {event.group.screenName} {' was rejected'} + {' was rejected'}
), group_subscribed: (event) => (
- {event.createdUser.screenName} - {' subscribed to '} {event.group.screenName} + + {' subscribed to '}
), group_unsubscribed: (event) => (
- {event.createdUser.screenName} - {' unsubscribed from '} {event.group.screenName} + + {' unsubscribed from '}
), invitation_used: (event) => (
- {event.createdUser.screenName} + {' has joined '} ${CONFIG.siteTitle} {' using your invitation '}
), @@ -290,52 +291,52 @@ const notificationTemplates = { comment_moderated: (event) => (
- {event.createdUser.screenName}{' '} + {' '} {' has deleted your comment to the '} {postLink(event)} {event.group_id ? ( {' in the group '} - {event.group.screenName} + ) : null}
), comment_moderated_by_another_admin: (event) => (
- {event.createdUser.screenName}{' '} + {' '} {' has removed a comment from '}{' '} - {event.affectedUser.screenName}{' '} + {' '} {' to the '} {postLink(event)} {' in the group '} - {event.group.screenName} +
), post_moderated: (event) => (
- {event.createdUser.screenName}{' '} + {' '} {' has removed your '} {event.post_id ? postLink(event) : 'post'} {event.group_id ? ( {' from the group '} - {event.group.screenName} + ) : null}
), post_moderated_by_another_admin: (event) => (
- {event.createdUser.screenName}{' '} + {' '} {' has removed the '} {event.post_id ? postLink(event) : 'post'} {' from '} - {event.affectedUser.screenName} + {event.group_id ? ( {` from the group `} - {event.group.screenName} + ) : null}
From b5bca324344a5e6efe1ef19a08029e9eaa9f079f Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 25 Aug 2022 12:07:54 +0430 Subject: [PATCH 06/23] Update notification-body.jsx --- src/components/notification-body.jsx | 560 +++------------------------ 1 file changed, 45 insertions(+), 515 deletions(-) diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx index d4a562283..67be6a871 100644 --- a/src/components/notification-body.jsx +++ b/src/components/notification-body.jsx @@ -1,520 +1,50 @@ -/* global CONFIG */ -import { useMemo } from 'react'; -import { connect, useSelector } from 'react-redux'; -import { Link } from 'react-router'; -import { - faClock, -} from '@fortawesome/free-solid-svg-icons'; - -import { Throbber } from './throbber'; -import Linkify from './linkify'; -import TimeDisplay from './time-display'; -import PaginatedView from './paginated-view'; -import ErrorBoundary from './error-boundary'; -import { UserPicture } from './user-picture'; -import UserName from './user-name'; -import { Icon } from './fontawesome-icons'; -import { SingleComment, SinglePost } from './notification-body'; -import { SignInLink } from './sign-in-link'; - -const getAuthorName = ({ postAuthor, createdUser, group }) => { - if (group && group.username) { - return group.username; - } - if (postAuthor && postAuthor.username) { - return postAuthor.username; - } - return createdUser.username; -}; - -const generatePostUrl = ({ post_id, ...event }) => `/${getAuthorName(event)}/${post_id}`; -const generateCommentUrl = ({ post_id, comment_id, ...event }) => - `/${getAuthorName(event)}/${post_id}#comment-${comment_id}`; -const postLink = (event) => - event.post_id ? post : 'deleted post'; -const directPostLink = (event) => - event.post_id ? ( - direct message - ) : ( - 'deleted direct message' - ); -const commentLink = (event, text = 'comment') => - event.comment_id ? ( - {text} - ) : text === 'comment' ? ( - 'deleted comment' - ) : text === 'New comment' ? ( - 'Deleted comment' - ) : ( - text - ); - -const backlinkLink = (event) => - event.target_comment_id ? ( - comment - ) : event.target_post_id ? ( - post - ) : ( - 'deleted entry' - ); - -const notificationTemplates = { - subscription_request_revoked: (event) => ( -
- - {' revoked subscription request to you'} -
- ), - - mention_in_post: (event) => ( -
- - {' mentioned you in the '} - {postLink(event)} - {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} - -
- ), - mention_in_comment: (event) => ( -
- - {' mentioned you in a '} - {commentLink(event, 'comment')} - {` to the `} - {postLink(event)} - {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} - -
- ), - mention_comment_to: (event) => ( -
- {' '} - {commentLink(event, 'replied')} - {` to you in the `} - {postLink(event)} - {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} - -
- ), - backlink_in_comment: (event) => ( -
- - {' mentioned your '} - {backlinkLink(event)} in a {commentLink(event, 'comment')} - {` to the `} - {postLink(event)} - {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} - -
- ), - backlink_in_post: (event) => ( -
- - {' mentioned your '} - {backlinkLink(event)} in the {postLink(event)} - {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} - -
- ), - banned_user: (event) => ( -
- {'You blocked '} - -
- ), - unbanned_user: (event) => ( -
- {'You unblocked '} - -
- ), - subscription_requested: (event) => ( -
- - {' sent you a subscription request '} - -
- ), - user_subscribed: (event) => ( -
- - {' subscribed to your feed'} -
- ), - user_unsubscribed: (event) => ( -
- - {' unsubscribed from your feed'} -
- ), - subscription_request_approved: (event) => ( -
- {'Your subscription request to '} - - {' was approved'} -
- ), - subscription_request_rejected: (event) => ( -
- {'Your subscription request to '} - - {' was rejected'} -
- ), - group_created: (event) => ( -
- {'You created a group '} - -
- ), - group_subscription_requested: (event) => ( -
- - {' sent a request to join '} - - {' that you admin'} -
- ), - group_admin_promoted: (event) => ( -
- - {' promoted '} - - {' to admin in the group '} - -
- ), - group_admin_demoted: (event) => ( -
- - {' revoked admin privileges from '} - - {' in group '} - -
- ), - managed_group_subscription_approved: (event) => ( -
- - {' request to join '} - - {' was approved by '} - -
- ), - managed_group_subscription_rejected: (event) => ( -
- - {' request to join '} - - {' was rejected'} -
- ), - group_subscription_approved: (event) => ( -
- {'Your request to join group '} - - {' was approved'} -
- ), - group_subscription_request_revoked: (event) => ( -
- - {' revoked subscription request to '} - -
- ), - direct_left: (event) => - event.created_user_id === event.receiver.id ? ( -
- {'You left a direct message created by '} - -
- ) : event.post_author_id === event.receiver.id ? ( -
- - {' left a '} {directPostLink(event)} {' created by you'} -
- ) : ( -
- - {' left a '} {directPostLink(event)} {' created by '} - -
- ), - direct: (event) => ( -
- {`You received a `} - {directPostLink(event)} - {` from `} - - -
- ), - direct_comment: (event) => ( -
- {commentLink(event, 'New comment')} - {' was posted to a '} - {directPostLink(event)} - {' from '} - - -
- ), - group_subscription_rejected: (event) => ( -
- {'Your request to join group '}{' '} - {' was rejected'} -
- ), - group_subscribed: (event) => ( -
- - {' subscribed to '} -
- ), - group_unsubscribed: (event) => ( -
- - {' unsubscribed from '} -
- ), - invitation_used: (event) => ( -
- - {' has joined '} ${CONFIG.siteTitle} {' using your invitation '} -
- ), - - banned_by_user: () => `Notification shouldn't be shown`, - unbanned_by_user: () => `Notification shouldn't be shown`, - - comment_moderated: (event) => ( -
- {' '} - {' has deleted your comment to the '} - {postLink(event)} - {event.group_id ? ( - - {' in the group '} - - - ) : null} -
- ), - comment_moderated_by_another_admin: (event) => ( -
- {' '} - {' has removed a comment from '}{' '} - {' '} - {' to the '} - {postLink(event)} - {' in the group '} - -
- ), - post_moderated: (event) => ( -
- {' '} - {' has removed your '} - {event.post_id ? postLink(event) : 'post'} - {event.group_id ? ( - - {' from the group '} - - - ) : null} -
- ), - post_moderated_by_another_admin: (event) => ( -
- {' '} - {' has removed the '} - {event.post_id ? postLink(event) : 'post'} - {' from '} - - {event.group_id ? ( - - {` from the group `} - - - ) : null} -
- ), -}; - -const notificationClasses = { - mention_in_post: 'mention', - mention_in_comment: 'mention', - mention_comment_to: 'mention', - banned_user: 'ban', - unbanned_user: 'ban', - invitation_used: 'subscription', - subscription_requested: 'subscription', - subscription_request_revoked: 'subscription', - user_subscribed: 'subscription', - user_unsubscribed: 'subscription', - subscription_request_approved: 'subscription', - subscription_request_rejected: 'subscription', - group_created: 'group', - group_subscription_requested: 'group', - group_admin_promoted: 'group', - group_admin_demoted: 'group', - group_subscription_approved: 'group', - group_subscription_request_revoked: 'group', - group_subscription_rejected: 'group', - group_subscribed: 'group', - group_unsubscribed: 'group', - managed_group_subscription_approved: 'group', - managed_group_subscription_rejected: 'group', - direct: 'direct', - direct_comment: 'direct', - banned_by_user: 'ban', - unbanned_by_user: 'ban', - comment_moderated: 'group', - comment_moderated_by_another_admin: 'group', - post_moderated: 'group', - post_moderated_by_another_admin: 'group', -}; - -const nop = () => false; - -const Notification = ({ event_type, ...props }) => { - return ( -
-
- -
-
-
{(notificationTemplates[event_type] || nop)(props)}
-
-
- {' '} - -
-
- ); -}; - -const isFilterActive = (filterName, filter) => filter && filter.includes(filterName); - -const Notifications = (props) => ( -
- -
- Notifications - {props.isLoading && ( - - - - )} -
-
-
Show:
- - Everything - - - Mentions - - - Subscriptions - - - Groups - - - Direct messages - - - Bans - -
- {props.authenticated ? ( - -
- {props.loading - ? 'Loading' - : props.events.length > 0 - ? props.events.map(Notification) - : 'No notifications yet'} -
-
- ) : ( -
- You must sign in or sign up before - visiting this page. -
- )} -
-
-); - -const mock = {}; - -const mapStateToProps = (state) => { - return { - isLoading: state.notifications.loading, - filter: state.routing.locationBeforeTransitions.query.filter, - authenticated: state.authenticated, - events: (state.notifications.events || []).map((event) => { - return { - ...event, - createdUser: - state.users[event.created_user_id] || state.subscribers[event.created_user_id] || mock, - affectedUser: - state.users[event.affected_user_id] || state.subscribers[event.affected_user_id] || mock, - group: state.users[event.group_id] || mock, - postAuthor: state.users[event.post_author_id], - post: state.posts[event.post_id] || mock, - comment: state.comments[event.comment_id] || mock, - receiver: state.user, - }; - }), - }; -}; - -export default connect(mapStateToProps)(Notifications); +import { useEffect } from 'react'; +import { useDispatch, useSelector} from 'react-redux'; + +import { getSinglePostBody, getSingleComment} from '../redux/action-creators'; + +/** + * @param {string} id +*/ + +export function getCommentBody(id) { + const dispatch = useDispatch(); + useEffect(() => void dispatch(getSingleComment(id)), [dispatch, id]); + return useSelector((state) => { + return { + cmBody: state.comments[id] + }; + }); +} -function ReviewRequestLink({ from, group = null }) { - const managedGroups = useSelector((state) => state.managedGroups); - const requests = useSelector((state) => state.userRequests); +export function getPostBody(id) { + const dispatch = useDispatch(); + useEffect(() => void dispatch(getSinglePostBody(id)), [dispatch, id]); + return useSelector((state) => { + return { + psBody: state.posts[id] + }; + }); +} - const hasRequest = useMemo(() => { - if (group) { - const g = managedGroups.find((g) => g.id === group.id); - return g && g.requests.some((u) => u.id === from.id); - } - return requests.some((u) => u.id === from.id); - }, [requests, managedGroups, group, from]); +export function SingleComment({id = null}) { + if (id) { + const { cmBody } = getCommentBody(id); + var commentBody = {}; + Object.assign(commentBody, cmBody); + return
{commentBody.body?.replace(/@[a-zA-Z0-9_]+/g, '')}
+ } else { + return null + } +} - if (!hasRequest) { - return null; +export function SinglePost({id = null}) { + if (id) { + const { psBody } = getPostBody(id); + var postBody = {}; + Object.assign(postBody, psBody); + return
{postBody.body?.replace(/@[a-zA-Z0-9_]+/g, '')}
+ } else { + return null } - - return ( - <> - - Review - - - ); } From 5b03bdf3ad13050592fdb364138644055aa52494 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 25 Aug 2022 17:31:49 +0430 Subject: [PATCH 07/23] fix user tag remover --- src/components/notification-body.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx index 67be6a871..3b2ebaac0 100644 --- a/src/components/notification-body.jsx +++ b/src/components/notification-body.jsx @@ -12,7 +12,8 @@ export function getCommentBody(id) { useEffect(() => void dispatch(getSingleComment(id)), [dispatch, id]); return useSelector((state) => { return { - cmBody: state.comments[id] + cmBody: state.comments[id], + ownUsername: state.user.username, }; }); } @@ -22,17 +23,18 @@ export function getPostBody(id) { useEffect(() => void dispatch(getSinglePostBody(id)), [dispatch, id]); return useSelector((state) => { return { - psBody: state.posts[id] + psBody: state.posts[id], + ownUsername: state.user.username, }; }); } export function SingleComment({id = null}) { if (id) { - const { cmBody } = getCommentBody(id); + const { cmBody, ownUsername } = getCommentBody(id); var commentBody = {}; Object.assign(commentBody, cmBody); - return
{commentBody.body?.replace(/@[a-zA-Z0-9_]+/g, '')}
+ return
{commentBody.body?.replace('@' + ownUsername, '')}
} else { return null } @@ -40,10 +42,10 @@ export function SingleComment({id = null}) { export function SinglePost({id = null}) { if (id) { - const { psBody } = getPostBody(id); + const { psBody, ownUsername } = getPostBody(id); var postBody = {}; Object.assign(postBody, psBody); - return
{postBody.body?.replace(/@[a-zA-Z0-9_]+/g, '')}
+ return
{postBody.body?.replace('@' + ownUsername, '')}
} else { return null } From 8d28281fdee36978a6a40af17a193fdd640e6b1f Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Fri, 26 Aug 2022 18:26:44 +0430 Subject: [PATCH 08/23] revert back to shows mentioned users in text and parse it with peaceoftext --- src/components/notification-body.jsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx index 3b2ebaac0..d145c8326 100644 --- a/src/components/notification-body.jsx +++ b/src/components/notification-body.jsx @@ -1,5 +1,6 @@ import { useEffect } from 'react'; import { useDispatch, useSelector} from 'react-redux'; +import PieceOfText from './piece-of-text'; import { getSinglePostBody, getSingleComment} from '../redux/action-creators'; @@ -13,7 +14,6 @@ export function getCommentBody(id) { return useSelector((state) => { return { cmBody: state.comments[id], - ownUsername: state.user.username, }; }); } @@ -24,17 +24,16 @@ export function getPostBody(id) { return useSelector((state) => { return { psBody: state.posts[id], - ownUsername: state.user.username, }; }); } export function SingleComment({id = null}) { if (id) { - const { cmBody, ownUsername } = getCommentBody(id); + const { cmBody } = getCommentBody(id); var commentBody = {}; Object.assign(commentBody, cmBody); - return
{commentBody.body?.replace('@' + ownUsername, '')}
+ return
} else { return null } @@ -42,11 +41,11 @@ export function SingleComment({id = null}) { export function SinglePost({id = null}) { if (id) { - const { psBody, ownUsername } = getPostBody(id); + const { psBody } = getPostBody(id); var postBody = {}; Object.assign(postBody, psBody); - return
{postBody.body?.replace('@' + ownUsername, '')}
+ return
} else { return null } -} +} \ No newline at end of file From 068b90e5207c38c3a8858846302fdc31ea2338d9 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Fri, 26 Aug 2022 18:55:51 +0430 Subject: [PATCH 09/23] yarn lint --- src/components/notification-body.jsx | 43 ++++++++++++++++------------ src/components/notifications.jsx | 30 +++++++------------ src/redux/action-creators.js | 2 +- src/redux/action-types.js | 4 +-- src/services/api.js | 2 -- 5 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx index d145c8326..22526d018 100644 --- a/src/components/notification-body.jsx +++ b/src/components/notification-body.jsx @@ -1,12 +1,11 @@ import { useEffect } from 'react'; -import { useDispatch, useSelector} from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import { getSinglePostBody, getSingleComment } from '../redux/action-creators'; import PieceOfText from './piece-of-text'; -import { getSinglePostBody, getSingleComment} from '../redux/action-creators'; - /** * @param {string} id -*/ + */ export function getCommentBody(id) { const dispatch = useDispatch(); @@ -14,8 +13,8 @@ export function getCommentBody(id) { return useSelector((state) => { return { cmBody: state.comments[id], - }; - }); + }; + }); } export function getPostBody(id) { @@ -24,28 +23,34 @@ export function getPostBody(id) { return useSelector((state) => { return { psBody: state.posts[id], - }; + }; }); } -export function SingleComment({id = null}) { +export function SingleComment({ id = null }) { if (id) { const { cmBody } = getCommentBody(id); - var commentBody = {}; + const commentBody = {}; Object.assign(commentBody, cmBody); - return
- } else { - return null - } + return ( +
+ +
+ ); + } + return null; } -export function SinglePost({id = null}) { +export function SinglePost({ id = null }) { if (id) { const { psBody } = getPostBody(id); - var postBody = {}; + const postBody = {}; Object.assign(postBody, psBody); - return
- } else { - return null + return ( +
+ +
+ ); } -} \ No newline at end of file + return null; +} diff --git a/src/components/notifications.jsx b/src/components/notifications.jsx index d4a562283..c64350a1a 100644 --- a/src/components/notifications.jsx +++ b/src/components/notifications.jsx @@ -2,9 +2,7 @@ import { useMemo } from 'react'; import { connect, useSelector } from 'react-redux'; import { Link } from 'react-router'; -import { - faClock, -} from '@fortawesome/free-solid-svg-icons'; +import { faClock } from '@fortawesome/free-solid-svg-icons'; import { Throbber } from './throbber'; import Linkify from './linkify'; @@ -88,8 +86,7 @@ const notificationTemplates = { ), mention_comment_to: (event) => (
- {' '} - {commentLink(event, 'replied')} + {commentLink(event, 'replied')} {` to you in the `} {postLink(event)} {` ${event.group.username ? ` [in @${event.group.username}]` : ''}`} @@ -263,8 +260,7 @@ const notificationTemplates = { ), group_subscription_rejected: (event) => (
- {'Your request to join group '}{' '} - {' was rejected'} + {'Your request to join group '} {' was rejected'}
), group_subscribed: (event) => ( @@ -291,8 +287,7 @@ const notificationTemplates = { comment_moderated: (event) => (
- {' '} - {' has deleted your comment to the '} + {' has deleted your comment to the '} {postLink(event)} {event.group_id ? ( @@ -304,10 +299,8 @@ const notificationTemplates = { ), comment_moderated_by_another_admin: (event) => (
- {' '} - {' has removed a comment from '}{' '} - {' '} - {' to the '} + {' has removed a comment from '}{' '} + {' to the '} {postLink(event)} {' in the group '} @@ -315,8 +308,7 @@ const notificationTemplates = { ), post_moderated: (event) => (
- {' '} - {' has removed your '} + {' has removed your '} {event.post_id ? postLink(event) : 'post'} {event.group_id ? ( @@ -328,8 +320,7 @@ const notificationTemplates = { ), post_moderated_by_another_admin: (event) => (
- {' '} - {' has removed the '} + {' has removed the '} {event.post_id ? postLink(event) : 'post'} {' from '} @@ -386,11 +377,10 @@ const Notification = ({ event_type, ...props }) => {
-
{(notificationTemplates[event_type] || nop)(props)}
+
{(notificationTemplates[event_type] || nop)(props)}
- {' '} - +
); diff --git a/src/redux/action-creators.js b/src/redux/action-creators.js index 061513613..267f2fefb 100644 --- a/src/redux/action-creators.js +++ b/src/redux/action-creators.js @@ -1222,7 +1222,7 @@ export function getSingleComment(commentId) { type: ActionTypes.GET_SINGLE_COMMENT, apiRequest: Api.getSingleComment, nonAuthRequest: true, - payload: { commentId } + payload: { commentId }, }; } diff --git a/src/redux/action-types.js b/src/redux/action-types.js index c3a398b63..a3bcf415b 100644 --- a/src/redux/action-types.js +++ b/src/redux/action-types.js @@ -66,8 +66,8 @@ export const TOGGLE_HIDDEN_POSTS = 'TOGGLE_HIDDEN_POSTS'; export const SUBSCRIBERS = 'SUBSCRIBERS'; export const SUBSCRIPTIONS = 'SUBSCRIPTIONS'; export const GET_USER_INFO = 'GET_USER_INFO'; -export const GET_SINGLE_COMMENT= 'GET_SINGLE_COMMENT'; -export const GET_SINGLE_POST_BODY= 'GET_SINGLE_POST_BODY'; +export const GET_SINGLE_COMMENT = 'GET_SINGLE_COMMENT'; +export const GET_SINGLE_POST_BODY = 'GET_SINGLE_POST_BODY'; export const CREATE_GROUP = 'CREATE_GROUP'; export const UPDATE_GROUP = 'UPDATE_GROUP'; export const UPDATE_GROUP_PICTURE = 'UPDATE_GROUP_PICTURE'; diff --git a/src/services/api.js b/src/services/api.js index bd0293a78..f8d60fde3 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -366,8 +366,6 @@ export function getUserInfo({ username }) { return fetch(`${apiRoot}/v1/users/${username}`, getRequestOptions()); } - - export function createGroup(groupSettings) { return fetch(`${apiRoot}/v1/groups`, postRequestOptions('POST', { group: groupSettings })); } From 66f14c5d6d2a8fe3fa3b480a1ae48197b4478efe Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Fri, 26 Aug 2022 19:57:36 +0430 Subject: [PATCH 10/23] remove clock icon --- src/components/fontawesome-icons.jsx | 2 -- src/components/notifications.jsx | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/fontawesome-icons.jsx b/src/components/fontawesome-icons.jsx index 9cf1d3b7d..fe68da783 100644 --- a/src/components/fontawesome-icons.jsx +++ b/src/components/fontawesome-icons.jsx @@ -6,7 +6,6 @@ import { faGlobeAmericas, faUserFriends, faPaperclip, - faClock, } from '@fortawesome/free-solid-svg-icons'; import { faComment, faHeart as faHeartO, faImage } from '@fortawesome/free-regular-svg-icons'; import { faCommentPlus } from './fontawesome-custom-icons'; @@ -25,7 +24,6 @@ const preloadedIcons = [ faUserFriends, faImage, faPaperclip, - faClock, ]; export const SVGSymbolDeclarations = memo(function SVGSymbolDeclarations() { diff --git a/src/components/notifications.jsx b/src/components/notifications.jsx index c64350a1a..57223c6a6 100644 --- a/src/components/notifications.jsx +++ b/src/components/notifications.jsx @@ -2,7 +2,6 @@ import { useMemo } from 'react'; import { connect, useSelector } from 'react-redux'; import { Link } from 'react-router'; -import { faClock } from '@fortawesome/free-solid-svg-icons'; import { Throbber } from './throbber'; import Linkify from './linkify'; @@ -11,7 +10,6 @@ import PaginatedView from './paginated-view'; import ErrorBoundary from './error-boundary'; import { UserPicture } from './user-picture'; import UserName from './user-name'; -import { Icon } from './fontawesome-icons'; import { SingleComment, SinglePost } from './notification-body'; import { SignInLink } from './sign-in-link'; @@ -380,7 +378,7 @@ const Notification = ({ event_type, ...props }) => {
{(notificationTemplates[event_type] || nop)(props)}
- +
); From 64241c5f200b23da846042961690492238a2f72a Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Fri, 26 Aug 2022 20:47:19 +0430 Subject: [PATCH 11/23] improve spacing between lines --- src/components/notification-body.jsx | 12 ++++++------ styles/shared/post.scss | 13 +++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx index 22526d018..cfc09dc48 100644 --- a/src/components/notification-body.jsx +++ b/src/components/notification-body.jsx @@ -7,7 +7,7 @@ import PieceOfText from './piece-of-text'; * @param {string} id */ -export function getCommentBody(id) { +export function GetCommentBody(id) { const dispatch = useDispatch(); useEffect(() => void dispatch(getSingleComment(id)), [dispatch, id]); return useSelector((state) => { @@ -17,7 +17,7 @@ export function getCommentBody(id) { }); } -export function getPostBody(id) { +export function GetPostBody(id) { const dispatch = useDispatch(); useEffect(() => void dispatch(getSinglePostBody(id)), [dispatch, id]); return useSelector((state) => { @@ -29,11 +29,11 @@ export function getPostBody(id) { export function SingleComment({ id = null }) { if (id) { - const { cmBody } = getCommentBody(id); + const { cmBody } = GetCommentBody(id); const commentBody = {}; Object.assign(commentBody, cmBody); return ( -
+
); @@ -43,11 +43,11 @@ export function SingleComment({ id = null }) { export function SinglePost({ id = null }) { if (id) { - const { psBody } = getPostBody(id); + const { psBody } = GetPostBody(id); const postBody = {}; Object.assign(postBody, psBody); return ( -
+
); diff --git a/styles/shared/post.scss b/styles/shared/post.scss index fcc08e9d7..f3650b5b1 100644 --- a/styles/shared/post.scss +++ b/styles/shared/post.scss @@ -19,6 +19,7 @@ $post-line-height: rem(20px); line-height: rem(19px); position: relative; min-height: 80px; + --bg-color: white; .dark-theme & { @@ -39,6 +40,7 @@ $post-line-height: rem(20px); .post-userpic { float: left; + @media (max-width: 767px) { margin: 0 rem(10px) 3px 0; } @@ -57,14 +59,11 @@ $post-line-height: rem(20px); .post-recipient { font-weight: 500; } - .post-notif { - margin-top: rem(5px); - margin-bottom: rem(5px); - } } .post-body { margin-left: 63px; + @media (max-width: 767px) { margin-left: 0; } @@ -76,6 +75,7 @@ $post-line-height: rem(20px); .post-text { margin-bottom: rem(8px); word-wrap: break-word; + @media (max-width: 767px) { min-height: rem(31px); } @@ -146,8 +146,8 @@ $post-line-height: rem(20px); } } -.notif-time-date{ - margin-top: 12px; +.notif-time-date { + margin-top: rem(10px); } .post-footer-icon { @@ -398,6 +398,7 @@ $post-line-height: rem(20px); .post-body { margin-left: 88px; + @media (max-width: 767px) { margin-left: 0; } From 2c2a89c270c43151b12fa7468b6ff73a4f3c04dc Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Fri, 26 Aug 2022 22:04:54 +0430 Subject: [PATCH 12/23] fix showing text & profile picture for mobile devices --- src/components/notification-body.jsx | 4 ++-- styles/shared/post.scss | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/notification-body.jsx b/src/components/notification-body.jsx index cfc09dc48..25689a6fc 100644 --- a/src/components/notification-body.jsx +++ b/src/components/notification-body.jsx @@ -33,7 +33,7 @@ export function SingleComment({ id = null }) { const commentBody = {}; Object.assign(commentBody, cmBody); return ( -
+
); @@ -47,7 +47,7 @@ export function SinglePost({ id = null }) { const postBody = {}; Object.assign(postBody, psBody); return ( -
+
); diff --git a/styles/shared/post.scss b/styles/shared/post.scss index f3650b5b1..31ad030b0 100644 --- a/styles/shared/post.scss +++ b/styles/shared/post.scss @@ -59,6 +59,12 @@ $post-line-height: rem(20px); .post-recipient { font-weight: 500; } + + .notif-body { + font-size: rem(14px); + margin-top: rem(5px); + position: relative; + } } .post-body { From f3fe2debf0d3eeb5eb3f4f03193663fce5ad6169 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Sat, 27 Aug 2022 00:53:09 +0430 Subject: [PATCH 13/23] improve spacing between lines --- styles/shared/post.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/shared/post.scss b/styles/shared/post.scss index 31ad030b0..9dd017bcf 100644 --- a/styles/shared/post.scss +++ b/styles/shared/post.scss @@ -62,7 +62,7 @@ $post-line-height: rem(20px); .notif-body { font-size: rem(14px); - margin-top: rem(5px); + margin-top: rem(10px); position: relative; } } From b5bf47e39f6025d354f5747f326bd629d3fe8739 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Sat, 27 Aug 2022 17:17:56 +0430 Subject: [PATCH 14/23] fix url for vercel --- vercel.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 vercel.json diff --git a/vercel.json b/vercel.json new file mode 100644 index 000000000..30e5a87b1 --- /dev/null +++ b/vercel.json @@ -0,0 +1,3 @@ +{ + "routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }] +} \ No newline at end of file From c42ad5f375d4853a73455cede6a8824bbed7d086 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Sat, 27 Aug 2022 18:17:22 +0430 Subject: [PATCH 15/23] add IG reel preview --- src/components/link-preview/instagram.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/link-preview/instagram.jsx b/src/components/link-preview/instagram.jsx index 490db3d6a..b75524e37 100644 --- a/src/components/link-preview/instagram.jsx +++ b/src/components/link-preview/instagram.jsx @@ -4,7 +4,7 @@ import _ from 'lodash'; import * as aspectRatio from './helpers/size-cache'; import FoldableContent from './helpers/foldable-content'; -const INSTAGRAM_RE = /^https?:\/\/(?:www\.)?instagram\.com\/(?:p|tv)\/([\w-]+)/i; +const INSTAGRAM_RE = /^https?:\/\/(?:www\.)?instagram\.com\/(?:p|tv|reel)\/([\w-]+)/i; export function canShowURL(url) { return INSTAGRAM_RE.test(url); From 1a4e706b8159e56bb4d88e997bf6d2af4ff7043a Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Sun, 28 Aug 2022 02:10:05 +0430 Subject: [PATCH 16/23] revert to default config --- config/default.js | 2 +- vercel.json | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 vercel.json diff --git a/config/default.js b/config/default.js index 47d81b636..e10a78296 100644 --- a/config/default.js +++ b/config/default.js @@ -6,7 +6,7 @@ const DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24; export default { api: { - root: 'https://freefeed.net', + root: 'https://candy.freefeed.net', }, siteTitle: 'FreeFeed', diff --git a/vercel.json b/vercel.json deleted file mode 100644 index 30e5a87b1..000000000 --- a/vercel.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }] -} \ No newline at end of file From c846debf700d9a1b137270a8e701d5277b6914ed Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 1 Sep 2022 00:59:28 +0430 Subject: [PATCH 17/23] Play video attachments using inline video tag - using @n1313 fork --- src/components/post/post-attachment-video.jsx | 67 +++++++++++++++++++ src/components/post/post-attachments.jsx | 42 +++++++++++- styles/shared/attachments-edit.scss | 1 + styles/shared/attachments.scss | 20 ++++++ styles/shared/media-viewer.scss | 8 +-- 5 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 src/components/post/post-attachment-video.jsx diff --git a/src/components/post/post-attachment-video.jsx b/src/components/post/post-attachment-video.jsx new file mode 100644 index 000000000..e9f90ff7e --- /dev/null +++ b/src/components/post/post-attachment-video.jsx @@ -0,0 +1,67 @@ +import { PureComponent } from 'react'; +import { faFileVideo, faPlayCircle } from '@fortawesome/free-regular-svg-icons'; +import { faTimes } from '@fortawesome/free-solid-svg-icons'; + +import { formatFileSize } from '../../utils'; +import { ButtonLink } from '../button-link'; +import { Icon } from '../fontawesome-icons'; + +class VideoAttachment extends PureComponent { + state = { + isOpen: false, + }; + + handleClickOnRemoveAttachment = () => { + this.props.removeAttachment(this.props.id); + }; + + toggleOpen = () => { + this.setState({ isOpen: true }); + }; + + render() { + const { props } = this; + const { isOpen } = this.state; + const formattedFileSize = formatFileSize(props.fileSize); + + const title = `${props.fileName} (${formattedFileSize})`; + + return ( +
+ {isOpen ? ( +
+ +
+ ) : ( + + + + )} +
+ + + {title} + + + {props.isEditing && ( + + )} +
+
+ ); + } +} + +export default VideoAttachment; diff --git a/src/components/post/post-attachments.jsx b/src/components/post/post-attachments.jsx index 7955f10d1..bee47cf23 100644 --- a/src/components/post/post-attachments.jsx +++ b/src/components/post/post-attachments.jsx @@ -3,11 +3,33 @@ import ErrorBoundary from '../error-boundary'; import ImageAttachmentsContainer from './post-attachment-image-container'; import AudioAttachment from './post-attachment-audio'; import GeneralAttachment from './post-attachment-general'; +import VideoAttachment from './post-attachment-video'; + +const looksLikeAVideoFile = (attachment) => { + const lowercaseFileName = attachment.fileName.toLowerCase(); + return lowercaseFileName.endsWith('.mov') || lowercaseFileName.endsWith('.mp4'); +}; export default (props) => { const attachments = props.attachments || []; - const imageAttachments = attachments.filter((attachment) => attachment.mediaType === 'image'); + const imageAttachments = []; + const audioAttachments = []; + const videoAttachments = []; + const generalAttachments = []; + + attachments.forEach((attachment) => { + if (attachment.mediaType === 'image') { + imageAttachments.push(attachment); + } else if (attachment.mediaType === 'audio') { + audioAttachments.push(attachment); + } else if (attachment.mediaType === 'general' && looksLikeAVideoFile(attachment)) { + videoAttachments.push(attachment); + } else { + generalAttachments.push(attachment); + } + }); + const imageAttachmentsContainer = imageAttachments.length > 0 ? ( { false ); - const audioAttachments = attachments.filter((attachment) => attachment.mediaType === 'audio'); const audioAttachmentsNodes = audioAttachments.map((attachment) => ( { false ); - const generalAttachments = attachments.filter((attachment) => attachment.mediaType === 'general'); + const videoAttachmentsNodes = videoAttachments.map((attachment) => ( + + )); + const videoAttachmentsContainer = + videoAttachments.length > 0 ? ( +
{videoAttachmentsNodes}
+ ) : ( + false + ); + const generalAttachmentsNodes = generalAttachments.map((attachment) => ( { {imageAttachmentsContainer} {audioAttachmentsContainer} + {videoAttachmentsContainer} {generalAttachmentsContainer}
diff --git a/styles/shared/attachments-edit.scss b/styles/shared/attachments-edit.scss index 928f213eb..5585706d9 100644 --- a/styles/shared/attachments-edit.scss +++ b/styles/shared/attachments-edit.scss @@ -74,6 +74,7 @@ } .audio-attachments, +.video-attachments, .general-attachments { .attachment:hover .remove-attachment { display: block; diff --git a/styles/shared/attachments.scss b/styles/shared/attachments.scss index 6c88f5739..6491655c0 100644 --- a/styles/shared/attachments.scss +++ b/styles/shared/attachments.scss @@ -131,6 +131,7 @@ } .audio-attachments, +.video-attachments, .general-attachments { .attachment { position: relative; @@ -148,3 +149,22 @@ } } } + +.video-attachments { + .video-attachment-click-to-play { + display: flex; + width: 3em; + height: 3em; + justify-content: center; + align-items: center; + border-radius: 2px; + background-color: #eee; + font-size: 2em; + } + + video { + max-width: 100%; + max-height: 400px; + background-color: #eee; + } +} diff --git a/styles/shared/media-viewer.scss b/styles/shared/media-viewer.scss index ec6a2d82f..7276f66b3 100644 --- a/styles/shared/media-viewer.scss +++ b/styles/shared/media-viewer.scss @@ -39,11 +39,11 @@ width: 100%; height: 100%; } -} -video { - width: 100% !important; - height: auto !important; + video { + width: 100% !important; + height: auto !important; + } } .media-link { From 3f136f5b3c7d2339172a1f378157ecd639d04f9b Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 1 Sep 2022 03:21:18 +0430 Subject: [PATCH 18/23] Revert "Play video attachments using inline video tag - using @n1313 fork" This reverts commit c846debf700d9a1b137270a8e701d5277b6914ed. --- src/components/post/post-attachment-video.jsx | 67 ------------------- src/components/post/post-attachments.jsx | 42 +----------- styles/shared/attachments-edit.scss | 1 - styles/shared/attachments.scss | 20 ------ styles/shared/media-viewer.scss | 8 +-- 5 files changed, 7 insertions(+), 131 deletions(-) delete mode 100644 src/components/post/post-attachment-video.jsx diff --git a/src/components/post/post-attachment-video.jsx b/src/components/post/post-attachment-video.jsx deleted file mode 100644 index e9f90ff7e..000000000 --- a/src/components/post/post-attachment-video.jsx +++ /dev/null @@ -1,67 +0,0 @@ -import { PureComponent } from 'react'; -import { faFileVideo, faPlayCircle } from '@fortawesome/free-regular-svg-icons'; -import { faTimes } from '@fortawesome/free-solid-svg-icons'; - -import { formatFileSize } from '../../utils'; -import { ButtonLink } from '../button-link'; -import { Icon } from '../fontawesome-icons'; - -class VideoAttachment extends PureComponent { - state = { - isOpen: false, - }; - - handleClickOnRemoveAttachment = () => { - this.props.removeAttachment(this.props.id); - }; - - toggleOpen = () => { - this.setState({ isOpen: true }); - }; - - render() { - const { props } = this; - const { isOpen } = this.state; - const formattedFileSize = formatFileSize(props.fileSize); - - const title = `${props.fileName} (${formattedFileSize})`; - - return ( -
- {isOpen ? ( -
- -
- ) : ( - - - - )} -
- - - {title} - - - {props.isEditing && ( - - )} -
-
- ); - } -} - -export default VideoAttachment; diff --git a/src/components/post/post-attachments.jsx b/src/components/post/post-attachments.jsx index bee47cf23..7955f10d1 100644 --- a/src/components/post/post-attachments.jsx +++ b/src/components/post/post-attachments.jsx @@ -3,33 +3,11 @@ import ErrorBoundary from '../error-boundary'; import ImageAttachmentsContainer from './post-attachment-image-container'; import AudioAttachment from './post-attachment-audio'; import GeneralAttachment from './post-attachment-general'; -import VideoAttachment from './post-attachment-video'; - -const looksLikeAVideoFile = (attachment) => { - const lowercaseFileName = attachment.fileName.toLowerCase(); - return lowercaseFileName.endsWith('.mov') || lowercaseFileName.endsWith('.mp4'); -}; export default (props) => { const attachments = props.attachments || []; - const imageAttachments = []; - const audioAttachments = []; - const videoAttachments = []; - const generalAttachments = []; - - attachments.forEach((attachment) => { - if (attachment.mediaType === 'image') { - imageAttachments.push(attachment); - } else if (attachment.mediaType === 'audio') { - audioAttachments.push(attachment); - } else if (attachment.mediaType === 'general' && looksLikeAVideoFile(attachment)) { - videoAttachments.push(attachment); - } else { - generalAttachments.push(attachment); - } - }); - + const imageAttachments = attachments.filter((attachment) => attachment.mediaType === 'image'); const imageAttachmentsContainer = imageAttachments.length > 0 ? ( { false ); + const audioAttachments = attachments.filter((attachment) => attachment.mediaType === 'audio'); const audioAttachmentsNodes = audioAttachments.map((attachment) => ( { false ); - const videoAttachmentsNodes = videoAttachments.map((attachment) => ( - - )); - const videoAttachmentsContainer = - videoAttachments.length > 0 ? ( -
{videoAttachmentsNodes}
- ) : ( - false - ); - + const generalAttachments = attachments.filter((attachment) => attachment.mediaType === 'general'); const generalAttachmentsNodes = generalAttachments.map((attachment) => ( { {imageAttachmentsContainer} {audioAttachmentsContainer} - {videoAttachmentsContainer} {generalAttachmentsContainer}
diff --git a/styles/shared/attachments-edit.scss b/styles/shared/attachments-edit.scss index 5585706d9..928f213eb 100644 --- a/styles/shared/attachments-edit.scss +++ b/styles/shared/attachments-edit.scss @@ -74,7 +74,6 @@ } .audio-attachments, -.video-attachments, .general-attachments { .attachment:hover .remove-attachment { display: block; diff --git a/styles/shared/attachments.scss b/styles/shared/attachments.scss index 6491655c0..6c88f5739 100644 --- a/styles/shared/attachments.scss +++ b/styles/shared/attachments.scss @@ -131,7 +131,6 @@ } .audio-attachments, -.video-attachments, .general-attachments { .attachment { position: relative; @@ -149,22 +148,3 @@ } } } - -.video-attachments { - .video-attachment-click-to-play { - display: flex; - width: 3em; - height: 3em; - justify-content: center; - align-items: center; - border-radius: 2px; - background-color: #eee; - font-size: 2em; - } - - video { - max-width: 100%; - max-height: 400px; - background-color: #eee; - } -} diff --git a/styles/shared/media-viewer.scss b/styles/shared/media-viewer.scss index 7276f66b3..ec6a2d82f 100644 --- a/styles/shared/media-viewer.scss +++ b/styles/shared/media-viewer.scss @@ -39,11 +39,11 @@ width: 100%; height: 100%; } +} - video { - width: 100% !important; - height: auto !important; - } +video { + width: 100% !important; + height: auto !important; } .media-link { From b7345fb769de0368ee57d813436b627b18363fd8 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 1 Sep 2022 03:56:39 +0430 Subject: [PATCH 19/23] Play video attachments using inline video tag - using @n1313 fork --- src/components/post/post-attachment-video.jsx | 41 ++++++++++++++++++ src/components/post/post-attachments.jsx | 42 +++++++++++++++++-- styles/shared/attachments-edit.scss | 1 + styles/shared/attachments.scss | 9 ++++ styles/shared/media-viewer.scss | 8 ++-- 5 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 src/components/post/post-attachment-video.jsx diff --git a/src/components/post/post-attachment-video.jsx b/src/components/post/post-attachment-video.jsx new file mode 100644 index 000000000..1b3565740 --- /dev/null +++ b/src/components/post/post-attachment-video.jsx @@ -0,0 +1,41 @@ +import { PureComponent } from 'react'; +import { faTimes } from '@fortawesome/free-solid-svg-icons'; + +import { formatFileSize } from '../../utils'; +import { Icon } from '../fontawesome-icons'; + +class VideoAttachment extends PureComponent { + handleClickOnRemoveAttachment = () => { + this.props.removeAttachment(this.props.id); + }; + + render() { + const { props } = this; + const formattedFileSize = formatFileSize(props.fileSize); + + const title = `${props.fileName} (${formattedFileSize})`; + + return ( +
+
+ +
+
+ {props.isEditing && ( + + )} +
+
+ ); + } +} + +export default VideoAttachment; diff --git a/src/components/post/post-attachments.jsx b/src/components/post/post-attachments.jsx index 7955f10d1..bee47cf23 100644 --- a/src/components/post/post-attachments.jsx +++ b/src/components/post/post-attachments.jsx @@ -3,11 +3,33 @@ import ErrorBoundary from '../error-boundary'; import ImageAttachmentsContainer from './post-attachment-image-container'; import AudioAttachment from './post-attachment-audio'; import GeneralAttachment from './post-attachment-general'; +import VideoAttachment from './post-attachment-video'; + +const looksLikeAVideoFile = (attachment) => { + const lowercaseFileName = attachment.fileName.toLowerCase(); + return lowercaseFileName.endsWith('.mov') || lowercaseFileName.endsWith('.mp4'); +}; export default (props) => { const attachments = props.attachments || []; - const imageAttachments = attachments.filter((attachment) => attachment.mediaType === 'image'); + const imageAttachments = []; + const audioAttachments = []; + const videoAttachments = []; + const generalAttachments = []; + + attachments.forEach((attachment) => { + if (attachment.mediaType === 'image') { + imageAttachments.push(attachment); + } else if (attachment.mediaType === 'audio') { + audioAttachments.push(attachment); + } else if (attachment.mediaType === 'general' && looksLikeAVideoFile(attachment)) { + videoAttachments.push(attachment); + } else { + generalAttachments.push(attachment); + } + }); + const imageAttachmentsContainer = imageAttachments.length > 0 ? ( { false ); - const audioAttachments = attachments.filter((attachment) => attachment.mediaType === 'audio'); const audioAttachmentsNodes = audioAttachments.map((attachment) => ( { false ); - const generalAttachments = attachments.filter((attachment) => attachment.mediaType === 'general'); + const videoAttachmentsNodes = videoAttachments.map((attachment) => ( + + )); + const videoAttachmentsContainer = + videoAttachments.length > 0 ? ( +
{videoAttachmentsNodes}
+ ) : ( + false + ); + const generalAttachmentsNodes = generalAttachments.map((attachment) => ( { {imageAttachmentsContainer} {audioAttachmentsContainer} + {videoAttachmentsContainer} {generalAttachmentsContainer}
diff --git a/styles/shared/attachments-edit.scss b/styles/shared/attachments-edit.scss index 928f213eb..5585706d9 100644 --- a/styles/shared/attachments-edit.scss +++ b/styles/shared/attachments-edit.scss @@ -74,6 +74,7 @@ } .audio-attachments, +.video-attachments, .general-attachments { .attachment:hover .remove-attachment { display: block; diff --git a/styles/shared/attachments.scss b/styles/shared/attachments.scss index 6c88f5739..09528ebb8 100644 --- a/styles/shared/attachments.scss +++ b/styles/shared/attachments.scss @@ -131,6 +131,7 @@ } .audio-attachments, +.video-attachments, .general-attachments { .attachment { position: relative; @@ -148,3 +149,11 @@ } } } + +.video-attachments { + video { + max-width: 100%; + max-height: 400px; + background-color: #eee; + } +} diff --git a/styles/shared/media-viewer.scss b/styles/shared/media-viewer.scss index ec6a2d82f..7276f66b3 100644 --- a/styles/shared/media-viewer.scss +++ b/styles/shared/media-viewer.scss @@ -39,11 +39,11 @@ width: 100%; height: 100%; } -} -video { - width: 100% !important; - height: auto !important; + video { + width: 100% !important; + height: auto !important; + } } .media-link { From ab2fe3f4de22d394aa96d4a6881a4d88358bb79e Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Thu, 1 Sep 2022 04:11:55 +0430 Subject: [PATCH 20/23] add .webm & .ogg to inline video player --- src/components/post/post-attachments.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/post/post-attachments.jsx b/src/components/post/post-attachments.jsx index bee47cf23..1deceb856 100644 --- a/src/components/post/post-attachments.jsx +++ b/src/components/post/post-attachments.jsx @@ -7,7 +7,12 @@ import VideoAttachment from './post-attachment-video'; const looksLikeAVideoFile = (attachment) => { const lowercaseFileName = attachment.fileName.toLowerCase(); - return lowercaseFileName.endsWith('.mov') || lowercaseFileName.endsWith('.mp4'); + return ( + lowercaseFileName.endsWith('.mov') || + lowercaseFileName.endsWith('.mp4') || + lowercaseFileName.endsWith('.webm') || + lowercaseFileName.endsWith('.ogg') + ); }; export default (props) => { From 9934fb59e588ffa823afcd82876472fe56d4742f Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Tue, 6 Sep 2022 01:19:18 +0430 Subject: [PATCH 21/23] stick navigation bar to the bottom of screen stick navigation bar to the bottom of screen for mobile devices with new icons and fix both them for navigation bar --- src/components/fontawesome-custom-icons.js | 25 +++++++++++ src/components/fontawesome-icons.jsx | 4 ++ src/components/layout.jsx | 49 ++++++++++++++-------- styles/helvetica/base.scss | 2 +- styles/helvetica/dark-theme.scss | 17 ++++++++ styles/helvetica/sidebar.scss | 34 +++++++++++++++ 6 files changed, 112 insertions(+), 19 deletions(-) diff --git a/src/components/fontawesome-custom-icons.js b/src/components/fontawesome-custom-icons.js index ea0d651a5..525c0550b 100644 --- a/src/components/fontawesome-custom-icons.js +++ b/src/components/fontawesome-custom-icons.js @@ -2,6 +2,31 @@ * Combination of Font Awesome 5.0 'comment' (regular) and 'plus' (solid) icons. * https://github.com/FortAwesome/Font-Awesome/blob/master/LICENSE.txt */ + +export const faMessage = { + iconName: 'message', + prefix: 'fas', + icon: [ + 640, + 512, + [], + '', + 'M208 352c114.9 0 208-78.8 208-176S322.9 0 208 0S0 78.8 0 176c0 38.6 14.7 74.3 39.6 103.4c-3.5 9.4-8.7 17.7-14.2 24.7c-4.8 6.2-9.7 11-13.3 14.3c-1.8 1.6-3.3 2.9-4.3 3.7c-.5 .4-.9 .7-1.1 .8l-.2 .2 0 0 0 0C1 327.2-1.4 334.4 .8 340.9S9.1 352 16 352c21.8 0 43.8-5.6 62.1-12.5c9.2-3.5 17.8-7.4 25.3-11.4C134.1 343.3 169.8 352 208 352zM448 176c0 112.3-99.1 196.9-216.5 207C255.8 457.4 336.4 512 432 512c38.2 0 73.9-8.7 104.7-23.9c7.5 4 16 7.9 25.2 11.4c18.3 6.9 40.3 12.5 62.1 12.5c6.9 0 13.1-4.5 15.2-11.1c2.1-6.6-.2-13.8-5.8-17.9l0 0 0 0-.2-.2c-.2-.2-.6-.4-1.1-.8c-1-.8-2.5-2-4.3-3.7c-3.6-3.3-8.5-8.1-13.3-14.3c-5.5-7-10.7-15.4-14.2-24.7c24.9-29 39.6-64.7 39.6-103.4c0-92.8-84.9-168.9-192.6-175.5c.4 5.1 .6 10.3 .6 15.5z', + ], +}; + +export const faHouse = { + iconName: 'house', + prefix: 'fas', + icon: [ + 576, + 512, + [], + '', + 'M575.8 255.5c0 18-15 32.1-32 32.1h-32l.7 160.2c0 2.7-.2 5.4-.5 8.1V472c0 22.1-17.9 40-40 40H456c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1H416 392c-22.1 0-40-17.9-40-40V448 384c0-17.7-14.3-32-32-32H256c-17.7 0-32 14.3-32 32v64 24c0 22.1-17.9 40-40 40H160 128.1c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2H104c-22.1 0-40-17.9-40-40V360c0-.9 0-1.9 .1-2.8V287.6H32c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z', + ], +}; + export const faCommentPlus = { iconName: 'comment-plus', prefix: 'fas', diff --git a/src/components/fontawesome-icons.jsx b/src/components/fontawesome-icons.jsx index fe68da783..3c837e9b4 100644 --- a/src/components/fontawesome-icons.jsx +++ b/src/components/fontawesome-icons.jsx @@ -6,6 +6,8 @@ import { faGlobeAmericas, faUserFriends, faPaperclip, + faBell, + faUser, } from '@fortawesome/free-solid-svg-icons'; import { faComment, faHeart as faHeartO, faImage } from '@fortawesome/free-regular-svg-icons'; import { faCommentPlus } from './fontawesome-custom-icons'; @@ -24,6 +26,8 @@ const preloadedIcons = [ faUserFriends, faImage, faPaperclip, + faBell, + faUser, ]; export const SVGSymbolDeclarations = memo(function SVGSymbolDeclarations() { diff --git a/src/components/layout.jsx b/src/components/layout.jsx index f2406759f..f235d1e2c 100644 --- a/src/components/layout.jsx +++ b/src/components/layout.jsx @@ -3,11 +3,12 @@ import { Component, Suspense } from 'react'; import { connect } from 'react-redux'; import { Helmet } from 'react-helmet'; import classnames from 'classnames'; -import { faBug } from '@fortawesome/free-solid-svg-icons'; +import { faBug, faBell, faUser } from '@fortawesome/free-solid-svg-icons'; import { Link } from 'react-router'; import { signOut, home } from '../redux/action-creators'; import { getCurrentRouteName } from '../utils'; +import { faMessage, faHouse } from './fontawesome-custom-icons'; import Footer from './footer'; import Sidebar from './sidebar'; import LoaderContainer from './loader-container'; @@ -157,23 +158,35 @@ class Layout extends Component { {props.authenticated && (
-
- - Discussions - - - Notifications - {props.user.unreadNotificationsNumber > 0 && - !props.user.frontendPreferences.hideUnreadNotifications && - ` (${props.user.unreadNotificationsNumber})`} - - - Directs - {props.user.unreadDirectsNumber > 0 && ` (${props.user.unreadDirectsNumber})`} - - - My feed - +
+
+ + + +
+
+ + + + {props.user.unreadNotificationsNumber > 0 && + !props.user.frontendPreferences.hideUnreadNotifications && + ` (${props.user.unreadNotificationsNumber})`} + + +
+
+ + + + {props.user.unreadDirectsNumber > 0 && ` (${props.user.unreadDirectsNumber})`} + + +
+
+ + + +
)} diff --git a/styles/helvetica/base.scss b/styles/helvetica/base.scss index cfca1ce93..ae417ea5b 100644 --- a/styles/helvetica/base.scss +++ b/styles/helvetica/base.scss @@ -55,7 +55,7 @@ a[aria-disabled='true'] { .footer { font-size: rem(14px); - margin-bottom: rem(20px); + margin-bottom: rem(70px); margin-left: rem(10px); @media (max-width: 991px) { margin-left: 0; diff --git a/styles/helvetica/dark-theme.scss b/styles/helvetica/dark-theme.scss index d8ad3be11..22a4705c6 100644 --- a/styles/helvetica/dark-theme.scss +++ b/styles/helvetica/dark-theme.scss @@ -425,4 +425,21 @@ body, color: initial !important; } } + + .bottom-navbar { + background-color: #202020; + + a:link { + color: $link-color-dim; + } + + a:visited { + color: $link-color-dim; + } + + a:hover { + color: $link-color-dim; + } + } + } diff --git a/styles/helvetica/sidebar.scss b/styles/helvetica/sidebar.scss index e150dd7e1..dab88687a 100644 --- a/styles/helvetica/sidebar.scss +++ b/styles/helvetica/sidebar.scss @@ -144,3 +144,37 @@ -webkit-user-select: none; user-select: none; } + +.bottom-navbar { + background-color: #edf1f5; + position: fixed; + bottom: 0; + max-width: 750px; + width: 100%; + z-index: 3; + height: 50px; + + .bottom-navbar-row { + float: left; + width: 25%; + display: block; + text-align: center; + padding: 10px 10px; + font-size: rem(20px); + + a:link { + color: #54a5f0; + text-decoration: none; + } + + a:visited { + color: #54a5f0; + text-decoration: none; + } + + a:hover { + color: #54a5f0; + text-decoration: none; + } + } +} From 162ceda4253bce01c5092dcee140b0610da220ae Mon Sep 17 00:00:00 2001 From: Mohammad Jafari <66924476+MMDJafari@users.noreply.github.com> Date: Tue, 6 Sep 2022 01:21:29 +0430 Subject: [PATCH 22/23] add dark subscription request alert style --- styles/helvetica/dark-theme.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/styles/helvetica/dark-theme.scss b/styles/helvetica/dark-theme.scss index 22a4705c6..04e105681 100644 --- a/styles/helvetica/dark-theme.scss +++ b/styles/helvetica/dark-theme.scss @@ -442,4 +442,10 @@ body, } } + .subscriptions-request-alert { + border: 1px solid $text-color; + color: $text-color; + background-color: $bg-color-lighter; + ; + } } From f7ef932487debb602c46de2171845b7610477777 Mon Sep 17 00:00:00 2001 From: Mohammad Jafari Date: Wed, 14 Dec 2022 23:34:09 +0330 Subject: [PATCH 23/23] rebase everything --- CHANGELOG.md | 37 +- README.md | 2 + config/default.js | 2 +- package.json | 86 +- src/components/app-updated.jsx | 2 +- src/components/block-in-group-form.jsx | 42 + src/components/email-verification-subform.jsx | 70 + src/components/feed.jsx | 9 +- src/components/fontawesome-custom-icons.js | 25 - src/components/fontawesome-icons.jsx | 4 - src/components/footer.jsx | 2 +- src/components/layout.jsx | 49 +- src/components/link-preview/video.jsx | 10 +- src/components/manage-subscribers.jsx | 196 +- src/components/media-viewer.jsx | 25 +- src/components/notification-body.jsx | 56 - src/components/notifications.jsx | 260 +- src/components/post/post-attachment-video.jsx | 38 +- src/components/post/post-attachments.jsx | 33 +- src/components/post/post-comment.jsx | 2 +- .../post/post-comments/loading-comments.jsx | 2 +- src/components/post/post-hides-ui.jsx | 92 +- src/components/post/post.jsx | 20 +- src/components/select-utils.js | 17 +- src/components/settings/forms/appearance.jsx | 34 +- src/components/settings/forms/privacy.jsx | 11 +- src/components/settings/forms/profile.jsx | 61 +- src/components/signin.jsx | 3 +- src/components/signup-form.jsx | 33 +- src/components/signup.jsx | 5 +- src/components/tile-user-list.jsx | 100 +- src/components/user-profile-head.jsx | 9 +- src/components/user-profile.jsx | 2 + src/components/user.jsx | 8 +- src/redux/action-creators.js | 55 +- src/redux/action-helpers.js | 8 +- src/redux/action-types.js | 10 +- src/redux/async-helpers.js | 21 +- src/redux/reducers.js | 169 +- src/redux/reducers/posts.js | 11 - src/services/api.js | 81 +- src/utils/hide-criteria.js | 100 + src/utils/index.js | 3 + src/utils/norm-hashtags.js | 15 + styles/helvetica/base.scss | 2 +- styles/helvetica/dark-theme.scss | 23 - styles/helvetica/sidebar.scss | 34 - styles/shared/attachments.scss | 11 + styles/shared/comments.scss | 4 +- styles/shared/media-viewer.scss | 41 +- styles/shared/post.scss | 15 - test/jest-setup.js | 5 + .../__snapshots__/app-updated.test.js.snap | 2 +- .../post-attachments.test.js.snap | 186 + .../jest/__snapshots__/subs-list.test.js.snap | 1 - test/jest/post-attachments.test.js | 82 + test/jest/user-profile-head.test.js | 4 +- test/unit/redux/reducers/comments.js | 14 + test/unit/select-utils/join-post-data.js | 1 + test/unit/utils/norm-hashtags.js | 16 + webpack/rules.js | 17 +- yarn.lock | 3263 +++++++++-------- 62 files changed, 3320 insertions(+), 2221 deletions(-) create mode 100644 src/components/block-in-group-form.jsx create mode 100644 src/components/email-verification-subform.jsx delete mode 100644 src/components/notification-body.jsx create mode 100644 src/utils/hide-criteria.js create mode 100644 src/utils/norm-hashtags.js create mode 100644 test/jest/__snapshots__/post-attachments.test.js.snap create mode 100644 test/jest/post-attachments.test.js create mode 100644 test/unit/utils/norm-hashtags.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2542a60af..31896f7a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,42 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.110.0] - Not released +## [1.113] - Not released +### Added +- Email verification support. If verification is supported by server, the new + field (Verification code) is appear in Sign Up and Profile forms. When user + sets up or updates email address, they should receive verification code on it + and enter that code to the form. + +## [1.112] - 2022-11-25 +### Added +- Group administrators can now block users in their managed groups. A blocked + user cannot post to the group, but can read and comment if the group is not + private. +- Video attachments now have a player, when browser supports them +- YouTube shorts now supported in media viewer + +### Fixed +- No refresh needed to view private users and groups after subscription approved +- No refresh needed to interact with new subscription requests +- Hidden comment class name updated to avoid interference with Firefox builtin extension style + +## [1.111.2] - 2022-09-23 +### Fixed +- Fix broken PhotoSwipe icons + +## [1.111.1] - 2022-09-08 +### Fixed +- Restore Vazir font (new css-loader didn't load it in 1.111.0) + +## [1.111.0] - 2022-09-07 +### Added +- Instagram Reels are supported by native previews. + First contribution by [Mohammad Jafari](https://github.com/MMDJafari/). Thanks! +- It is now possible to hide posts by hashtags! Also, the underlying algorithm + allows to add other types of hiding criteria in the future. + +## [1.110.0] - 2022-06-29 ### Fixed - The erroneous "Remove from" items has been removed from the post's "More" menu - Fixed domain-name in donate link diff --git a/README.md b/README.md index 568447aa8..75253f245 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FFreeFeed%2Ffreefeed-react-client.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FFreeFeed%2Ffreefeed-react-client?ref=badge_shield) +[Node.js](https://nodejs.org) 14 or 16 is supported. + We use [yarn](https://yarnpkg.com/) as dependency manager (instead of npm) so you need to install it and run `yarn` after downloading this code. If you're using Windows, you should install developer tools by using `npm install --global --production windows-build-tools` from an elevated PowerShell or CMD.exe (run as Administrator). ## Starting Development Server with Hot-Reload diff --git a/config/default.js b/config/default.js index e10a78296..39efacdd6 100644 --- a/config/default.js +++ b/config/default.js @@ -70,7 +70,7 @@ export default { readMoreStyle: 'modern', homeFeedSort: ACTIVITY, homeFeedMode: HOMEFEED_MODE_CLASSIC, - homefeed: { hideUsers: [] }, + homefeed: { hideUsers: [], hideTags: [] }, hidesInNonHomeFeeds: false, pinnedGroups: [], hideUnreadNotifications: false, diff --git a/package.json b/package.json index 8fb487410..904b4bd2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reactive-pepyatka", - "version": "1.110.0", + "version": "1.113.0", "description": "", "main": "index.js", "dependencies": { @@ -9,9 +9,9 @@ "@fortawesome/free-solid-svg-icons": "~5.15.4", "@sentry/react": "~6.19.7", "autotrack": "~2.4.1", - "classnames": "~2.3.1", + "classnames": "~2.3.2", "custom-event": "~1.0.1", - "date-fns": "~2.28.0", + "date-fns": "~2.29.3", "debug": "~4.3.4", "filesize": "~8.0.7", "final-form": "~4.20.7", @@ -20,7 +20,7 @@ "keycode-js": "~3.1.0", "local-storage-fallback": "~4.1.2", "lodash": "~4.17.21", - "lru-cache": "~7.10.2", + "lru-cache": "~7.14.1", "memoize-one": "~6.0.0", "mousetrap": "~1.6.5", "porter-stemmer": "~0.9.1", @@ -33,40 +33,40 @@ "react-helmet": "~6.1.0", "react-photoswipe": "~1.3.0", "react-portal": "~4.2.2", - "react-redux": "~7.2.8", + "react-redux": "~7.2.9", "react-router": "~3.2.6", "react-router-redux": "~4.0.8", "react-select": "~1.2.1", "react-sortablejs": "~2.0.11", "react-textarea-autosize": "~8.3.4", - "recharts": "~2.1.12", + "recharts": "~2.1.16", "redux": "~4.1.2", "snarkdown": "~2.0.0", "social-text-tokenizer": "~2.2.0", "socket.io-client": "~2.3.1", "tabbable": "~5.2.1", - "ua-parser-js": "~1.0.2", + "ua-parser-js": "~1.0.32", "use-subscription": "~1.5.1", "validator": "~13.7.0", "vazir-font": "~30.1.0", "whatwg-fetch": "~3.6.2" }, "devDependencies": { - "@babel/core": "~7.18.6", - "@babel/eslint-parser": "~7.18.2", + "@babel/core": "~7.19.6", + "@babel/eslint-parser": "~7.19.1", "@babel/plugin-proposal-class-properties": "~7.18.6", "@babel/plugin-proposal-do-expressions": "~7.18.6", "@babel/plugin-syntax-class-properties": "~7.12.13", "@babel/plugin-transform-modules-commonjs": "~7.18.6", - "@babel/plugin-transform-react-constant-elements": "~7.18.6", + "@babel/plugin-transform-react-constant-elements": "~7.18.12", "@babel/plugin-transform-react-inline-elements": "~7.18.6", - "@babel/plugin-transform-runtime": "~7.18.6", - "@babel/preset-env": "~7.18.6", + "@babel/plugin-transform-runtime": "~7.19.6", + "@babel/preset-env": "~7.19.4", "@babel/preset-react": "~7.18.6", - "@babel/register": "~7.18.6", - "@babel/runtime": "~7.18.6", + "@babel/register": "~7.18.9", + "@babel/runtime": "~7.19.4", "@gfx/zopfli": "~1.0.15", - "@testing-library/jest-dom": "~5.16.4", + "@testing-library/jest-dom": "~5.16.5", "@testing-library/react": "~12.1.5", "@testing-library/react-hooks": "~7.0.2", "@testing-library/user-event": "~14.1.1", @@ -74,67 +74,67 @@ "babel-loader": "~8.2.5", "babel-plugin-lodash": "~3.3.4", "babel-plugin-transform-react-remove-prop-types": "~0.4.24", - "compression-webpack-plugin": "~9.2.0", - "copy-webpack-plugin": "~10.2.4", - "core-js": "~3.23.3", + "compression-webpack-plugin": "~10.0.0", + "copy-webpack-plugin": "~11.0.0", + "core-js": "~3.25.5", "cross-env": "~7.0.3", - "css-loader": "~5.2.7", - "css-minimizer-webpack-plugin": "~3.4.1", - "eslint": "~8.18.0", + "css-loader": "~6.7.2", + "css-minimizer-webpack-plugin": "~4.0.0", + "eslint": "~8.23.1", "eslint-config-prettier": "~8.5.0", "eslint-plugin-babel": "~5.3.1", "eslint-plugin-import": "~2.26.0", "eslint-plugin-lodash": "~7.4.0", - "eslint-plugin-prettier": "~4.1.0", - "eslint-plugin-promise": "~6.0.0", - "eslint-plugin-react": "~7.30.1", + "eslint-plugin-prettier": "~4.2.1", + "eslint-plugin-promise": "~6.0.1", + "eslint-plugin-react": "~7.31.11", "eslint-plugin-react-hooks": "~4.6.0", - "eslint-plugin-unicorn": "~42.0.0", + "eslint-plugin-unicorn": "~43.0.2", "eslint-plugin-you-dont-need-lodash-underscore": "~6.12.0", "eslint-webpack-plugin": "~3.2.0", "file-loader": "~6.2.0", "html-webpack-plugin": "~5.5.0", - "husky": "~8.0.1", + "husky": "~8.0.2", "identity-obj-proxy": "~3.0.0", "jest": "~27.5.1", - "jest-canvas-mock": "~2.3.1", + "jest-canvas-mock": "~2.4.0", "lint-staged": "~12.4.3", "mini-css-extract-plugin": "~2.6.1", - "mocha": "~9.2.2", + "mocha": "~10.0.0", "mochapack": "~2.1.4", "node-noop": "~1.0.0", - "node-sass": "~7.0.1", + "node-sass": "~7.0.3", "npm-run-all": "~4.1.5", "null-loader": "~4.0.1", "prettier": "~2.7.1", "pug": "~3.0.2", "pug-loader": "~2.4.0", "querystring": "~0.2.1", - "react-hot-loader": "~4.13.0", + "react-hot-loader": "~4.13.1", "react-markdown-loader": "~1.3.1", "react-test-renderer": "~17.0.2", - "regenerator-runtime": "~0.13.9", + "regenerator-runtime": "~0.13.11", "resolve-url-loader": "~5.0.0", "rimraf": "~3.0.2", - "sass-loader": "~12.6.0", + "sass-loader": "~13.0.2", "sinon": "~13.0.2", "style-loader": "~3.3.1", - "stylelint": "~14.9.1", - "stylelint-config-prettier": "~9.0.3", - "stylelint-config-standard-scss": "~3.0.0", + "stylelint": "~14.11.0", + "stylelint-config-prettier": "~9.0.4", + "stylelint-config-standard-scss": "~5.0.0", "stylelint-prettier": "~2.0.0", - "stylelint-scss": "~4.2.0", - "terser-webpack-plugin": "~5.3.3", - "unexpected": "~12.0.4", + "stylelint-scss": "~4.3.0", + "terser-webpack-plugin": "~5.3.6", + "unexpected": "~13.0.1", "unexpected-react": "~6.0.2", "unexpected-sinon": "~11.1.0", "url": "~0.11.0", - "webpack": "~5.73.0", - "webpack-bundle-analyzer": "~4.5.0", + "webpack": "~5.74.0", + "webpack-bundle-analyzer": "~4.6.1", "webpack-cli": "~4.10.0", - "webpack-dev-server": "~4.9.2", + "webpack-dev-server": "~4.11.1", "webpack-node-externals": "~3.0.0", - "webpack-version-file": "~0.1.6", + "webpack-version-file": "~0.1.7", "worker-loader": "~3.0.8" }, "resolutions": { @@ -167,5 +167,5 @@ "url": "https://github.com/FreeFeed/freefeed-react-client.git" }, "license": "MIT", - "packageManager": "yarn@3.2.1" + "packageManager": "yarn@3.3.0" } diff --git a/src/components/app-updated.jsx b/src/components/app-updated.jsx index c5e2dfa9f..0679e4495 100644 --- a/src/components/app-updated.jsx +++ b/src/components/app-updated.jsx @@ -18,7 +18,7 @@ export function AppUpdated() { return (
- There’s a new update for {CONFIG.siteTitle} available!{' '} + There’s an update for {CONFIG.siteTitle}!{' '} Refresh the page {' '} diff --git a/src/components/block-in-group-form.jsx b/src/components/block-in-group-form.jsx new file mode 100644 index 000000000..482aca6ab --- /dev/null +++ b/src/components/block-in-group-form.jsx @@ -0,0 +1,42 @@ +import { useCallback, useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { blockUserInGroup } from '../redux/action-creators'; + +export function BlockInGroupForm({ groupName }) { + const dispatch = useDispatch(); + const status = useSelector((state) => state.blockUserInGroupStatus); + + const [text, setText] = useState(''); + const canSubmit = !!text.trim() && !status.loading; + const onInput = useCallback((e) => setText(e.target.value), []); + const onSubmit = useCallback( + (e) => (e.preventDefault(), dispatch(blockUserInGroup(groupName, text.trim()))), + [dispatch, groupName, text], + ); + + useEffect(() => status.success && setText(''), [status.success]); + + return ( +
+

+ + + + +

+ {status.error && ( +

+ {status.errorText} +

+ )} +
+ ); +} diff --git a/src/components/email-verification-subform.jsx b/src/components/email-verification-subform.jsx new file mode 100644 index 000000000..0ffa07dcf --- /dev/null +++ b/src/components/email-verification-subform.jsx @@ -0,0 +1,70 @@ +import { useCallback, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { sendVerificationCode } from '../redux/action-creators'; +import { groupErrClass } from './form-utils'; +import { useServerInfo } from './hooks/server-info'; + +export function EmailVerificationSubform({ emailField, codeField, create = false }) { + const dispatch = useDispatch(); + const sendStatus = useSelector((state) => state.sendVerificationCodeStatus); + const [serverInfo, serverInfoStatus] = useServerInfo(); + + const verificationEnabled = serverInfoStatus.success && serverInfo.emailVerificationEnabled; + + const shouldVerify = Boolean( + verificationEnabled && emailField.meta.dirty && !emailField.meta.invalid, + ); + + const [lastSentTo, setLastSentTo] = useState(''); + + const sendEmailCode = useCallback(() => { + dispatch(sendVerificationCode(emailField.input.value, create ? 'sign-up' : 'update')); + setLastSentTo(emailField.input.value); + }, [create, dispatch, emailField.input.value]); + + return ( + shouldVerify && ( +
+ +

+ To confirm {create ? 'your' : 'updated'} email address, please click{' '} + {' '} + and enter the code that we will send to {emailField.input.value}. +

+ {sendStatus.loading && ( +

+ Sending code to {lastSentTo}... +

+ )} + {sendStatus.success && ( +

+ Code was sent to {lastSentTo}, please check your mailbox (and, + probably, the Spam folder) +

+ )} + {sendStatus.error && ( +

+ Error sending to {lastSentTo}: {sendStatus.errorText} +

+ )} +

+ +

+
+ ) + ); +} diff --git a/src/components/feed.jsx b/src/components/feed.jsx index 60403ee09..27cf5f193 100644 --- a/src/components/feed.jsx +++ b/src/components/feed.jsx @@ -109,7 +109,7 @@ class Feed extends PureComponent { } } -const postIsHidden = (post) => !!(post.isHidden || post.hiddenByNames); +const postIsHidden = (post) => !!(post.isHidden || post.hiddenByCriteria); export default connect( (state) => { @@ -151,14 +151,17 @@ function FeedEntry({ post, section, ...props }) { const onPostUnmount = useCallback((offset) => setHideLinkTopOffset(offset), []); const isRecentlyHidden = - props.separateHiddenEntries && (post.isHidden || post.hiddenByNames) && section === 'visible'; + props.separateHiddenEntries && + (post.isHidden || post.hiddenByCriteria) && + section === 'visible'; return isRecentlyHidden ? ( ) : (

- © FreeFeed 1.110.0 (Not released) + © FreeFeed 1.113.0 (Not released)
About {' | '} diff --git a/src/components/layout.jsx b/src/components/layout.jsx index f235d1e2c..f2406759f 100644 --- a/src/components/layout.jsx +++ b/src/components/layout.jsx @@ -3,12 +3,11 @@ import { Component, Suspense } from 'react'; import { connect } from 'react-redux'; import { Helmet } from 'react-helmet'; import classnames from 'classnames'; -import { faBug, faBell, faUser } from '@fortawesome/free-solid-svg-icons'; +import { faBug } from '@fortawesome/free-solid-svg-icons'; import { Link } from 'react-router'; import { signOut, home } from '../redux/action-creators'; import { getCurrentRouteName } from '../utils'; -import { faMessage, faHouse } from './fontawesome-custom-icons'; import Footer from './footer'; import Sidebar from './sidebar'; import LoaderContainer from './loader-container'; @@ -158,35 +157,23 @@ class Layout extends Component { {props.authenticated && (

-
-
- - - -
-
- - - - {props.user.unreadNotificationsNumber > 0 && - !props.user.frontendPreferences.hideUnreadNotifications && - ` (${props.user.unreadNotificationsNumber})`} - - -
-
- - - - {props.user.unreadDirectsNumber > 0 && ` (${props.user.unreadDirectsNumber})`} - - -
-
- - - -
+
+ + Discussions + + + Notifications + {props.user.unreadNotificationsNumber > 0 && + !props.user.frontendPreferences.hideUnreadNotifications && + ` (${props.user.unreadNotificationsNumber})`} + + + Directs + {props.user.unreadDirectsNumber > 0 && ` (${props.user.unreadDirectsNumber})`} + + + My feed +
)} diff --git a/src/components/link-preview/video.jsx b/src/components/link-preview/video.jsx index 8adef7ff6..a58704eea 100644 --- a/src/components/link-preview/video.jsx +++ b/src/components/link-preview/video.jsx @@ -10,7 +10,7 @@ import cachedFetch from './helpers/cached-fetch'; import * as aspectRatio from './helpers/size-cache'; const YOUTUBE_VIDEO_RE = - /^https?:\/\/(?:www\.|m\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?(?:v=|.+&v=)))([\w-]+)/i; + /^https?:\/\/(?:www\.|m\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|shorts\/|v\/|watch\?(?:v=|.+&v=)))([\w-]+)/i; const VIMEO_VIDEO_RE = /^https?:\/\/vimeo\.com\/(\d+)(?:\/([a-z\d]+))?/i; const COUB_VIDEO_RE = /^https?:\/\/coub\.com\/view\/([a-z\d]+)/i; const IMGUR_VIDEO_RE = /^https?:\/\/i\.imgur\.com\/([a-z\d]+)\.(gifv|mp4)/i; @@ -164,7 +164,7 @@ function getVideoId(url) { function getDefaultAspectRatio(url) { if (YOUTUBE_VIDEO_RE.test(url)) { - return 9 / 16; + return isYoutubeShort(url) ? 16 / 9 : 9 / 16; } if (VIMEO_VIDEO_RE.test(url)) { return 9 / 16; @@ -207,7 +207,7 @@ function getYoutubeVideoInfo(url, withoutAutoplay) { return { byline: `Open on YouTube`, - aspectRatio: aspectRatio.set(url, 9 / 16), + aspectRatio: aspectRatio.set(url, isYoutubeShort(url) ? 16 / 9 : 9 / 16), previewURL: `https://img.youtube.com/vi/${videoID}/hqdefault.jpg`, playerURL: `https://www.youtube.com/embed/${videoID}?rel=0&fs=1${ withoutAutoplay ? '' : '&autoplay=1' @@ -387,3 +387,7 @@ function loadImage(url) { img.src = url; }); } + +function isYoutubeShort(url) { + return url.includes('/shorts/'); +} diff --git a/src/components/manage-subscribers.jsx b/src/components/manage-subscribers.jsx index 6d95c58df..eb8bcdf82 100644 --- a/src/components/manage-subscribers.jsx +++ b/src/components/manage-subscribers.jsx @@ -1,89 +1,147 @@ -import { PureComponent } from 'react'; -import { connect } from 'react-redux'; +import { useCallback, useEffect, useMemo } from 'react'; +import { connect, useDispatch, useSelector } from 'react-redux'; import { Link } from 'react-router'; import _ from 'lodash'; -import { unsubscribeFromGroup, makeGroupAdmin, unadminGroupAdmin } from '../redux/action-creators'; +import { + unsubscribeFromGroup, + makeGroupAdmin, + unadminGroupAdmin, + getGroupBlockedUsers, + unblockUserInGroup, +} from '../redux/action-creators'; import { tileUserListFactory, WITH_REMOVE_AND_MAKE_ADMIN_HANDLES, WITH_REMOVE_ADMIN_RIGHTS, } from './tile-user-list'; +import { BlockInGroupForm } from './block-in-group-form'; const SubsList = tileUserListFactory({ type: WITH_REMOVE_AND_MAKE_ADMIN_HANDLES }); const AdminsList = tileUserListFactory({ type: WITH_REMOVE_ADMIN_RIGHTS }); +const BlockedList = tileUserListFactory(); -class ManageSubscribersHandler extends PureComponent { - handleRemove = (username) => { - this.props.unsubscribeFromGroup(this.props.groupName, username); - }; +function ManageSubscribersHandler({ user: currentUser, groupName, amIAdmin, ...props }) { + const dispatch = useDispatch(); + const blockedUsersStatus = useSelector((state) => state.groupBlockedUsersStatus); + const blockedUsersIds = useSelector((state) => state.groupBlockedUsers); + const allUsers = useSelector((state) => state.users); - handleMakeAdmin = (user) => { - this.props.makeGroupAdmin(this.props.groupName, user); - }; + const blockedUsers = useMemo( + () => blockedUsersIds.map((id) => allUsers[id]), + [allUsers, blockedUsersIds], + ); - handleRemoveAdminRights = (user) => { - const isItMe = this.props.user.id === user.id; - this.props.unadminGroupAdmin(this.props.groupName, user, isItMe); - }; + const handleRemove = useCallback( + (username) => dispatch(unsubscribeFromGroup(groupName, username)), + [dispatch, groupName], + ); + const handleMakeAdmin = useCallback( + (user) => dispatch(makeGroupAdmin(groupName, user)), + [dispatch, groupName], + ); + const handleRemoveAdminRights = useCallback( + (user) => { + const isItMe = currentUser.id === user.id; + dispatch(unadminGroupAdmin(groupName, user, isItMe)); + }, + [currentUser.id, dispatch, groupName], + ); - render() { - const { props } = this; + useEffect( + () => amIAdmin && blockedUsersStatus.initial && dispatch(getGroupBlockedUsers(groupName)), + [amIAdmin, blockedUsersStatus.initial, dispatch, groupName], + ); - return ( -
-
- {props.boxHeader} -
-
-
-
- {props.groupName} › Manage subscribers -
-
- Browse subscribers -
-
-
-

Manage subscribers

- {props.users ?

Subscribers

: false} - {props.users ? ( - props.users.length === 0 ? ( -
- There’s not a single one subscriber yet. You might invite some friends to - change that. -
- ) : ( - - ) - ) : ( - false - )} + const blockedUsersActions = useMemo( + () => [ + { + title: 'Unblock', + handler: (user) => dispatch(unblockUserInGroup(groupName, user.username)), + }, + ], + [dispatch, groupName], + ); + + useEffect(() => { + if (!amIAdmin) { + props.router.replace(`/${groupName}/subscribers`); + } + }, [amIAdmin, groupName, props.router]); -

Admins

+ if (!amIAdmin) { + return null; + } - {props.amILastGroupAdmin ? ( + return ( +
+
+ {props.boxHeader} +
+
+
+
+ {groupName} › Manage subscribers +
+
+ Browse subscribers +
+
+
+

Manage subscribers

+ {props.users ?

Subscribers

: false} + {props.users ? ( + props.users.length === 0 ? (
- You are the only Admin for this group. Before you can drop administrative privileges - or leave this group, you have to promote another group member to Admin first. + There’s not a single one subscriber yet. You might invite some friends to + change that.
) : ( - + + ) + ) : ( + false + )} + +

+ Admins +

+ + {props.amILastGroupAdmin ? ( +
+ You are the only Admin for this group. Before you can drop administrative privileges + or leave this group, you have to promote another group member to Admin first. +
+ ) : ( + + )} + +

+ Blocked users +

+
+

+ Blocked users cannot write posts to the group. They can still see and comment the + posts in the group while it is not private. +

+ + {(blockedUsersStatus.loading || blockedUsersStatus.initial) && ( +

Loading list…

)} + {blockedUsersStatus.error &&

{blockedUsersStatus.errorText}

}
+ {blockedUsersStatus.success && blockedUsers.length > 0 ? ( + + ) : ( +

This group has no blocked users.

+ )}
- ); - } +
+ ); } + function selectState(state, ownProps) { const { boxHeader, groupAdmins: allGroupAdmins, user } = state; const groupName = ownProps.params.userName; @@ -93,18 +151,18 @@ function selectState(state, ownProps) { }); const users = _.sortBy(usersWhoAreNotAdmins, 'username'); - const amILastGroupAdmin = - groupAdmins.find((u) => u.username == state.user.username) != null && groupAdmins.length == 1; - - return { boxHeader, groupName, user, groupAdmins, users, amILastGroupAdmin }; -} + const amIAdmin = state.managedGroups.some((g) => g.username === groupName); + const amILastGroupAdmin = amIAdmin && groupAdmins.length == 1; -function selectActions(dispatch) { return { - unsubscribeFromGroup: (...args) => dispatch(unsubscribeFromGroup(...args)), - makeGroupAdmin: (...args) => dispatch(makeGroupAdmin(...args)), - unadminGroupAdmin: (...args) => dispatch(unadminGroupAdmin(...args)), + boxHeader, + groupName, + user, + groupAdmins, + users, + amIAdmin, + amILastGroupAdmin, }; } -export default connect(selectState, selectActions)(ManageSubscribersHandler); +export default connect(selectState)(ManageSubscribersHandler); diff --git a/src/components/media-viewer.jsx b/src/components/media-viewer.jsx index e796a14ff..dbfc74deb 100644 --- a/src/components/media-viewer.jsx +++ b/src/components/media-viewer.jsx @@ -55,18 +55,25 @@ const getEmbeddableItem = async (url, withoutAutoplay) => { } let playerHTML = null; - const w = 800; - const h = info.aspectRatio ? Math.round(w * info.aspectRatio) : 450; - const wrapperPadding = info.aspectRatio ? `${info.aspectRatio * 100}%` : null; + let w = 800; + let h = 450; + if (info.aspectRatio) { + if (info.aspectRatio <= 1) { + h = Math.round(w * info.aspectRatio); + } else { + h = 800; + w = Math.round(h / info.aspectRatio); + } + } if (info.html) { - playerHTML = `
${info.html}
`; + playerHTML = `
${info.html}
`; } else { let player = null; if (info.playerURL) { player = (