Skip to content

Commit

Permalink
HF 30 - Private groups
Browse files Browse the repository at this point in the history
  • Loading branch information
1aerostorm committed Aug 5, 2024
1 parent e1b850c commit d7f1d58
Show file tree
Hide file tree
Showing 24 changed files with 647 additions and 184 deletions.
1 change: 1 addition & 0 deletions src/components/all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@import './elements/LoadingIndicator';
@import './elements/Logo';
@import "./elements/NotifiCounter";
@import "./elements/Stub";
@import "./elements/Userpic";
@import "./elements/VerticalMenu";
@import "./elements/app/AppReminder";
Expand Down
9 changes: 0 additions & 9 deletions src/components/dialogs/LoginDialog/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import keyCodes from 'app/utils/keyCodes';
import { pageSession } from 'app/redux/UserSaga'

export function showLoginDialog(username, onClose, authType = 'active', saveLogin = false, hint = '') {
let dm, oldZ = ''

DialogManager.showDialog({
component: LoginDialog,
adaptive: true,
Expand All @@ -21,16 +19,9 @@ export function showLoginDialog(username, onClose, authType = 'active', saveLogi
hint,
},
onClose: (data) => {
if (dm) dm.style.zIndex = oldZ
if (onClose) onClose(data)
},
});

setTimeout(() => {
dm = document.getElementsByClassName('DialogManager')[0]
oldZ = dm ? dm.style.zIndex : ''
if (dm) dm.style.zIndex = 1000
}, 1)
}

export default class LoginDialog extends React.PureComponent {
Expand Down
125 changes: 125 additions & 0 deletions src/components/elements/Stub.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React from 'react'
import { connect } from 'react-redux'
import tt from 'counterpart'

import transaction from 'app/redux/TransactionReducer'
import { getRoleInGroup } from 'app/utils/groups'

class StubInner extends React.Component {
onBtnClick = (e) => {
e.preventDefault()
const { username, group, pending } = this.props
this.props.groupMember({
requester: username, group: group.name,
member: username,
member_type: pending ? 'retired' : 'pending',
onSuccess: () => {
},
onError: (err, errStr) => {
alert(errStr)
}
})
}

render() {
const { type, banned, notMember, pending } = this.props

const isCompose = type === 'compose'

let text, btn
if (banned) {
text = tt('stub_jsx.banned')
} else if (pending) {
text = tt('stub_jsx.pending')
text += ' '
btn = <a href='#' className='stub-btn alert' onClick={this.onBtnClick}>{tt('msgs_group_dropdown.cancel')}</a>
} else if (notMember) {
text = isCompose ? tt('stub_jsx.read_only') : tt('stub_jsx.private_group')
text += ' '
btn = <a href='#' className='stub-btn' onClick={this.onBtnClick}>{tt('stub_jsx.join')}</a>
}

if (isCompose) {
return <div className='msgs-compose-input compose-stub'>
{text}{btn}
</div>
} else {
return <div className='msgs-stub'>
{text}{btn}
</div>
}
}
}

const Stub = connect(
(state, ownProps) => {
const currentUser = state.user.get('current')

const username = state.user.getIn(['current', 'username'])

let the_group = state.global.get('the_group')
if (the_group && the_group.toJS) the_group = the_group.toJS()

return {
the_group,
username,
}
},
dispatch => ({
groupMember: ({ requester, group, member, member_type,
onSuccess, onError }) => {
const opData = {
requester,
name: group,
member,
member_type,
json_metadata: '{}',
extensions: [],
}

const plugin = 'private_message'
const json = JSON.stringify(['private_group_member', opData])

dispatch(transaction.actions.broadcastOperation({
type: 'custom_json',
operation: {
id: plugin,
required_posting_auths: [requester],
json,
},
username: requester,
successCallback: onSuccess,
errorCallback: (err, errStr) => {
console.error(err)
if (onError) onError(err, errStr)
},
}));
},
}),
)(StubInner)

export default Stub

export const renderStubs = (the_group, to, username) => {
let composeStub, msgsStub
if (!the_group || the_group.name !== to) {
return { composeStub, msgsStub}
}

const { privacy } = the_group
if (privacy !== 'public_group') {
const { amBanned, amMember, amModer, amPending } = getRoleInGroup(the_group, username)
const notMember = !amModer && !amMember
if (amBanned || notMember) {
composeStub = { ui: <Stub type='compose' banned={amBanned} notMember={notMember}
pending={amPending} group={the_group} /> }
if (privacy === 'private_group') {
composeStub = { disabled: true }
msgsStub = { ui: <Stub type='messages' banned={amBanned} notMember={notMember}
pending={amPending} group={the_group} /> }
}
}
}

return { composeStub, msgsStub}
}
14 changes: 14 additions & 0 deletions src/components/elements/Stub.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.compose-stub {
height: 60px !important;
padding-top: 0.75rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
padding-bottom: 0.5rem;

.stub-btn {
white-space: nowrap;
&.alert {
color: red;
}
}
}
2 changes: 1 addition & 1 deletion src/components/elements/common/DialogManager/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
left: 0;
right: 0;
bottom: 0;
z-index: 500;
z-index: 1000;

&__window {
position: absolute;
Expand Down
15 changes: 9 additions & 6 deletions src/components/elements/groups/GroupMember.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cn from 'classnames'
import Icon from 'app/components/elements/Icon'
import TimeAgoWrapper from 'app/components/elements/TimeAgoWrapper'
import Userpic from 'app/components/elements/Userpic'
import { getMemberType } from 'app/utils/groups'
import { getRoleInGroup } from 'app/utils/groups'

class GroupMember extends React.Component {
// shouldComponentUpdate(nextProps) {
Expand Down Expand Up @@ -54,10 +54,13 @@ class GroupMember extends React.Component {
render() {
const { member, username, currentGroup } = this.props
const { account, member_type, joined } = member
const { creatingNew, member_list } = currentGroup
const { creatingNew, } = currentGroup

const amOwner = currentGroup.owner === username
const amModer = amOwner || (member_list && getMemberType(member_list, username) === 'moder')
let { amOwner, amModer } = getRoleInGroup(currentGroup, username)
if (creatingNew) {
amOwner = true
amModer = true
}

const isMe = username === account
const isOwner = currentGroup.owner === account
Expand Down Expand Up @@ -86,7 +89,7 @@ class GroupMember extends React.Component {
if ((!isMe || isBanned) && amModer) {
banBtn = <Icon className={cn('member-btn ban', { selected: isBanned })}
title={banTitle} name='ionicons/ban' size='1_25x'
onClick={e => this.groupMember(e, member, 'banned')} />
onClick={e => this.groupMember(e, member, isBanned ? 'member' : 'banned')} />
}
} else {
deleteBtn = <Icon className={'member-btn delete'} title={tt('g.delete')} name='cross' size='1x'
Expand All @@ -112,7 +115,7 @@ class GroupMember extends React.Component {
{(amOwner || isMember) && <Icon className={cn('member-btn member', { selected: isMember })}
title={memberTitle}
name='ionicons/person' size='1_5x' onClick={e => this.groupMember(e, member, 'member')} />}
{(amOwner || isModer) && <Icon className={cn('member-btn moder', { selected: isModer })}
{(amOwner|| isModer) && <Icon className={cn('member-btn moder', { selected: isModer })}
title={moderTitle}
name='ionicons/person-add' size='1_5x' onClick={e => this.groupMember(e, member, 'moder')} />}
{banBtn}
Expand Down
29 changes: 19 additions & 10 deletions src/components/elements/messages/Compose/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import tt from 'counterpart';
import cn from 'classnames'
import { Picker } from 'emoji-picker-element';
import TextareaAutosize from 'react-textarea-autosize';

Expand Down Expand Up @@ -64,6 +65,10 @@ export default class Compose extends React.Component {

onEmojiClick = (event) => {
event.stopPropagation();

const { stub } = this.props
if (stub) return

this._tooltip.classList.toggle('shown');
if (!this._tooltip.classList.contains('shown')) {
const input = document.getElementsByClassName('msgs-compose-input')[0];
Expand Down Expand Up @@ -100,6 +105,7 @@ export default class Compose extends React.Component {

onEmojiSelect = (event) => {
event.stopPropagation();

this._tooltip.classList.toggle('shown');

const input = document.getElementsByClassName('msgs-compose-input')[0];
Expand Down Expand Up @@ -200,7 +206,7 @@ export default class Compose extends React.Component {
}

render() {
const { account, rightItems, replyingMessage } = this.props
const { account, rightItems, replyingMessage, stub } = this.props
const { onPanelDeleteClick, onPanelReplyClick, onPanelEditClick, onPanelCloseClick, onCancelReply } = this;

const selectedMessages = Object.entries(this.props.selectedMessages);
Expand All @@ -224,36 +230,39 @@ export default class Compose extends React.Component {
</div>);
}

const sendButton = selectedMessagesCount ? null :
(<button className='button small msgs-compose-send' title={tt('g.submit')}
onClick={this.onSendClick}
const sendButton = (selectedMessagesCount && !stub) ? null :
(<button className={cn('button small msgs-compose-send', {
disabled: !!stub
})} title={tt('g.submit')}
onClick={stub ? null : this.onSendClick}
>
<Icon name='new/envelope' size='1_25x' />
</button>);

return (
<div className='msgs-compose'>
{
!selectedMessagesCount ? rightItems : null
(!selectedMessagesCount || stub) ? rightItems : null
}

{!selectedMessagesCount ? (<div className='msgs-compose-input-panel'>
{quote}
<TextareaAutosize
{(!selectedMessagesCount || stub) ? (<div className='msgs-compose-input-panel'>
{stub ? null : quote}
{(stub && stub.ui) ? stub.ui : <TextareaAutosize
className='msgs-compose-input'
disabled={stub && stub.disabled}
placeholder={tt('messages.type_a_message')}
onKeyDown={this.onKeyDown}
onPaste={this.onPaste}
minRows={2}
maxRows={14}
onHeightChange={this.onHeightChange}
onChange={e => this.onChange(e.target.value)}
/>
/>}
</div>) : null}

{sendButton}

{selectedMessagesCount ? (<div className='msgs-compose-panel'>
{(selectedMessagesCount && !stub) ? (<div className='msgs-compose-panel'>
{(selectedMessagesCount === 1) ? (<button className='button small' onClick={onPanelReplyClick}>
<Icon name='reply' />
<span>{tt('g.reply')}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
transition: all 0.1s;
}

.msgs-toolbar-button:hover {
.msgs-toolbar-button:hover:not(.disabled) {
cursor: pointer;
color: #0063ce;
}

.msgs-toolbar-button:active {
.msgs-toolbar-button:active:not(.disabled) {
color: #007aff;
opacity: 0.25;
}
Loading

0 comments on commit d7f1d58

Please sign in to comment.