Skip to content

Commit

Permalink
Private groups
Browse files Browse the repository at this point in the history
  • Loading branch information
1aerostorm committed Apr 29, 2024
1 parent 4e90fb0 commit 8239dc1
Show file tree
Hide file tree
Showing 17 changed files with 427 additions and 2 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"redux-logger": "^3.0.6",
"redux-modules": "0.0.5",
"redux-saga": "^1.1.3",
"sass": "^1.49.7"
"sass": "^1.49.7",
"speakingurl": "^14.0.1"
},
"devDependencies": {
"@red-mobile/cordova-plugin-shortcuts-android": "^1.0.1",
Expand Down
5 changes: 5 additions & 0 deletions src/assets/icons/chevron-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/components/all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

// modules
@import './modules/LoginForm.scss';
@import './modules/CreateGroup.scss';
@import './modules/Modals.scss';

@import "./pages/Messages";
1 change: 1 addition & 0 deletions src/components/elements/Icon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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')],
Expand Down
5 changes: 5 additions & 0 deletions src/components/elements/messages/StartPanel/StartPanel.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.msgs-start-panel {
.button {
display: block;
}
}
56 changes: 56 additions & 0 deletions src/components/elements/messages/StartPanel/index.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<img className='msgs-empty-chat' src='/msg_empty.png' />
<div className='msgs-start-panel'>
<button className='button' onClick={this.startChat}>{tt('msgs_start_panel.start_chat')}</button>
<button className='button hollow' onClick={this.goCreateGroup}>{tt('msgs_start_panel.create_group')}</button>
</div>
</div>
)
}
}

export default connect(
(state, ownProps) => {
return { ...ownProps }
},
dispatch => ({
showCreateGroup() {
dispatch(user.actions.showCreateGroup())
},
})
)(StartPanel)
28 changes: 28 additions & 0 deletions src/components/elements/messages/Stepper/Stepper.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
56 changes: 56 additions & 0 deletions src/components/elements/messages/Stepper/index.jsx
Original file line number Diff line number Diff line change
@@ -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(<div className={'step ' + cn} style={{ minWidth: width + '%' }}>
<div className={'bar'}></div>
{content}
</div>)
}

return <div className='Stepper'>
{stepObjs}
</div>
}
}

export default Stepper
172 changes: 172 additions & 0 deletions src/components/modules/CreateGroup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
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 getSlug from 'speakingurl'

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: {
title: '',
name: '',
is_encrypted: true,
privacy: 'public_group'
}
}
this.stepperRef = React.createRef()
}

validate = () => {
}

_onSubmit = () => {
}

goNext = (e, setFieldValue) => {
e.preventDefault()
this.stepperRef.current.nextStep()
}

onTitleChange = (e, setFieldValue) => {
const { value } = e.target
setFieldValue('title', value)
let link = getSlug(value)
setFieldValue('name', link)
this.setState({
showName: true
})
}

onPrivacyChange = (e, setFieldValue) => {
setFieldValue('privacy', e.target.value)
setFieldValue('is_encrypted', true)
}

render() {
const { showName } = this.state
const form = (<Formik
initialValues={this.state.initialValues}
enableReinitialize={true}
validate={this.validate}
onSubmit={this._onSubmit}
>
{({
handleSubmit, isSubmitting, isValid, values, setFieldValue, handleChange,
}) => {
const disabled = !isValid
return (
<Form>

<div className='row' style={{ marginTop: '1.0rem', marginBottom: '1.0rem' }}>
<div className='column small-4' style={{paddingTop: 5}}>
{tt('create_group_jsx.title')}
</div>
<div className='column small-8'>
<Field
type='text'
name='title'
maxLength='48'
onChange={e => this.onTitleChange(e, setFieldValue)}
autoFocus
/>
<ErrorMessage name='title' component='div' className='error' />
</div>
</div>

{showName ? <div className='row' style={{ marginTop: '1.0rem', marginBottom: '1.0rem' }}>
<div className='column small-4' style={{paddingTop: 5}}>
{tt('create_group_jsx.name')}
</div>
<div className='column small-8'>
<Field
type='text'
name='name'
maxLength='32'
autoFocus
/>
<ErrorMessage name='name' component='div' className='error' />
</div>
</div> : null}

<div className='row' style={{ marginTop: '1.0rem', marginBottom: '1.0rem' }}>
<div className='column small-4' style={{paddingTop: 5}}>
{tt('create_group_jsx.access')}
</div>
<div className='column small-8'>
<Field
as='select'
name='privacy'
onChange={e => this.onPrivacyChange(e, setFieldValue)}
>
<option value='public_group'>{tt('create_group_jsx.access_all')}</option>
<option value='public_read_only'>{tt('create_group_jsx.all_read_only')}</option>
<option value='private_group'>{tt('create_group_jsx.access_private')}</option>
</Field>
<ErrorMessage name='privacy' component='div' className='error' />
</div>
</div>

<div className='row' style={{ marginTop: '1.0rem', marginBottom: '1.0rem' }}>
<div className='column small-12'>
<label style={{fontSize: '100%'}}>
<Field
type='checkbox'
name='is_encrypted'
disabled={values.privacy === 'private_group'}
/>
{tt('create_group_jsx.encrypted')}
{values.privacy === 'private_group' ? <span className='secondary'>{tt('create_group_jsx.encrypted_dis')}</span> : null}
</label>
<ErrorMessage name='is_encrypted' component='div' className='error' />
</div>
</div>

<Stepper ref={this.stepperRef} steps={{
name: tt('create_group_jsx.step_name'),
logo: tt('create_group_jsx.step_logo'),
admin: tt('create_group_jsx.step_admin'),
create: tt('create_group_jsx.step_create')
}} startStep='name' />
{isSubmitting ? <span><LoadingIndicator type='circle' /><br /></span>
: <span>
<button onClick={this.goNext} disabled={disabled} className='button small next-button' title={tt('g.submit')}>
<Icon name='chevron-right' size='1_25x' />
</button>
</span>}
</Form>
)}}</Formik>)

return <div className='CreateGroup'>
<div className='row'>
<h3>{tt('msgs_start_panel.create_group')}</h3>
</div>
{form}
</div>
}
}

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)
15 changes: 15 additions & 0 deletions src/components/modules/CreateGroup.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit 8239dc1

Please sign in to comment.