From b21f7ca347fcefb45e72c6d6546ae6b746e3ab53 Mon Sep 17 00:00:00 2001 From: 1aerostorm Date: Mon, 29 Apr 2024 00:17:32 +0300 Subject: [PATCH] Private groups --- src/assets/icons/chevron-right.svg | 5 + src/components/all.scss | 1 + src/components/elements/Icon.jsx | 1 + .../messages/StartPanel/StartPanel.scss | 5 + .../elements/messages/StartPanel/index.jsx | 56 ++++++++ .../elements/messages/Stepper/Stepper.scss | 28 ++++ .../elements/messages/Stepper/index.jsx | 56 ++++++++ src/components/modules/CreateGroup.jsx | 134 ++++++++++++++++++ src/components/modules/CreateGroup.scss | 15 ++ src/components/modules/Modals.jsx | 13 ++ .../modules/messages/Messenger/Messenger.css | 13 ++ .../modules/messages/Messenger/index.js | 3 +- src/locales/en.json | 23 +++ src/locales/ru-RU.json | 23 +++ src/redux/UserReducer.js | 3 + 15 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 src/assets/icons/chevron-right.svg create mode 100644 src/components/elements/messages/StartPanel/StartPanel.scss create mode 100644 src/components/elements/messages/StartPanel/index.jsx create mode 100644 src/components/elements/messages/Stepper/Stepper.scss create mode 100644 src/components/elements/messages/Stepper/index.jsx create mode 100644 src/components/modules/CreateGroup.jsx create mode 100644 src/components/modules/CreateGroup.scss diff --git a/src/assets/icons/chevron-right.svg b/src/assets/icons/chevron-right.svg new file mode 100644 index 000000000..f922ce9f7 --- /dev/null +++ b/src/assets/icons/chevron-right.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/components/all.scss b/src/components/all.scss index 5e942a191..0f9e5ce22 100644 --- a/src/components/all.scss +++ b/src/components/all.scss @@ -20,6 +20,7 @@ // modules @import './modules/LoginForm.scss'; +@import './modules/CreateGroup.scss'; @import './modules/Modals.scss'; @import "./pages/Messages"; diff --git a/src/components/elements/Icon.jsx b/src/components/elements/Icon.jsx index 120f3e412..26dff1106 100644 --- a/src/components/elements/Icon.jsx +++ b/src/components/elements/Icon.jsx @@ -9,6 +9,7 @@ const icons = new Map([ // ['chevron-up-circle', require('app/assets/icons/chevron-up-circle.svg')], // ['chevron-down-circle', require('app/assets/icons/chevron-down-circle.svg')], ['chevron-left', require('app/assets/icons/chevron-left.svg')], + ['chevron-right', require('app/assets/icons/chevron-right.svg')], // ['chatboxes', require('app/assets/icons/chatboxes.svg')], ['cross', require('app/assets/icons/cross.svg')], // ['chatbox', require('app/assets/icons/chatbox.svg')], diff --git a/src/components/elements/messages/StartPanel/StartPanel.scss b/src/components/elements/messages/StartPanel/StartPanel.scss new file mode 100644 index 000000000..63dac7e5a --- /dev/null +++ b/src/components/elements/messages/StartPanel/StartPanel.scss @@ -0,0 +1,5 @@ +.msgs-start-panel { + .button { + display: block; + } +} diff --git a/src/components/elements/messages/StartPanel/index.jsx b/src/components/elements/messages/StartPanel/index.jsx new file mode 100644 index 000000000..3dd02c37c --- /dev/null +++ b/src/components/elements/messages/StartPanel/index.jsx @@ -0,0 +1,56 @@ +import React from 'react' +import tt from 'counterpart' +import {connect} from 'react-redux' + +import user from 'app/redux/UserReducer' +import './StartPanel.scss' + +class StartPanel extends React.Component { + constructor(props) { + super(props) + this.state = { + } + } + + startChat = (e) => { + e.preventDefault() + const inp = document.getElementsByClassName('conversation-search-input') + if (!inp.length) { + console.error('startChat - no conversation-search-input') + return + } + if (inp.length > 1) { + console.error('startChat - multiple conversation-search-input:', inp) + return + } + inp[0].focus() + } + + goCreateGroup = (e) => { + e.preventDefault() + this.props.showCreateGroup() + } + + render() { + return ( +
+ +
+ + +
+
+ ) + } +} + +export default connect( + (state, ownProps) => { + return { ...ownProps } + }, + dispatch => ({ + showCreateGroup() { + dispatch(user.actions.showCreateGroup()) + }, + }) +)(StartPanel) diff --git a/src/components/elements/messages/Stepper/Stepper.scss b/src/components/elements/messages/Stepper/Stepper.scss new file mode 100644 index 000000000..fef8b68a6 --- /dev/null +++ b/src/components/elements/messages/Stepper/Stepper.scss @@ -0,0 +1,28 @@ +.Stepper { + width: 100%; + + .step { + display: inline-block; + text-align: center; + color: gray; + font-size: 90%; + .bar { + background-color: gray; + height: 8px; + margin-top: 0.25rem; + margin-bottom: 0.25rem; + } + + &.left { + .bar { + background-color: #0078C4; + } + } + &.current { + color: #0078C4; + .bar { + background-color: #0078C4; + } + } + } +} diff --git a/src/components/elements/messages/Stepper/index.jsx b/src/components/elements/messages/Stepper/index.jsx new file mode 100644 index 000000000..6006feee4 --- /dev/null +++ b/src/components/elements/messages/Stepper/index.jsx @@ -0,0 +1,56 @@ +import React from 'react' + +import './Stepper.scss' + +class Stepper extends React.Component { + constructor(props) { + super(props) + const { steps, startStep } = this.props + const entr = Object.entries(steps) + this.state = { + currentStep: startStep || entr[0][0] + } + } + + nextStep = () => { + const { steps } = this.props + const entr = Object.entries(steps) + const { currentStep } = this.state + let found + for (const [key, content] of entr) { + if (found) { + this.setState({ + currentStep: key + }) + return + } + found = key === currentStep + } + } + + render() { + const { steps } = this.props + let { currentStep } = this.state + + const entr = Object.entries(steps) + currentStep = currentStep || entr[0][0] + const width = (100 / entr.length).toFixed(1) + const stepObjs = [] + let foundCurrent + for (const [key, content] of entr) { + const isCurrent = key === currentStep + foundCurrent = foundCurrent || isCurrent + const cn = foundCurrent ? (isCurrent ? 'current' : '') : 'left' + stepObjs.push(
+
+ {content} +
) + } + + return
+ {stepObjs} +
+ } +} + +export default Stepper diff --git a/src/components/modules/CreateGroup.jsx b/src/components/modules/CreateGroup.jsx new file mode 100644 index 000000000..b19e6823a --- /dev/null +++ b/src/components/modules/CreateGroup.jsx @@ -0,0 +1,134 @@ +import React from 'react' +import {connect} from 'react-redux' +import { Formik, Form, Field, ErrorMessage, } from 'formik' +import { Map } from 'immutable' +import { Asset, AssetEditor } from 'golos-lib-js/lib/utils' +import tt from 'counterpart' + +import g from 'app/redux/GlobalReducer' +import transaction from 'app/redux/TransactionReducer' +import user from 'app/redux/UserReducer' +import Icon from 'app/components/elements/Icon' +import LoadingIndicator from 'app/components/elements/LoadingIndicator' +import FormikAgent from 'app/components/elements/donate/FormikUtils' +import Stepper from 'app/components/elements/messages/Stepper' + +class CreateGroup extends React.Component { + constructor(props) { + super(props) + this.state = { + initialValues: { + name: '', + is_encrypted: true, + privacy: 'public_group' + } + } + this.stepperRef = React.createRef() + } + + validate = () => { + } + + _onSubmit = () => { + } + + goNext = (e) => { + e.preventDefault() + this.stepperRef.current.nextStep() + } + + render() { + const form = ( + {({ + handleSubmit, isSubmitting, isValid, values, setFieldValue, handleChange, + }) => { + const disabled = !isValid + return ( +
+ +
+
+ {tt('create_group_jsx.name')} +
+
+ + +
+
+ +
+
+ {tt('create_group_jsx.access')} +
+
+ + + + + + +
+
+ +
+
+ + +
+
+ + + {isSubmitting ?
+ : + + } + + )}}
) + + return
+
+

{tt('msgs_start_panel.create_group')}

+
+ {form} +
+ } +} + +export default connect( + (state, ownProps) => { + const currentUser = state.user.getIn(['current']) + const currentAccount = currentUser && state.global.getIn(['accounts', currentUser.get('username')]) + + return { ...ownProps, + currentUser, + currentAccount, + } + }, + dispatch => ({ + }) +)(CreateGroup) diff --git a/src/components/modules/CreateGroup.scss b/src/components/modules/CreateGroup.scss new file mode 100644 index 000000000..386d59a69 --- /dev/null +++ b/src/components/modules/CreateGroup.scss @@ -0,0 +1,15 @@ +.CreateGroup { + .next-button { + width: 48px; + height: 48px; + padding-left: 13px !important; + color: white; + position: absolute; + bottom: 0.4rem; + right: 0.5rem; + } + .Stepper { + width: 85%; + margin-left: 1rem; + } +} diff --git a/src/components/modules/Modals.jsx b/src/components/modules/Modals.jsx index 8fcb103c1..c3643426d 100644 --- a/src/components/modules/Modals.jsx +++ b/src/components/modules/Modals.jsx @@ -5,6 +5,7 @@ import { connect } from 'react-redux'; import CloseButton from 'react-foundation-components/lib/global/close-button'; import Reveal from 'react-foundation-components/lib/global/reveal'; +import CreateGroup from 'app/components/modules/CreateGroup' import Donate from 'app/components/modules/Donate' import LoginForm from 'app/components/modules/LoginForm'; import AppDownload from 'app/components/modules/app/AppDownload' @@ -17,6 +18,7 @@ class Modals extends React.Component { static propTypes = { show_login_modal: PropTypes.bool, show_donate_modal: PropTypes.bool, + show_create_group_modal: PropTypes.bool, show_app_download_modal: PropTypes.bool, hideDonate: PropTypes.func.isRequired, hideAppDownload: PropTypes.func.isRequired, @@ -34,9 +36,11 @@ class Modals extends React.Component { const { show_login_modal, show_donate_modal, + show_create_group_modal, show_app_download_modal, hideLogin, hideDonate, + hideCreateGroup, hideAppDownload, notifications, removeNotification, @@ -60,6 +64,10 @@ class Modals extends React.Component { } + {show_create_group_modal && + + + } {show_app_download_modal && @@ -81,6 +89,7 @@ export default connect( return { show_login_modal: state.user.get('show_login_modal'), show_donate_modal: state.user.get('show_donate_modal'), + show_create_group_modal: state.user.get('show_create_group_modal'), show_app_download_modal: state.user.get('show_app_download_modal'), loginUnclosable, notifications: state.app.get('notifications'), @@ -95,6 +104,10 @@ export default connect( if (e) e.preventDefault() dispatch(user.actions.hideDonate()) }, + hideCreateGroup: e => { + if (e) e.preventDefault() + dispatch(user.actions.hideCreateGroup()) + }, hideAppDownload: e => { if (e) e.preventDefault() dispatch(user.actions.hideAppDownload()) diff --git a/src/components/modules/messages/Messenger/Messenger.css b/src/components/modules/messages/Messenger/Messenger.css index d5db6318a..6dc153449 100644 --- a/src/components/modules/messages/Messenger/Messenger.css +++ b/src/components/modules/messages/Messenger/Messenger.css @@ -85,3 +85,16 @@ transform: translateX(-50%) translateY(-50%); left: 50%; } + +.msgs-start-panel { + top: 50%; + transform: translateX(-50%) translateY(-50%); + left: 50%; + position: absolute; + z-index: 10; + background-color: rgba(255, 255, 255, 0.5); + backdrop-filter: blur(5px); + padding: 1rem; + border-radius: 10px; + border: 1px solid black; +} diff --git a/src/components/modules/messages/Messenger/index.js b/src/components/modules/messages/Messenger/index.js index e3ad9747e..2f04d2255 100644 --- a/src/components/modules/messages/Messenger/index.js +++ b/src/components/modules/messages/Messenger/index.js @@ -3,6 +3,7 @@ import Dropzone from 'react-dropzone'; import ConversationList from '../ConversationList'; import MessageList from '../MessageList'; +import StartPanel from 'app/components/elements/messages/StartPanel' import isScreenSmall from 'app/utils/isScreenSmall' import './Messenger.css'; @@ -83,7 +84,7 @@ export default class Messages extends React.Component { topRight={messagesTopRight} renderEmpty={() => { if ((localStorage.getItem('msgr_auth') && !account) || process.env.MOBILE_APP) return null - return () + return }} messages={messages} replyingMessage={replyingMessage} diff --git a/src/locales/en.json b/src/locales/en.json index 91abf9e1b..aafdb332e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -76,6 +76,29 @@ "blocked_BY": "You are blocked by @%(BY)s.", "do_not_bother_BY": "@%(BY)s wants to not be bothered by low-reputation users." }, + "msgs_start_panel": { + "start_chat": "Start chat", + "create_group": "Create group" + }, + "create_group_jsx": { + "name": "Name", + "logo": "Logo", + "admin": "Admin", + "encrypted": "Encrypt messages in group", + "encrypted_hint": "Шифрование позволяет сделать сообщения доступными только тем, кто имеет доступ к группе.", + "access": "Who will see this group", + "access_hint": "Можно сделать группу открытой, или доступной только тем, чьи заявки на вступление в группу одобрит администрация.", + "access_all": "Everyone", + "all_read_only": "Everyone, but posting only for members", + "access_private": "Only members", + "next": "Next", + "back": "Back", + "submit": "Create", + "step_name": "Name", + "step_logo": "Logo", + "step_admin": "Administrator", + "step_create": "Create!" + }, "emoji_i18n": { "categoriesLabel": "Категории", "emojiUnsupportedMessage": "Ваш браузер не поддерживает эмодзи.", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index 65fb2f390..650998743 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -77,6 +77,29 @@ "blocked_BY": "Вы заблокированы пользователем @%(BY)s.", "do_not_bother_BY": "@%(BY)s просит пользователей с низкой репутацией не беспокоить." }, + "msgs_start_panel": { + "start_chat": "Начать чат", + "create_group": "Создать группу" + }, + "create_group_jsx": { + "name": "Название", + "logo": "Логотип", + "admin": "Администратор", + "encrypted": "Шифровать сообщения в группе", + "encrypted_hint": "Шифрование позволяет сделать сообщения доступными только тем, кто имеет доступ к группе.", + "access": "Кому будет доступна группа", + "access_hint": "Можно сделать группу открытой, или доступной только тем, чьи заявки на вступление в группу одобрит администрация.", + "access_all": "Всем", + "all_read_only": "Всем, но постить только участникам", + "access_private": "Только участникам", + "next": "Далее", + "back": "Назад", + "submit": "Создать", + "step_name": "Имя", + "step_logo": "Логотип", + "step_admin": "Администратор", + "step_create": "Создать!" + }, "emoji_i18n": { "categoriesLabel": "Категории", "emojiUnsupportedMessage": "Ваш браузер не поддерживает эмодзи.", diff --git a/src/redux/UserReducer.js b/src/redux/UserReducer.js index 20fe4bfd1..e60902e38 100644 --- a/src/redux/UserReducer.js +++ b/src/redux/UserReducer.js @@ -5,6 +5,7 @@ const defaultState = fromJS({ current: null, show_login_modal: false, show_donate_modal: false, + show_create_group_modal: false, show_app_download_modal: false, loginLoading: false, pub_keys_used: null, @@ -128,6 +129,8 @@ export default createModule({ { action: 'HIDE_CONNECTION_ERROR_MODAL', reducer: state => state.set('hide_connection_error_modal', true) }, { action: 'SHOW_DONATE', reducer: state => state.set('show_donate_modal', true) }, { action: 'HIDE_DONATE', reducer: state => state.set('show_donate_modal', false) }, + { action: 'SHOW_CREATE_GROUP', reducer: state => state.set('show_create_group_modal', true) }, + { action: 'HIDE_CREATE_GROUP', reducer: state => state.set('show_create_group_modal', false) }, { action: 'SHOW_APP_DOWNLOAD', reducer: state => state.set('show_app_download_modal', true) }, { action: 'HIDE_APP_DOWNLOAD', reducer: state => state.set('show_app_download_modal', false) }, { action: 'SET_DONATE_DEFAULTS', reducer: (state, {payload}) => state.set('donate_defaults', fromJS(payload)) },