From e3ac7e6e6d5e7e4593efc707e9d0ff341ef3d134 Mon Sep 17 00:00:00 2001 From: 1aerostorm Date: Mon, 8 Jul 2024 21:51:10 +0300 Subject: [PATCH] HF 30 - Private group, finish members --- src/components/elements/groups/GroupCost.jsx | 34 +++++ src/components/modules/CreateGroup.jsx | 133 +++++++++++++++--- src/components/modules/groups/GroupFinal.jsx | 61 +++++++- .../modules/groups/GroupMembers.jsx | 21 +-- src/components/modules/groups/GroupName.jsx | 13 +- .../modules/groups/GroupSettings.jsx | 2 +- src/components/modules/groups/MyGroups.jsx | 2 +- src/locales/en.json | 22 ++- src/locales/ru-RU.json | 8 +- src/redux/TransactionSaga.js | 12 +- 10 files changed, 244 insertions(+), 64 deletions(-) create mode 100644 src/components/elements/groups/GroupCost.jsx diff --git a/src/components/elements/groups/GroupCost.jsx b/src/components/elements/groups/GroupCost.jsx new file mode 100644 index 00000000..371e6fbe --- /dev/null +++ b/src/components/elements/groups/GroupCost.jsx @@ -0,0 +1,34 @@ +import React from 'react' +import tt from 'counterpart' + +import Icon from 'app/components/elements/Icon' + +export default class GroupCost extends React.Component { + state = {} + + constructor(props) { + super(props) + } + + render() { + let { cost, marginTop } = this.props + if (!cost) { + return null + } + marginTop = marginTop || '1.0rem' + const isFree = cost.eq(0) + const costTitle = isFree ? tt('create_group_jsx.create_of_group_is') : tt('create_group_jsx.gbg_too_low') + const costStr = isFree ? tt('create_group_jsx.free') : cost.floatString + return
+
+ + + + {costTitle} + {costStr}. + + +
+
+ } +} diff --git a/src/components/modules/CreateGroup.jsx b/src/components/modules/CreateGroup.jsx index 6c755e20..f54791aa 100644 --- a/src/components/modules/CreateGroup.jsx +++ b/src/components/modules/CreateGroup.jsx @@ -138,16 +138,28 @@ class CreateGroup extends React.Component { submitError: '' }) + let members = [] + const { name } = data + let { groups } = this.props + if (groups) { + groups = groups.toJS() + const group = groups[name] + if (group) { + let mems = group.members + if (mems) { + members = mems.data + } + } + } + showLoginDialog(creator, (res) => { const password = res && res.password if (!password) { actions.setSubmitting(false) return } - this.props.privateGroup({ - password, - ...data, - onSuccess: () => { + try { + const finalSuccess = () => { actions.setSubmitting(false) const { closeMe } = this.props if (closeMe) closeMe() @@ -155,12 +167,45 @@ class CreateGroup extends React.Component { window.location.href = '/' + data.name return } - }, - onError: (err, errStr) => { - this.setState({ submitError: errStr }) - actions.setSubmitting(false) } - }) + this.props.privateGroup({ + password, + ...data, + onSuccess: () => { + try { + if (!members.length) { + finalSuccess() + return + } + + this.props.groupMembers({ + requester: data.creator, + name: data.name, + members, + onSuccess: () => { + finalSuccess() + }, + onError: (err, errStr) => { + this.setState({ submitError: { + type: 'members', err: errStr } }) + actions.setSubmitting(false) + } + }) + } catch (err) { + this.setState({ submitError: { + type: 'members', err: err.toString() } }) + actions.setSubmitting(false) + } + }, + onError: (err, errStr) => { + this.setState({ submitError: errStr }) + actions.setSubmitting(false) + } + }) + } catch (err) { + this.setState({ submitError: err.toString() }) + actions.setSubmitting(false) + } }, 'active') } @@ -215,14 +260,17 @@ class CreateGroup extends React.Component { {({ handleSubmit, isSubmitting, isValid, values, errors, setFieldValue, applyFieldValue, setFieldTouched, handleChange, }) => { - const disabled = !isValid || !!validators || !values.name + let disabled = !isValid || !!validators || !values.name + if (submitError && submitError.type === 'members') { + disabled = true + } return (
{!isSubmitting ? (step === 'name' ? : step === 'logo' ? : step === 'members' ? : - step === 'final' ? : + step === 'final' ? : ) : null} {!isSubmitting && { - let json_metadata = { + const trx = [] + let json_metadata, opData, json + + json_metadata = { app: 'golos-messenger', version: 1, title, logo } json_metadata = JSON.stringify(json_metadata) - - const opData = { + opData = { creator, name, json_metadata, @@ -283,24 +336,58 @@ export default connect( privacy, extensions: [], } - - const json = JSON.stringify(['private_group', opData]) + json = JSON.stringify(['private_group', opData]) + trx.push(['custom_json', { + id: 'private_message', + required_auths: [creator], + json, + }]) dispatch(transaction.actions.broadcastOperation({ type: 'custom_json', - operation: { + trx, + username: creator, + keys: [password], + successCallback: onSuccess, + errorCallback: (err, errStr) => { + console.error(err) + if (onError) onError(err, errStr) + }, + })); + }, + groupMembers: ({ requester, name, members, + onSuccess, onError }) => { + const trx = [] + let opData, json + + for (const mem of members) { + const { account, member_type, json_metadata } = mem + opData = { + requester, + name, + member: account, + member_type, + json_metadata: '{}', + extensions: [], + } + json = JSON.stringify(['private_group_member', opData]) + trx.push(['custom_json', { id: 'private_message', - required_auths: [creator], + required_posting_auths: [requester], json, - }, - username: creator, - password, + }]) + } + + dispatch(transaction.actions.broadcastOperation({ + type: 'custom_json', + trx, + username: requester, successCallback: onSuccess, errorCallback: (err, errStr) => { console.error(err) if (onError) onError(err, errStr) }, })); - } + }, }) )(CreateGroup) diff --git a/src/components/modules/groups/GroupFinal.jsx b/src/components/modules/groups/GroupFinal.jsx index 1fa2cdec..8954093c 100644 --- a/src/components/modules/groups/GroupFinal.jsx +++ b/src/components/modules/groups/GroupFinal.jsx @@ -1,8 +1,10 @@ import React from 'react' import { connect } from 'react-redux' +import { Link } from 'react-router-dom' import { Field, ErrorMessage, } from 'formik' import tt from 'counterpart' +import GroupCost from 'app/components/elements/groups/GroupCost' import ExtLink from 'app/components/elements/ExtLink' class GroupFinal extends React.Component { @@ -13,6 +15,14 @@ class GroupFinal extends React.Component { } decorateSubmitError = (error) => { + if (error && error.type === 'members') { + return + {tt('create_group_jsx.cannot_set_members')}
+ {tt('create_group_jsx.cannot_set_members2')}
+
+ {error.err} +
+ } if (error && error.startsWith && error.startsWith(tt('donate_jsx.insufficient_funds'))) { const { username } = this.props return @@ -27,18 +37,57 @@ class GroupFinal extends React.Component { } render() { - const { submitError } = this.props + let { group, submitError, cost } = this.props + let moders = [], members = [] + if (group) { + let allMembers = group.get('members') + if (allMembers) { + allMembers = allMembers.get('data').toJS() + const makeLink = (pgm) => { + return + {'@' + pgm.account} + + } + const addCommas = (arr) => { + return arr.reduce((list, elem, i) => { + const { key } = elem + list.push(elem) + if (i !== arr.length - 1) { + list.push() + } + return list + }, []) + } + for (const pgm of allMembers) { + if (pgm.member_type === 'moder') { + moders.push(makeLink(pgm)) + } else { + members.push(makeLink(pgm)) + } + } + moders = addCommas(moders) + members = addCommas(members) + } + } return -
+
{tt('create_group_jsx.final_desc')} + {moders.length ?
+ {tt('create_group_jsx.moders_list')} {moders} +
: null} + {members.length ?
+ {tt('create_group_jsx.members_list')} {members} +
: null} {submitError ?
{this.decorateSubmitError(submitError)}
: null}
+ + } } @@ -49,9 +98,17 @@ export default connect( const currentUser = state.user.getIn(['current']) const username = currentUser && currentUser.get('username') + const { newGroup } = ownProps + let currentGroup + if (newGroup) { + currentGroup = newGroup + } + const group = currentGroup && state.global.getIn(['groups', currentGroup.name]) + return { ...ownProps, username, + group, } }, dispatch => ({ diff --git a/src/components/modules/groups/GroupMembers.jsx b/src/components/modules/groups/GroupMembers.jsx index 228232dd..50039486 100644 --- a/src/components/modules/groups/GroupMembers.jsx +++ b/src/components/modules/groups/GroupMembers.jsx @@ -2,7 +2,6 @@ import React from 'react' import { connect } from 'react-redux' import { Field, ErrorMessage, } from 'formik' import tt from 'counterpart' -import { api } from 'golos-lib-js' import { validateAccountName } from 'golos-lib-js/lib/utils' import g from 'app/redux/GlobalReducer' @@ -14,25 +13,7 @@ import LoadingIndicator from 'app/components/elements/LoadingIndicator' import { getGroupMeta, getGroupTitle } from 'app/utils/groups' export async function validateMembersStep(values, errors) { - /*if (!values.admin) { - errors.admin = tt('g.required') - } else { - const nameError = validateAccountName(values.admin) - if (nameError.error) { - errors.admin = tt('account_name.' + nameError.error) - } else { - try { - let accs = await api.getAccountsAsync([values.admin]) - accs = accs[0] - if (!accs) { - errors.admin = tt('g.username_does_not_exist') - } - } catch (err) { - console.error(err) - errors.admin = 'Blockchain unavailable :(' - } - } - }*/ + // nothing yet... } class GroupMembers extends React.Component { diff --git a/src/components/modules/groups/GroupName.jsx b/src/components/modules/groups/GroupName.jsx index 35029ccd..170ace4e 100644 --- a/src/components/modules/groups/GroupName.jsx +++ b/src/components/modules/groups/GroupName.jsx @@ -4,6 +4,7 @@ import getSlug from 'speakingurl' import tt from 'counterpart' import { api } from 'golos-lib-js' +import GroupCost from 'app/components/elements/groups/GroupCost' import Icon from 'app/components/elements/Icon' export async function validateNameStep(values, errors) { @@ -148,17 +149,7 @@ export default class GroupName extends React.Component {
- {cost ?
-
- - - - {tt('create_group_jsx.gbg_too_low')} - {cost.floatString}. - - -
-
: null} +
} } diff --git a/src/components/modules/groups/GroupSettings.jsx b/src/components/modules/groups/GroupSettings.jsx index 27caa38e..a9d8e72e 100644 --- a/src/components/modules/groups/GroupSettings.jsx +++ b/src/components/modules/groups/GroupSettings.jsx @@ -322,7 +322,7 @@ export default connect( json, }, username: creator, - password, + keys: [password], successCallback: onSuccess, errorCallback: (err, errStr) => { console.error(err) diff --git a/src/components/modules/groups/MyGroups.jsx b/src/components/modules/groups/MyGroups.jsx index 80fceccf..255d0f36 100644 --- a/src/components/modules/groups/MyGroups.jsx +++ b/src/components/modules/groups/MyGroups.jsx @@ -220,7 +220,7 @@ export default connect( json, }, username: owner, - password, + keys: [password], successCallback: onSuccess, errorCallback: (err, errStr) => { console.error(err) diff --git a/src/locales/en.json b/src/locales/en.json index 23c7bdcc..adc9d20a 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -136,14 +136,19 @@ "gbg_too_low": "Group creation costs ", "gbg_too_low2": "That is not enough ", "gbg_too_low3": "Your GBG balance is ", + "create_of_group_is": "Creating of group is ", + "free": "free", "deposit_gp": "Increase Golos Power", "logo_desc": "The group logo is like a user’s avatar... Not required, but \"must have\"...", "logo_upload": "Upload logo", "logo_link": "Add logo from the URL", "members_desc": "Add people, set moderators of the group...", "final_desc": "Now we are ready to create the group!", + "moders_list": "Moderators:", + "members_list": "Members:", "image_wrong": "Cannot load this image.", - "image_timeout": "Cannot load this image, it is loading too long..." + "image_timeout": "Cannot load this image, it is loading too long...", + "add_member": "+ Add Member..." }, "my_groups_jsx": { "title": "My Groups", @@ -155,6 +160,21 @@ "login_hint_GROUP": "(delete \"%(GROUP)s\" group)", "members": "Members" }, + "group_members_jsx": { + "title": "Members Of ", + "title2": " Group", + "check_pending": "Join Requests", + "check_pending_hint": "Pending Group Join Requests", + "check_banned": "Blocked", + "member": "Member", + "moder": "Moderator", + "owner": "Group Owner", + "make_member": "Make Simple Member", + "make_moder": "Make Moderator", + "ban": "Ban", + "unban": "Unban", + "banned": "Banned" + }, "group_settings_jsx": { "title_GROUP": "%(GROUP)s Group", "submit": "Save", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index 07089a42..ca295b03 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -140,15 +140,21 @@ "gbg_too_low": "Создание группы стоит ", "gbg_too_low2": "Вам не хватает ", "gbg_too_low3": "На вашем балансе - ", + "create_of_group_is": "Создание группы - ", + "free": "бесплатно", "deposit_gp": "Пополнить Силу Голоса", "logo_desc": "Логотип группы - это как аватарка у пользователя... Необязательно, но \"must have\"...", "logo_upload": "Загрузить логотип", "logo_link": "Добавить логотип ссылкой", "members_desc": "Вы можете добавить в группу людей, назначить модераторов...", "final_desc": "Теперь все готово к созданию группы!", + "moders_list": "Модераторы группы:", + "members_list": "Участники:", "image_wrong": "Не удается загрузить картинку.", "image_timeout": "Не удается загрузить картинку, она загружается слишком долго...", - "add_member": "+ Добавить участника..." + "add_member": "+ Добавить участника...", + "cannot_set_members": "Группа создана успешно. Но, к сожалению, не получилось задать участников из-за ошибки.", + "cannot_set_members2": "Вы можете попытаться сделать это заново в настройках группы." }, "my_groups_jsx": { "title": "Мои группы", diff --git a/src/redux/TransactionSaga.js b/src/redux/TransactionSaga.js index 8ce75f1c..d543f2ac 100644 --- a/src/redux/TransactionSaga.js +++ b/src/redux/TransactionSaga.js @@ -163,11 +163,15 @@ function* broadcastOperation( return; } - if (!password) { - password = yield select(state => state.user.getIn(['current', 'private_keys', 'posting_private'])); - if (!password) { + if (!keys) keys = ['posting'] + keys = [...new Set(keys)] // remove duplicate + const idxP = keys.indexOf('posting') + if (idxP !== -1) { + const posting = yield select(state => state.user.getIn(['current', 'private_keys', 'posting_private'])); + if (!posting) { alert('Not authorized') } + keys[idxP] = posting } let operations = trx || [ @@ -195,7 +199,7 @@ function* broadcastOperation( } try { const res = yield golos.broadcast.sendAsync( - tx, [password]) + tx, keys) for (const [type, operation] of operations) { if (hook['accepted_' + type]) { try {