Messages permissions (#100)
This commit is contained in:
parent
1cda98f415
commit
f65a284953
|
@ -31,23 +31,28 @@ export const ROOM = createRequestTypes('ROOM', ['ADD_USER_TYPING', 'REMOVE_USER_
|
||||||
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
|
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
|
||||||
export const MESSAGES = createRequestTypes('MESSAGES', [
|
export const MESSAGES = createRequestTypes('MESSAGES', [
|
||||||
...defaultTypes,
|
...defaultTypes,
|
||||||
|
'ACTIONS_SHOW',
|
||||||
|
'ACTIONS_HIDE',
|
||||||
'DELETE_REQUEST',
|
'DELETE_REQUEST',
|
||||||
'DELETE_SUCCESS',
|
'DELETE_SUCCESS',
|
||||||
'DELETE_FAILURE',
|
'DELETE_FAILURE',
|
||||||
'EDIT_INIT',
|
'EDIT_INIT',
|
||||||
|
'EDIT_CANCEL',
|
||||||
'EDIT_REQUEST',
|
'EDIT_REQUEST',
|
||||||
'EDIT_SUCCESS',
|
'EDIT_SUCCESS',
|
||||||
'EDIT_FAILURE',
|
'EDIT_FAILURE',
|
||||||
'STAR_REQUEST',
|
'TOGGLE_STAR_REQUEST',
|
||||||
'STAR_SUCCESS',
|
'TOGGLE_STAR_SUCCESS',
|
||||||
'STAR_FAILURE',
|
'TOGGLE_STAR_FAILURE',
|
||||||
'PERMALINK_REQUEST',
|
'PERMALINK_REQUEST',
|
||||||
'PERMALINK_SUCCESS',
|
'PERMALINK_SUCCESS',
|
||||||
'PERMALINK_FAILURE',
|
'PERMALINK_FAILURE',
|
||||||
|
'PERMALINK_CLEAR',
|
||||||
'TOGGLE_PIN_REQUEST',
|
'TOGGLE_PIN_REQUEST',
|
||||||
'TOGGLE_PIN_SUCCESS',
|
'TOGGLE_PIN_SUCCESS',
|
||||||
'TOGGLE_PIN_FAILURE',
|
'TOGGLE_PIN_FAILURE',
|
||||||
'SET_INPUT'
|
'SET_INPUT',
|
||||||
|
'CLEAR_INPUT'
|
||||||
]);
|
]);
|
||||||
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [
|
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [
|
||||||
...defaultTypes,
|
...defaultTypes,
|
||||||
|
|
|
@ -25,6 +25,14 @@ export function setAllSettings(settings) {
|
||||||
payload: settings
|
payload: settings
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setAllPermissions(permissions) {
|
||||||
|
return {
|
||||||
|
type: types.SET_ALL_PERMISSIONS,
|
||||||
|
payload: permissions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function login() {
|
export function login() {
|
||||||
return {
|
return {
|
||||||
type: 'LOGIN'
|
type: 'LOGIN'
|
||||||
|
|
|
@ -20,6 +20,19 @@ export function messagesFailure(err) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function actionsShow(actionMessage) {
|
||||||
|
return {
|
||||||
|
type: types.MESSAGES.ACTIONS_SHOW,
|
||||||
|
actionMessage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function actionsHide() {
|
||||||
|
return {
|
||||||
|
type: types.MESSAGES.ACTIONS_HIDE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function deleteRequest(message) {
|
export function deleteRequest(message) {
|
||||||
return {
|
return {
|
||||||
type: types.MESSAGES.DELETE_REQUEST,
|
type: types.MESSAGES.DELETE_REQUEST,
|
||||||
|
@ -47,6 +60,12 @@ export function editInit(message) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function editCancel() {
|
||||||
|
return {
|
||||||
|
type: types.MESSAGES.EDIT_CANCEL
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function editRequest(message) {
|
export function editRequest(message) {
|
||||||
return {
|
return {
|
||||||
type: types.MESSAGES.EDIT_REQUEST,
|
type: types.MESSAGES.EDIT_REQUEST,
|
||||||
|
@ -66,22 +85,22 @@ export function editFailure() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function starRequest(message) {
|
export function toggleStarRequest(message) {
|
||||||
return {
|
return {
|
||||||
type: types.MESSAGES.STAR_REQUEST,
|
type: types.MESSAGES.TOGGLE_STAR_REQUEST,
|
||||||
message
|
message
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function starSuccess() {
|
export function toggleStarSuccess() {
|
||||||
return {
|
return {
|
||||||
type: types.MESSAGES.STAR_SUCCESS
|
type: types.MESSAGES.TOGGLE_STAR_SUCCESS
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function starFailure() {
|
export function toggleStarFailure() {
|
||||||
return {
|
return {
|
||||||
type: types.MESSAGES.STAR_FAILURE
|
type: types.MESSAGES.TOGGLE_STAR_FAILURE
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +125,12 @@ export function permalinkFailure(err) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function permalinkClear() {
|
||||||
|
return {
|
||||||
|
type: types.MESSAGES.PERMALINK_CLEAR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function togglePinRequest(message) {
|
export function togglePinRequest(message) {
|
||||||
return {
|
return {
|
||||||
type: types.MESSAGES.TOGGLE_PIN_REQUEST,
|
type: types.MESSAGES.TOGGLE_PIN_REQUEST,
|
||||||
|
@ -132,3 +157,9 @@ export function setInput(message) {
|
||||||
message
|
message
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function clearInput() {
|
||||||
|
return {
|
||||||
|
type: types.MESSAGES.CLEAR_INPUT
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export const SET_CURRENT_SERVER = 'SET_CURRENT_SERVER';
|
export const SET_CURRENT_SERVER = 'SET_CURRENT_SERVER';
|
||||||
export const SET_ALL_SETTINGS = 'SET_ALL_SETTINGS';
|
export const SET_ALL_SETTINGS = 'SET_ALL_SETTINGS';
|
||||||
|
export const SET_ALL_PERMISSIONS = 'SET_ALL_PERMISSIONS';
|
||||||
|
|
|
@ -0,0 +1,308 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Alert, Clipboard } from 'react-native';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ActionSheet from 'react-native-actionsheet';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
import {
|
||||||
|
deleteRequest,
|
||||||
|
editInit,
|
||||||
|
toggleStarRequest,
|
||||||
|
permalinkRequest,
|
||||||
|
permalinkClear,
|
||||||
|
togglePinRequest,
|
||||||
|
setInput,
|
||||||
|
actionsHide
|
||||||
|
} from '../actions/messages';
|
||||||
|
|
||||||
|
@connect(
|
||||||
|
state => ({
|
||||||
|
showActions: state.messages.showActions,
|
||||||
|
actionMessage: state.messages.actionMessage,
|
||||||
|
user: state.login.user,
|
||||||
|
permissions: state.permissions,
|
||||||
|
permalink: state.messages.permalink,
|
||||||
|
Message_AllowDeleting: state.settings.Message_AllowDeleting,
|
||||||
|
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes,
|
||||||
|
Message_AllowEditing: state.settings.Message_AllowEditing,
|
||||||
|
Message_AllowEditing_BlockEditInMinutes: state.settings.Message_AllowEditing_BlockEditInMinutes,
|
||||||
|
Message_AllowPinning: state.settings.Message_AllowPinning,
|
||||||
|
Message_AllowStarring: state.settings.Message_AllowStarring
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
actionsHide: () => dispatch(actionsHide()),
|
||||||
|
deleteRequest: message => dispatch(deleteRequest(message)),
|
||||||
|
editInit: message => dispatch(editInit(message)),
|
||||||
|
toggleStarRequest: message => dispatch(toggleStarRequest(message)),
|
||||||
|
permalinkRequest: message => dispatch(permalinkRequest(message)),
|
||||||
|
permalinkClear: () => dispatch(permalinkClear()),
|
||||||
|
togglePinRequest: message => dispatch(togglePinRequest(message)),
|
||||||
|
setInput: message => dispatch(setInput(message))
|
||||||
|
})
|
||||||
|
)
|
||||||
|
export default class MessageActions extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
actionsHide: PropTypes.func.isRequired,
|
||||||
|
showActions: PropTypes.bool.isRequired,
|
||||||
|
room: PropTypes.object,
|
||||||
|
actionMessage: PropTypes.object,
|
||||||
|
user: PropTypes.object,
|
||||||
|
permissions: PropTypes.object.isRequired,
|
||||||
|
deleteRequest: PropTypes.func.isRequired,
|
||||||
|
editInit: PropTypes.func.isRequired,
|
||||||
|
toggleStarRequest: PropTypes.func.isRequired,
|
||||||
|
permalinkRequest: PropTypes.func.isRequired,
|
||||||
|
permalinkClear: PropTypes.func.isRequired,
|
||||||
|
togglePinRequest: PropTypes.func.isRequired,
|
||||||
|
setInput: PropTypes.func.isRequired,
|
||||||
|
permalink: PropTypes.string,
|
||||||
|
Message_AllowDeleting: PropTypes.bool,
|
||||||
|
Message_AllowDeleting_BlockDeleteInMinutes: PropTypes.number,
|
||||||
|
Message_AllowEditing: PropTypes.bool,
|
||||||
|
Message_AllowEditing_BlockEditInMinutes: PropTypes.number,
|
||||||
|
Message_AllowPinning: PropTypes.bool,
|
||||||
|
Message_AllowStarring: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
copyPermalink: false,
|
||||||
|
reply: false,
|
||||||
|
quote: false
|
||||||
|
};
|
||||||
|
this.handleActionPress = this.handleActionPress.bind(this);
|
||||||
|
this.options = [''];
|
||||||
|
const { roles } = this.props.room[0];
|
||||||
|
const roomRoles = Array.from(Object.keys(roles), i => roles[i].value);
|
||||||
|
const userRoles = this.props.user.roles || [];
|
||||||
|
this.mergedRoles = [...new Set([...roomRoles, ...userRoles])];
|
||||||
|
this.setPermissions(this.props.permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.showActions !== this.props.showActions && nextProps.showActions) {
|
||||||
|
const { actionMessage } = nextProps;
|
||||||
|
// Cancel
|
||||||
|
this.options = ['Cancel'];
|
||||||
|
this.CANCEL_INDEX = 0;
|
||||||
|
// Reply
|
||||||
|
this.options.push('Reply');
|
||||||
|
this.REPLY_INDEX = this.options.length - 1;
|
||||||
|
// Edit
|
||||||
|
if (this.allowEdit(nextProps)) {
|
||||||
|
this.options.push('Edit');
|
||||||
|
this.EDIT_INDEX = this.options.length - 1;
|
||||||
|
}
|
||||||
|
// Permalink
|
||||||
|
this.options.push('Copy Permalink');
|
||||||
|
this.PERMALINK_INDEX = this.options.length - 1;
|
||||||
|
// Copy
|
||||||
|
this.options.push('Copy Message');
|
||||||
|
this.COPY_INDEX = this.options.length - 1;
|
||||||
|
// Quote
|
||||||
|
this.options.push('Quote');
|
||||||
|
this.QUOTE_INDEX = this.options.length - 1;
|
||||||
|
// Star
|
||||||
|
if (this.props.Message_AllowStarring) {
|
||||||
|
this.options.push(actionMessage.starred ? 'Unstar' : 'Star');
|
||||||
|
this.STAR_INDEX = this.options.length - 1;
|
||||||
|
}
|
||||||
|
// Pin
|
||||||
|
if (this.props.Message_AllowPinning) {
|
||||||
|
this.options.push(actionMessage.pinned ? 'Unpin' : 'Pin');
|
||||||
|
this.PIN_INDEX = this.options.length - 1;
|
||||||
|
}
|
||||||
|
// Delete
|
||||||
|
if (this.allowDelete(nextProps)) {
|
||||||
|
this.options.push('Delete');
|
||||||
|
this.DELETE_INDEX = this.options.length - 1;
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.ActionSheet.show();
|
||||||
|
});
|
||||||
|
} else if (this.props.permalink !== nextProps.permalink && nextProps.permalink) {
|
||||||
|
// copy permalink
|
||||||
|
if (this.state.copyPermalink) {
|
||||||
|
this.setState({ copyPermalink: false });
|
||||||
|
await Clipboard.setString(nextProps.permalink);
|
||||||
|
Alert.alert('Permalink copied to clipboard!');
|
||||||
|
this.props.permalinkClear();
|
||||||
|
// quote
|
||||||
|
} else if (this.state.quote) {
|
||||||
|
this.setState({ quote: false });
|
||||||
|
const msg = `[ ](${ nextProps.permalink }) `;
|
||||||
|
this.props.setInput({ msg });
|
||||||
|
|
||||||
|
// reply
|
||||||
|
} else if (this.state.reply) {
|
||||||
|
this.setState({ reply: false });
|
||||||
|
let msg = `[ ](${ nextProps.permalink }) `;
|
||||||
|
|
||||||
|
// if original message wasn't sent by current user and neither from a direct room
|
||||||
|
if (this.props.user.username !== this.props.actionMessage.u.username && this.props.room[0].t !== 'd') {
|
||||||
|
msg += `@${ this.props.actionMessage.u.username } `;
|
||||||
|
}
|
||||||
|
this.props.setInput({ msg });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.setPermissions(this.props.permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPermissions(permissions) {
|
||||||
|
this.hasEditPermission = permissions['edit-message']
|
||||||
|
.some(item => this.mergedRoles.indexOf(item) !== -1);
|
||||||
|
this.hasDeletePermission = permissions['delete-message']
|
||||||
|
.some(item => this.mergedRoles.indexOf(item) !== -1);
|
||||||
|
this.hasForceDeletePermission = permissions['force-delete-message']
|
||||||
|
.some(item => this.mergedRoles.indexOf(item) !== -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
isOwn = props => props.actionMessage.u && props.actionMessage.u._id === props.user.id;
|
||||||
|
|
||||||
|
allowEdit = (props) => {
|
||||||
|
const editOwn = this.isOwn(props);
|
||||||
|
const { Message_AllowEditing: isEditAllowed } = this.props;
|
||||||
|
if (!(this.hasEditPermission || (isEditAllowed && editOwn))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const blockEditInMinutes = this.props.Message_AllowEditing_BlockEditInMinutes;
|
||||||
|
if (blockEditInMinutes) {
|
||||||
|
let msgTs;
|
||||||
|
if (props.actionMessage.ts != null) {
|
||||||
|
msgTs = moment(props.actionMessage.ts);
|
||||||
|
}
|
||||||
|
let currentTsDiff;
|
||||||
|
if (msgTs != null) {
|
||||||
|
currentTsDiff = moment().diff(msgTs, 'minutes');
|
||||||
|
}
|
||||||
|
return currentTsDiff < blockEditInMinutes;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
allowDelete = (props) => {
|
||||||
|
const deleteOwn = this.isOwn(props);
|
||||||
|
const { Message_AllowDeleting: isDeleteAllowed } = this.props;
|
||||||
|
if (!(this.hasDeletePermission || (isDeleteAllowed && deleteOwn) || this.hasForceDeletePermission)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.hasForceDeletePermission) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const blockDeleteInMinutes = this.props.Message_AllowDeleting_BlockDeleteInMinutes;
|
||||||
|
if (blockDeleteInMinutes != null && blockDeleteInMinutes !== 0) {
|
||||||
|
let msgTs;
|
||||||
|
if (props.actionMessage.ts != null) {
|
||||||
|
msgTs = moment(props.actionMessage.ts);
|
||||||
|
}
|
||||||
|
let currentTsDiff;
|
||||||
|
if (msgTs != null) {
|
||||||
|
currentTsDiff = moment().diff(msgTs, 'minutes');
|
||||||
|
}
|
||||||
|
return currentTsDiff < blockDeleteInMinutes;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDelete() {
|
||||||
|
Alert.alert(
|
||||||
|
'Are you sure?',
|
||||||
|
'You will not be able to recover this message!',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: 'Cancel',
|
||||||
|
style: 'cancel'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Yes, delete it!',
|
||||||
|
style: 'destructive',
|
||||||
|
onPress: () => this.props.deleteRequest(this.props.actionMessage)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ cancelable: false }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEdit() {
|
||||||
|
const { _id, msg, rid } = this.props.actionMessage;
|
||||||
|
this.props.editInit({ _id, msg, rid });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCopy = async() => {
|
||||||
|
await Clipboard.setString(this.props.actionMessage.msg);
|
||||||
|
Alert.alert('Copied to clipboard!');
|
||||||
|
}
|
||||||
|
|
||||||
|
handleStar() {
|
||||||
|
this.props.toggleStarRequest(this.props.actionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePermalink() {
|
||||||
|
this.setState({ copyPermalink: true });
|
||||||
|
this.props.permalinkRequest(this.props.actionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePin() {
|
||||||
|
this.props.togglePinRequest(this.props.actionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReply() {
|
||||||
|
this.setState({ reply: true });
|
||||||
|
this.props.permalinkRequest(this.props.actionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleQuote() {
|
||||||
|
this.setState({ quote: true });
|
||||||
|
this.props.permalinkRequest(this.props.actionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleActionPress = (actionIndex) => {
|
||||||
|
switch (actionIndex) {
|
||||||
|
case this.REPLY_INDEX:
|
||||||
|
this.handleReply();
|
||||||
|
break;
|
||||||
|
case this.EDIT_INDEX:
|
||||||
|
this.handleEdit();
|
||||||
|
break;
|
||||||
|
case this.PERMALINK_INDEX:
|
||||||
|
this.handlePermalink();
|
||||||
|
break;
|
||||||
|
case this.COPY_INDEX:
|
||||||
|
this.handleCopy();
|
||||||
|
break;
|
||||||
|
case this.QUOTE_INDEX:
|
||||||
|
this.handleQuote();
|
||||||
|
break;
|
||||||
|
case this.STAR_INDEX:
|
||||||
|
this.handleStar();
|
||||||
|
break;
|
||||||
|
case this.PIN_INDEX:
|
||||||
|
this.handlePin();
|
||||||
|
break;
|
||||||
|
case this.DELETE_INDEX:
|
||||||
|
this.handleDelete();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.props.actionsHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ActionSheet
|
||||||
|
ref={o => this.ActionSheet = o}
|
||||||
|
title='Messages actions'
|
||||||
|
options={this.options}
|
||||||
|
cancelButtonIndex={this.CANCEL_INDEX}
|
||||||
|
destructiveButtonIndex={this.DELETE_INDEX}
|
||||||
|
onPress={this.handleActionPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import ImagePicker from 'react-native-image-picker';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { userTyping } from '../actions/room';
|
import { userTyping } from '../actions/room';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import { editRequest } from '../actions/messages';
|
import { editRequest, editCancel, clearInput } from '../actions/messages';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
textBox: {
|
textBox: {
|
||||||
|
@ -25,7 +25,7 @@ const styles = StyleSheet.create({
|
||||||
alignSelf: 'stretch',
|
alignSelf: 'stretch',
|
||||||
flexGrow: 1
|
flexGrow: 1
|
||||||
},
|
},
|
||||||
fileButton: {
|
actionButtons: {
|
||||||
color: '#aaa',
|
color: '#aaa',
|
||||||
paddingTop: 10,
|
paddingTop: 10,
|
||||||
paddingBottom: 10,
|
paddingBottom: 10,
|
||||||
|
@ -40,23 +40,29 @@ const styles = StyleSheet.create({
|
||||||
message: state.messages.message,
|
message: state.messages.message,
|
||||||
editing: state.messages.editing
|
editing: state.messages.editing
|
||||||
}), dispatch => ({
|
}), dispatch => ({
|
||||||
|
editCancel: () => dispatch(editCancel()),
|
||||||
editRequest: message => dispatch(editRequest(message)),
|
editRequest: message => dispatch(editRequest(message)),
|
||||||
typing: status => dispatch(userTyping(status))
|
typing: status => dispatch(userTyping(status)),
|
||||||
|
clearInput: () => dispatch(clearInput())
|
||||||
}))
|
}))
|
||||||
export default class MessageBox extends React.Component {
|
export default class MessageBox extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onSubmit: PropTypes.func.isRequired,
|
onSubmit: PropTypes.func.isRequired,
|
||||||
rid: PropTypes.string.isRequired,
|
rid: PropTypes.string.isRequired,
|
||||||
|
editCancel: PropTypes.func.isRequired,
|
||||||
editRequest: PropTypes.func.isRequired,
|
editRequest: PropTypes.func.isRequired,
|
||||||
message: PropTypes.object,
|
message: PropTypes.object,
|
||||||
editing: PropTypes.bool,
|
editing: PropTypes.bool,
|
||||||
typing: PropTypes.func
|
typing: PropTypes.func,
|
||||||
|
clearInput: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (this.props.message !== nextProps.message) {
|
if (this.props.message !== nextProps.message && nextProps.message) {
|
||||||
this.component.setNativeProps({ text: nextProps.message.msg });
|
this.component.setNativeProps({ text: nextProps.message.msg });
|
||||||
this.component.focus();
|
this.component.focus();
|
||||||
|
} else if (!nextProps.message) {
|
||||||
|
this.component.setNativeProps({ text: '' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +81,7 @@ export default class MessageBox extends React.Component {
|
||||||
// if is submiting a new message
|
// if is submiting a new message
|
||||||
this.props.onSubmit(message);
|
this.props.onSubmit(message);
|
||||||
}
|
}
|
||||||
|
this.props.clearInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
addFile = () => {
|
addFile = () => {
|
||||||
|
@ -104,10 +111,24 @@ export default class MessageBox extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editCancel() {
|
||||||
|
this.props.editCancel();
|
||||||
|
this.component.setNativeProps({ text: '' });
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLeftButton() {
|
||||||
|
const { editing } = this.props;
|
||||||
|
if (editing) {
|
||||||
|
return <Icon style={styles.actionButtons} name='close' onPress={() => this.editCancel()} />;
|
||||||
|
}
|
||||||
|
return <Icon style={styles.actionButtons} name='add-circle-outline' onPress={this.addFile} />;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style={[styles.textBox, (this.props.editing ? styles.editing : null)]}>
|
<View style={[styles.textBox, (this.props.editing ? styles.editing : null)]}>
|
||||||
<SafeAreaView style={styles.safeAreaView}>
|
<SafeAreaView style={styles.safeAreaView}>
|
||||||
|
{this.renderLeftButton()}
|
||||||
<TextInput
|
<TextInput
|
||||||
ref={component => this.component = component}
|
ref={component => this.component = component}
|
||||||
style={styles.textBoxInput}
|
style={styles.textBoxInput}
|
||||||
|
@ -119,7 +140,6 @@ export default class MessageBox extends React.Component {
|
||||||
underlineColorAndroid='transparent'
|
underlineColorAndroid='transparent'
|
||||||
defaultValue=''
|
defaultValue=''
|
||||||
/>
|
/>
|
||||||
<Icon style={styles.fileButton} name='add-circle-outline' onPress={this.addFile} />
|
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,7 +31,7 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class Message extends React.PureComponent {
|
export default class User extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
item: PropTypes.object.isRequired,
|
item: PropTypes.object.isRequired,
|
||||||
Message_TimeFormat: PropTypes.string.isRequired,
|
Message_TimeFormat: PropTypes.string.isRequired,
|
||||||
|
|
|
@ -1,28 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View, StyleSheet, TouchableOpacity, Text, Alert, Clipboard } from 'react-native';
|
import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
|
||||||
import { emojify } from 'react-emojione';
|
import { emojify } from 'react-emojione';
|
||||||
import Markdown from 'react-native-easy-markdown'; // eslint-disable-line
|
import Markdown from 'react-native-easy-markdown'; // eslint-disable-line
|
||||||
import ActionSheet from 'react-native-actionsheet';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { actionsShow } from '../../actions/messages';
|
||||||
import Card from './Card';
|
import Card from './Card';
|
||||||
import User from './User';
|
import User from './User';
|
||||||
import Avatar from '../Avatar';
|
import Avatar from '../Avatar';
|
||||||
import {
|
|
||||||
deleteRequest,
|
|
||||||
editInit,
|
|
||||||
starRequest,
|
|
||||||
permalinkRequest,
|
|
||||||
togglePinRequest,
|
|
||||||
setInput
|
|
||||||
} from '../../actions/messages';
|
|
||||||
import RocketChat from '../../lib/rocketchat';
|
|
||||||
|
|
||||||
const title = 'Message actions';
|
|
||||||
const options = ['Cancel', 'Reply', 'Edit', 'Permalink', 'Copy', 'Quote', 'Star Message', 'Pin Message', 'Delete'];
|
|
||||||
const CANCEL_INDEX = 0;
|
|
||||||
const DESTRUCTIVE_INDEX = 8;
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
content: {
|
content: {
|
||||||
|
@ -47,70 +33,23 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
message: state.messages.message,
|
message: state.messages.message,
|
||||||
permalink: state.messages.permalink,
|
editing: state.messages.editing
|
||||||
user: state.login.user
|
|
||||||
}), dispatch => ({
|
}), dispatch => ({
|
||||||
deleteRequest: message => dispatch(deleteRequest(message)),
|
actionsShow: actionMessage => dispatch(actionsShow(actionMessage))
|
||||||
editInit: message => dispatch(editInit(message)),
|
|
||||||
starRequest: message => dispatch(starRequest(message)),
|
|
||||||
permalinkRequest: message => dispatch(permalinkRequest(message)),
|
|
||||||
togglePinRequest: message => dispatch(togglePinRequest(message)),
|
|
||||||
setInput: message => dispatch(setInput(message))
|
|
||||||
}))
|
}))
|
||||||
export default class Message extends React.Component {
|
export default class Message extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
item: PropTypes.object.isRequired,
|
item: PropTypes.object.isRequired,
|
||||||
baseUrl: PropTypes.string.isRequired,
|
baseUrl: PropTypes.string.isRequired,
|
||||||
Message_TimeFormat: PropTypes.string.isRequired,
|
Message_TimeFormat: PropTypes.string.isRequired,
|
||||||
deleteRequest: PropTypes.func.isRequired,
|
message: PropTypes.object.isRequired,
|
||||||
editInit: PropTypes.func.isRequired,
|
editing: PropTypes.bool,
|
||||||
starRequest: PropTypes.func.isRequired,
|
actionsShow: PropTypes.func
|
||||||
permalinkRequest: PropTypes.func.isRequired,
|
|
||||||
togglePinRequest: PropTypes.func.isRequired,
|
|
||||||
setInput: PropTypes.func.isRequired,
|
|
||||||
user: PropTypes.object.isRequired,
|
|
||||||
message: PropTypes.object,
|
|
||||||
permalink: PropTypes.string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
onLongPress() {
|
||||||
super(props);
|
const { item } = this.props;
|
||||||
this.state = {
|
this.props.actionsShow(JSON.parse(JSON.stringify(item)));
|
||||||
copyPermalink: false,
|
|
||||||
reply: false,
|
|
||||||
quote: false
|
|
||||||
};
|
|
||||||
this.handleActionPress = this.handleActionPress.bind(this);
|
|
||||||
this.showActions = this.showActions.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
async componentWillReceiveProps(nextProps) {
|
|
||||||
if (this.props.permalink !== nextProps.permalink) {
|
|
||||||
// copy permalink
|
|
||||||
if (this.state.copyPermalink) {
|
|
||||||
this.setState({ copyPermalink: false });
|
|
||||||
await Clipboard.setString(nextProps.permalink);
|
|
||||||
Alert.alert('Permalink copied to clipboard!');
|
|
||||||
|
|
||||||
// quote
|
|
||||||
} else if (this.state.quote) {
|
|
||||||
this.setState({ quote: false });
|
|
||||||
const msg = `[ ](${ nextProps.permalink }) `;
|
|
||||||
this.props.setInput({ msg });
|
|
||||||
|
|
||||||
// reply
|
|
||||||
} else if (this.state.reply) {
|
|
||||||
this.setState({ reply: false });
|
|
||||||
let msg = `[ ](${ nextProps.permalink }) `;
|
|
||||||
const room = await RocketChat.getRoom(this.props.item.rid);
|
|
||||||
|
|
||||||
// if original message wasn't sent by current user and neither from a direct room
|
|
||||||
if (this.props.user.username !== this.props.item.u.username && room.t !== 'd') {
|
|
||||||
msg += `@${ this.props.item.u.username } `;
|
|
||||||
}
|
|
||||||
this.props.setInput({ msg });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isDeleted() {
|
isDeleted() {
|
||||||
|
@ -125,90 +64,6 @@ export default class Message extends React.Component {
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
showActions = () => {
|
|
||||||
this.ActionSheet.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDelete() {
|
|
||||||
Alert.alert(
|
|
||||||
'Are you sure?',
|
|
||||||
'You will not be able to recover this message!',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: 'Cancel',
|
|
||||||
style: 'cancel'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Yes, delete it!',
|
|
||||||
style: 'destructive',
|
|
||||||
onPress: () => this.props.deleteRequest(this.props.item)
|
|
||||||
}
|
|
||||||
],
|
|
||||||
{ cancelable: false }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEdit() {
|
|
||||||
const { _id, msg, rid } = this.props.item;
|
|
||||||
this.props.editInit({ _id, msg, rid });
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCopy = async() => {
|
|
||||||
await Clipboard.setString(this.props.item.msg);
|
|
||||||
Alert.alert('Copied to clipboard!');
|
|
||||||
}
|
|
||||||
|
|
||||||
handleStar() {
|
|
||||||
this.props.starRequest(this.props.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePermalink() {
|
|
||||||
this.setState({ copyPermalink: true });
|
|
||||||
this.props.permalinkRequest(this.props.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleTogglePin() {
|
|
||||||
this.props.togglePinRequest(this.props.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleReply() {
|
|
||||||
this.setState({ reply: true });
|
|
||||||
this.props.permalinkRequest(this.props.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleQuote() {
|
|
||||||
this.setState({ quote: true });
|
|
||||||
this.props.permalinkRequest(this.props.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleActionPress = (actionIndex) => {
|
|
||||||
// reply
|
|
||||||
if (actionIndex === 1) {
|
|
||||||
this.handleReply();
|
|
||||||
// edit
|
|
||||||
} else if (actionIndex === 2) {
|
|
||||||
this.handleEdit();
|
|
||||||
// permalink
|
|
||||||
} else if (actionIndex === 3) {
|
|
||||||
this.handlePermalink();
|
|
||||||
// copy
|
|
||||||
} else if (actionIndex === 4) {
|
|
||||||
this.handleCopy();
|
|
||||||
// quote
|
|
||||||
} else if (actionIndex === 5) {
|
|
||||||
this.handleQuote();
|
|
||||||
// star
|
|
||||||
} else if (actionIndex === 6) {
|
|
||||||
this.handleStar();
|
|
||||||
// toggle pin
|
|
||||||
} else if (actionIndex === 7) {
|
|
||||||
this.handleTogglePin();
|
|
||||||
// delete
|
|
||||||
} else if (actionIndex === 8) {
|
|
||||||
this.handleDelete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMessageContent() {
|
renderMessageContent() {
|
||||||
if (this.isDeleted()) {
|
if (this.isDeleted()) {
|
||||||
return <Text style={styles.textInfo}>Message removed</Text>;
|
return <Text style={styles.textInfo}>Message removed</Text>;
|
||||||
|
@ -223,7 +78,9 @@ export default class Message extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { item } = this.props;
|
const {
|
||||||
|
item, message, editing
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
const extraStyle = {};
|
const extraStyle = {};
|
||||||
if (item.temp) {
|
if (item.temp) {
|
||||||
|
@ -231,40 +88,30 @@ export default class Message extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const username = item.alias || item.u.username;
|
const username = item.alias || item.u.username;
|
||||||
const isEditing = this.props.message._id === item._id;
|
const isEditing = message._id === item._id && editing;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onLongPress={() => this.showActions()}
|
onLongPress={() => this.onLongPress()}
|
||||||
disabled={this.isDeleted()}
|
disabled={this.isDeleted()}
|
||||||
style={isEditing ? styles.editing : null}
|
style={[styles.message, extraStyle, isEditing ? styles.editing : null]}
|
||||||
>
|
>
|
||||||
<View style={[styles.message, extraStyle]}>
|
<Avatar
|
||||||
<Avatar
|
style={{ marginRight: 10 }}
|
||||||
style={{ marginRight: 10 }}
|
text={item.avatar ? '' : username}
|
||||||
text={item.avatar ? '' : username}
|
size={40}
|
||||||
size={40}
|
baseUrl={this.props.baseUrl}
|
||||||
|
avatar={item.avatar}
|
||||||
|
/>
|
||||||
|
<View style={[styles.content]}>
|
||||||
|
<User
|
||||||
|
onPress={this._onPress}
|
||||||
|
item={item}
|
||||||
|
Message_TimeFormat={this.props.Message_TimeFormat}
|
||||||
baseUrl={this.props.baseUrl}
|
baseUrl={this.props.baseUrl}
|
||||||
avatar={item.avatar}
|
|
||||||
/>
|
|
||||||
<View style={[styles.content]}>
|
|
||||||
<User
|
|
||||||
onPress={this._onPress}
|
|
||||||
item={item}
|
|
||||||
Message_TimeFormat={this.props.Message_TimeFormat}
|
|
||||||
baseUrl={this.props.baseUrl}
|
|
||||||
/>
|
|
||||||
{this.attachments()}
|
|
||||||
{this.renderMessageContent(item)}
|
|
||||||
</View>
|
|
||||||
<ActionSheet
|
|
||||||
ref={o => this.ActionSheet = o}
|
|
||||||
title={title}
|
|
||||||
options={options}
|
|
||||||
cancelButtonIndex={CANCEL_INDEX}
|
|
||||||
destructiveButtonIndex={DESTRUCTIVE_INDEX}
|
|
||||||
onPress={this.handleActionPress}
|
|
||||||
/>
|
/>
|
||||||
|
{this.attachments()}
|
||||||
|
{this.renderMessageContent(item)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,6 +24,24 @@ const settingsSchema = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const permissionsRolesSchema = {
|
||||||
|
name: 'permissionsRoles',
|
||||||
|
properties: {
|
||||||
|
value: 'string'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const permissionsSchema = {
|
||||||
|
name: 'permissions',
|
||||||
|
primaryKey: '_id',
|
||||||
|
properties: {
|
||||||
|
_id: 'string',
|
||||||
|
_server: 'servers',
|
||||||
|
roles: { type: 'list', objectType: 'permissionsRoles' },
|
||||||
|
_updatedAt: { type: 'date', optional: true }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const roomsSchema = {
|
const roomsSchema = {
|
||||||
name: 'rooms',
|
name: 'rooms',
|
||||||
primaryKey: '_id',
|
primaryKey: '_id',
|
||||||
|
@ -35,6 +53,13 @@ const roomsSchema = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const subscriptionRolesSchema = {
|
||||||
|
name: 'subscriptionRolesSchema',
|
||||||
|
properties: {
|
||||||
|
value: 'string'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const subscriptionSchema = {
|
const subscriptionSchema = {
|
||||||
name: 'subscriptions',
|
name: 'subscriptions',
|
||||||
primaryKey: '_id',
|
primaryKey: '_id',
|
||||||
|
@ -50,7 +75,7 @@ const subscriptionSchema = {
|
||||||
rid: 'string',
|
rid: 'string',
|
||||||
open: { type: 'bool', optional: true },
|
open: { type: 'bool', optional: true },
|
||||||
alert: { type: 'bool', optional: true },
|
alert: { type: 'bool', optional: true },
|
||||||
// roles: [ 'owner' ],
|
roles: { type: 'list', objectType: 'subscriptionRolesSchema' },
|
||||||
unread: { type: 'int', optional: true },
|
unread: { type: 'int', optional: true },
|
||||||
userMentions: { type: 'int', optional: true },
|
userMentions: { type: 'int', optional: true },
|
||||||
// userMentions: 0,
|
// userMentions: 0,
|
||||||
|
@ -128,11 +153,14 @@ const realm = new Realm({
|
||||||
settingsSchema,
|
settingsSchema,
|
||||||
serversSchema,
|
serversSchema,
|
||||||
subscriptionSchema,
|
subscriptionSchema,
|
||||||
|
subscriptionRolesSchema,
|
||||||
messagesSchema,
|
messagesSchema,
|
||||||
usersSchema,
|
usersSchema,
|
||||||
roomsSchema,
|
roomsSchema,
|
||||||
attachment,
|
attachment,
|
||||||
messagesEditedBySchema
|
messagesEditedBySchema,
|
||||||
|
permissionsSchema,
|
||||||
|
permissionsRolesSchema
|
||||||
],
|
],
|
||||||
deleteRealmIfMigrationNeeded: true
|
deleteRealmIfMigrationNeeded: true
|
||||||
});
|
});
|
||||||
|
|
|
@ -70,7 +70,7 @@ const RocketChat = {
|
||||||
message.temp = false;
|
message.temp = false;
|
||||||
message._server = server;
|
message._server = server;
|
||||||
message.attachments = message.attachments || [];
|
message.attachments = message.attachments || [];
|
||||||
message.starred = !!message.starred;
|
message.starred = message.starred && message.starred.length > 0;
|
||||||
realm.create('messages', message, true);
|
realm.create('messages', message, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,9 @@ const RocketChat = {
|
||||||
const [type, data] = ddpMessage.fields.args;
|
const [type, data] = ddpMessage.fields.args;
|
||||||
const [, ev] = ddpMessage.fields.eventName.split('/');
|
const [, ev] = ddpMessage.fields.eventName.split('/');
|
||||||
if (/subscriptions/.test(ev)) {
|
if (/subscriptions/.test(ev)) {
|
||||||
|
if (data.roles) {
|
||||||
|
data.roles = data.roles.map(role => ({ value: role }));
|
||||||
|
}
|
||||||
realm.write(() => {
|
realm.write(() => {
|
||||||
realm.create('subscriptions', data, true);
|
realm.create('subscriptions', data, true);
|
||||||
});
|
});
|
||||||
|
@ -98,6 +101,7 @@ const RocketChat = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
RocketChat.getSettings();
|
RocketChat.getSettings();
|
||||||
|
RocketChat.getPermissions();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(e => console.error(e));
|
.catch(e => console.error(e));
|
||||||
|
@ -136,6 +140,17 @@ const RocketChat = {
|
||||||
}).then(response => response.json());
|
}).then(response => response.json());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
userInfo({ server, token, userId }) {
|
||||||
|
return fetch(`${ server }/api/v1/users.info?userId=${ userId }`, {
|
||||||
|
method: 'get',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Auth-Token': token,
|
||||||
|
'X-User-Id': userId
|
||||||
|
}
|
||||||
|
}).then(response => response.json());
|
||||||
|
},
|
||||||
|
|
||||||
register({ credentials }) {
|
register({ credentials }) {
|
||||||
return call('registerUser', credentials);
|
return call('registerUser', credentials);
|
||||||
},
|
},
|
||||||
|
@ -385,6 +400,9 @@ const RocketChat = {
|
||||||
if (room) {
|
if (room) {
|
||||||
subscription.roomUpdatedAt = room._updatedAt;
|
subscription.roomUpdatedAt = room._updatedAt;
|
||||||
}
|
}
|
||||||
|
if (subscription.roles) {
|
||||||
|
subscription.roles = subscription.roles.map(role => ({ value: role }));
|
||||||
|
}
|
||||||
subscription._server = { id: server.server };
|
subscription._server = { id: server.server };
|
||||||
return subscription;
|
return subscription;
|
||||||
});
|
});
|
||||||
|
@ -413,7 +431,8 @@ const RocketChat = {
|
||||||
reduxStore.dispatch(actions.setAllSettings(RocketChat.parseSettings(filteredSettings)));
|
reduxStore.dispatch(actions.setAllSettings(RocketChat.parseSettings(filteredSettings)));
|
||||||
},
|
},
|
||||||
parseSettings: settings => settings.reduce((ret, item) => {
|
parseSettings: settings => settings.reduce((ret, item) => {
|
||||||
ret[item._id] = item[settingsType[item.type]] || item.valueAsString || item.value;
|
ret[item._id] = item[settingsType[item.type]] || item.valueAsString || item.valueAsNumber ||
|
||||||
|
item.valueAsBoolean || item.value;
|
||||||
return ret;
|
return ret;
|
||||||
}, {}),
|
}, {}),
|
||||||
_prepareSettings(settings) {
|
_prepareSettings(settings) {
|
||||||
|
@ -423,6 +442,26 @@ const RocketChat = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
_filterSettings: settings => settings.filter(setting => settingsType[setting.type] && setting.value),
|
_filterSettings: settings => settings.filter(setting => settingsType[setting.type] && setting.value),
|
||||||
|
async getPermissions() {
|
||||||
|
const temp = realm.objects('permissions').sorted('_updatedAt', true)[0];
|
||||||
|
const result = await (!temp ? call('permissions/get') : call('permissions/get', new Date(temp._updatedAt)));
|
||||||
|
let permissions = temp ? result.update : result;
|
||||||
|
permissions = RocketChat._preparePermissions(permissions);
|
||||||
|
realm.write(() => {
|
||||||
|
permissions.forEach(permission => realm.create('permissions', permission, true));
|
||||||
|
});
|
||||||
|
reduxStore.dispatch(actions.setAllPermissions(RocketChat.parsePermissions(permissions)));
|
||||||
|
},
|
||||||
|
parsePermissions: permissions => permissions.reduce((ret, item) => {
|
||||||
|
ret[item._id] = item.roles.reduce((roleRet, role) => [...roleRet, role.value], []);
|
||||||
|
return ret;
|
||||||
|
}, {}),
|
||||||
|
_preparePermissions(permissions) {
|
||||||
|
permissions.forEach((permission) => {
|
||||||
|
permission.roles = permission.roles.map(role => ({ value: role }));
|
||||||
|
});
|
||||||
|
return permissions;
|
||||||
|
},
|
||||||
deleteMessage(message) {
|
deleteMessage(message) {
|
||||||
return call('deleteMessage', { _id: message._id });
|
return call('deleteMessage', { _id: message._id });
|
||||||
},
|
},
|
||||||
|
@ -430,7 +469,7 @@ const RocketChat = {
|
||||||
const { _id, msg, rid } = message;
|
const { _id, msg, rid } = message;
|
||||||
return call('updateMessage', { _id, msg, rid });
|
return call('updateMessage', { _id, msg, rid });
|
||||||
},
|
},
|
||||||
starMessage(message) {
|
toggleStarMessage(message) {
|
||||||
return call('starMessage', { _id: message._id, rid: message.rid, starred: !message.starred });
|
return call('starMessage', { _id: message._id, rid: message.rid, starred: !message.starred });
|
||||||
},
|
},
|
||||||
togglePinMessage(message) {
|
togglePinMessage(message) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import server from './server';
|
||||||
import navigator from './navigator';
|
import navigator from './navigator';
|
||||||
import createChannel from './createChannel';
|
import createChannel from './createChannel';
|
||||||
import app from './app';
|
import app from './app';
|
||||||
|
import permissions from './permissions';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
settings, login, meteor, messages, server, navigator, createChannel, app, room
|
settings, login, meteor, messages, server, navigator, createChannel, app, room, permissions
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,8 +4,10 @@ const initialState = {
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
failure: false,
|
failure: false,
|
||||||
message: {},
|
message: {},
|
||||||
|
actionMessage: {},
|
||||||
editing: false,
|
editing: false,
|
||||||
permalink: ''
|
permalink: '',
|
||||||
|
showActions: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function messages(state = initialState, action) {
|
export default function messages(state = initialState, action) {
|
||||||
|
@ -27,12 +29,29 @@ export default function messages(state = initialState, action) {
|
||||||
failure: true,
|
failure: true,
|
||||||
errorMessage: action.err
|
errorMessage: action.err
|
||||||
};
|
};
|
||||||
|
case types.MESSAGES.ACTIONS_SHOW:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
showActions: true,
|
||||||
|
actionMessage: action.actionMessage
|
||||||
|
};
|
||||||
|
case types.MESSAGES.ACTIONS_HIDE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
showActions: false
|
||||||
|
};
|
||||||
case types.MESSAGES.EDIT_INIT:
|
case types.MESSAGES.EDIT_INIT:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
message: action.message,
|
message: action.message,
|
||||||
editing: true
|
editing: true
|
||||||
};
|
};
|
||||||
|
case types.MESSAGES.EDIT_CANCEL:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
message: {},
|
||||||
|
editing: false
|
||||||
|
};
|
||||||
case types.MESSAGES.EDIT_SUCCESS:
|
case types.MESSAGES.EDIT_SUCCESS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -50,13 +69,21 @@ export default function messages(state = initialState, action) {
|
||||||
...state,
|
...state,
|
||||||
permalink: action.permalink
|
permalink: action.permalink
|
||||||
};
|
};
|
||||||
|
case types.MESSAGES.PERMALINK_CLEAR:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
permalink: ''
|
||||||
|
};
|
||||||
case types.MESSAGES.SET_INPUT:
|
case types.MESSAGES.SET_INPUT:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
message: action.message
|
message: action.message
|
||||||
};
|
};
|
||||||
// case types.LOGOUT:
|
case types.MESSAGES.CLEAR_INPUT:
|
||||||
// return initialState;
|
return {
|
||||||
|
...state,
|
||||||
|
message: {}
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import * as types from '../constants/types';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
permissions: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default function permissions(state = initialState.permissions, action) {
|
||||||
|
if (action.type === types.SET_ALL_PERMISSIONS) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
|
@ -18,8 +18,10 @@ const restore = function* restore() {
|
||||||
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
|
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
|
||||||
if (currentServer) {
|
if (currentServer) {
|
||||||
yield put(setServer(currentServer));
|
yield put(setServer(currentServer));
|
||||||
const tmp = realm.objects('settings');
|
const settings = realm.objects('settings');
|
||||||
yield put(actions.setAllSettings(RocketChat.parseSettings(tmp.slice(0, tmp.length))));
|
yield put(actions.setAllSettings(RocketChat.parseSettings(settings.slice(0, settings.length))));
|
||||||
|
const permissions = realm.objects('permissions');
|
||||||
|
yield put(actions.setAllPermissions(RocketChat.parsePermissions(permissions.slice(0, permissions.length))));
|
||||||
}
|
}
|
||||||
yield put(actions.appReady({}));
|
yield put(actions.appReady({}));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ const setUsernameCall = args => RocketChat.setUsername(args);
|
||||||
const logoutCall = args => RocketChat.logout(args);
|
const logoutCall = args => RocketChat.logout(args);
|
||||||
const meCall = args => RocketChat.me(args);
|
const meCall = args => RocketChat.me(args);
|
||||||
const forgotPasswordCall = args => RocketChat.forgotPassword(args);
|
const forgotPasswordCall = args => RocketChat.forgotPassword(args);
|
||||||
|
const userInfoCall = args => RocketChat.userInfo(args);
|
||||||
|
|
||||||
const getToken = function* getToken() {
|
const getToken = function* getToken() {
|
||||||
const currentServer = yield select(getServer);
|
const currentServer = yield select(getServer);
|
||||||
|
@ -76,6 +77,10 @@ const handleLoginRequest = function* handleLoginRequest({ credentials }) {
|
||||||
// if user has username
|
// if user has username
|
||||||
if (me.username) {
|
if (me.username) {
|
||||||
user.username = me.username;
|
user.username = me.username;
|
||||||
|
const userInfo = yield call(userInfoCall, { server, token: user.token, userId: user.id });
|
||||||
|
if (userInfo.user.roles) {
|
||||||
|
user.roles = userInfo.user.roles;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
yield put(registerIncomplete());
|
yield put(registerIncomplete());
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import {
|
||||||
deleteFailure,
|
deleteFailure,
|
||||||
editSuccess,
|
editSuccess,
|
||||||
editFailure,
|
editFailure,
|
||||||
starSuccess,
|
toggleStarSuccess,
|
||||||
starFailure,
|
toggleStarFailure,
|
||||||
permalinkSuccess,
|
permalinkSuccess,
|
||||||
permalinkFailure,
|
permalinkFailure,
|
||||||
togglePinSuccess,
|
togglePinSuccess,
|
||||||
|
@ -18,7 +18,7 @@ import RocketChat from '../lib/rocketchat';
|
||||||
|
|
||||||
const deleteMessage = message => RocketChat.deleteMessage(message);
|
const deleteMessage = message => RocketChat.deleteMessage(message);
|
||||||
const editMessage = message => RocketChat.editMessage(message);
|
const editMessage = message => RocketChat.editMessage(message);
|
||||||
const starMessage = message => RocketChat.starMessage(message);
|
const toggleStarMessage = message => RocketChat.toggleStarMessage(message);
|
||||||
const getPermalink = message => RocketChat.getPermalink(message);
|
const getPermalink = message => RocketChat.getPermalink(message);
|
||||||
const togglePinMessage = message => RocketChat.togglePinMessage(message);
|
const togglePinMessage = message => RocketChat.togglePinMessage(message);
|
||||||
|
|
||||||
|
@ -54,12 +54,12 @@ const handleEditRequest = function* handleEditRequest({ message }) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStarRequest = function* handleStarRequest({ message }) {
|
const handleToggleStarRequest = function* handleToggleStarRequest({ message }) {
|
||||||
try {
|
try {
|
||||||
yield call(starMessage, message);
|
yield call(toggleStarMessage, message);
|
||||||
yield put(starSuccess());
|
yield put(toggleStarSuccess());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(starFailure());
|
yield put(toggleStarFailure());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ const root = function* root() {
|
||||||
yield takeLatest(MESSAGES.REQUEST, get);
|
yield takeLatest(MESSAGES.REQUEST, get);
|
||||||
yield takeLatest(MESSAGES.DELETE_REQUEST, handleDeleteRequest);
|
yield takeLatest(MESSAGES.DELETE_REQUEST, handleDeleteRequest);
|
||||||
yield takeLatest(MESSAGES.EDIT_REQUEST, handleEditRequest);
|
yield takeLatest(MESSAGES.EDIT_REQUEST, handleEditRequest);
|
||||||
yield takeLatest(MESSAGES.STAR_REQUEST, handleStarRequest);
|
yield takeLatest(MESSAGES.TOGGLE_STAR_REQUEST, handleToggleStarRequest);
|
||||||
yield takeLatest(MESSAGES.PERMALINK_REQUEST, handlePermalinkRequest);
|
yield takeLatest(MESSAGES.PERMALINK_REQUEST, handlePermalinkRequest);
|
||||||
yield takeLatest(MESSAGES.TOGGLE_PIN_REQUEST, handleTogglePinRequest);
|
yield takeLatest(MESSAGES.TOGGLE_PIN_REQUEST, handleTogglePinRequest);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,9 +7,11 @@ import { bindActionCreators } from 'redux';
|
||||||
|
|
||||||
import * as actions from '../actions';
|
import * as actions from '../actions';
|
||||||
import { openRoom } from '../actions/room';
|
import { openRoom } from '../actions/room';
|
||||||
|
import { editCancel } from '../actions/messages';
|
||||||
import realm from '../lib/realm';
|
import realm from '../lib/realm';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import Message from '../containers/message';
|
import Message from '../containers/message';
|
||||||
|
import MessageActions from '../containers/MessageActions';
|
||||||
import MessageBox from '../containers/MessageBox';
|
import MessageBox from '../containers/MessageBox';
|
||||||
import Typing from '../containers/Typing';
|
import Typing from '../containers/Typing';
|
||||||
import KeyboardView from '../presentation/KeyboardView';
|
import KeyboardView from '../presentation/KeyboardView';
|
||||||
|
@ -57,13 +59,15 @@ const typing = () => <Typing />;
|
||||||
}),
|
}),
|
||||||
dispatch => ({
|
dispatch => ({
|
||||||
actions: bindActionCreators(actions, dispatch),
|
actions: bindActionCreators(actions, dispatch),
|
||||||
openRoom: room => dispatch(openRoom(room))
|
openRoom: room => dispatch(openRoom(room)),
|
||||||
|
editCancel: () => dispatch(editCancel())
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
export default class RoomView extends React.Component {
|
export default class RoomView extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object.isRequired,
|
navigation: PropTypes.object.isRequired,
|
||||||
openRoom: PropTypes.func.isRequired,
|
openRoom: PropTypes.func.isRequired,
|
||||||
|
editCancel: PropTypes.func,
|
||||||
rid: PropTypes.string,
|
rid: PropTypes.string,
|
||||||
server: PropTypes.string,
|
server: PropTypes.string,
|
||||||
sid: PropTypes.string,
|
sid: PropTypes.string,
|
||||||
|
@ -86,6 +90,7 @@ export default class RoomView extends React.Component {
|
||||||
.objects('messages')
|
.objects('messages')
|
||||||
.filtered('_server.id = $0 AND rid = $1', this.props.server, this.rid)
|
.filtered('_server.id = $0 AND rid = $1', this.props.server, this.rid)
|
||||||
.sorted('ts', true);
|
.sorted('ts', true);
|
||||||
|
this.room = realm.objects('subscriptions').filtered('rid = $0', this.rid);
|
||||||
this.state = {
|
this.state = {
|
||||||
slow: false,
|
slow: false,
|
||||||
dataSource: ds.cloneWithRows([]),
|
dataSource: ds.cloneWithRows([]),
|
||||||
|
@ -114,6 +119,7 @@ export default class RoomView extends React.Component {
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
clearTimeout(this.timer);
|
clearTimeout(this.timer);
|
||||||
this.data.removeAllListeners();
|
this.data.removeAllListeners();
|
||||||
|
this.props.editCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
onEndReached = () => {
|
onEndReached = () => {
|
||||||
|
@ -210,6 +216,7 @@ export default class RoomView extends React.Component {
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
{this.renderFooter()}
|
{this.renderFooter()}
|
||||||
|
<MessageActions room={this.room} />
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue