Room actions (#231)
* Layout * Empty starred list * Favorite room * Pinned messages * fix last messages * fix date on pinned messages
This commit is contained in:
parent
bb5e29fdc7
commit
b1bb815b07
|
@ -41,8 +41,8 @@ jobs:
|
||||||
- image: circleci/android:api-26-alpha
|
- image: circleci/android:api-26-alpha
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"
|
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError"
|
||||||
JVM_OPTS: -Xmx2048m
|
JVM_OPTS: -Xmx4096m
|
||||||
TERM: dumb
|
TERM: dumb
|
||||||
BASH_ENV: "~/.nvm/nvm.sh"
|
BASH_ENV: "~/.nvm/nvm.sh"
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,8 @@ export const SERVER = createRequestTypes('SERVER', [
|
||||||
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT', 'DISCONNECT_BY_USER']);
|
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT', 'DISCONNECT_BY_USER']);
|
||||||
export const LOGOUT = 'LOGOUT'; // logout is always success
|
export const LOGOUT = 'LOGOUT'; // logout is always success
|
||||||
export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'REQUEST']);
|
export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'REQUEST']);
|
||||||
|
export const STARRED_MESSAGES = createRequestTypes('STARRED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGE_RECEIVED', 'MESSAGE_UNSTARRED']);
|
||||||
|
export const PINNED_MESSAGES = createRequestTypes('PINNED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGE_RECEIVED', 'MESSAGE_UNPINNED']);
|
||||||
|
|
||||||
export const INCREMENT = 'INCREMENT';
|
export const INCREMENT = 'INCREMENT';
|
||||||
export const DECREMENT = 'DECREMENT';
|
export const DECREMENT = 'DECREMENT';
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import * as types from './actionsTypes';
|
||||||
|
|
||||||
|
export function openPinnedMessages(rid) {
|
||||||
|
return {
|
||||||
|
type: types.PINNED_MESSAGES.OPEN,
|
||||||
|
rid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closePinnedMessages() {
|
||||||
|
return {
|
||||||
|
type: types.PINNED_MESSAGES.CLOSE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pinnedMessageReceived(message) {
|
||||||
|
return {
|
||||||
|
type: types.PINNED_MESSAGES.MESSAGE_RECEIVED,
|
||||||
|
message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pinnedMessageUnpinned(messageId) {
|
||||||
|
return {
|
||||||
|
type: types.PINNED_MESSAGES.MESSAGE_UNPINNED,
|
||||||
|
messageId
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import * as types from './actionsTypes';
|
||||||
|
|
||||||
|
export function openStarredMessages(rid) {
|
||||||
|
return {
|
||||||
|
type: types.STARRED_MESSAGES.OPEN,
|
||||||
|
rid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeStarredMessages() {
|
||||||
|
return {
|
||||||
|
type: types.STARRED_MESSAGES.CLOSE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function starredMessageReceived(message) {
|
||||||
|
return {
|
||||||
|
type: types.STARRED_MESSAGES.MESSAGE_RECEIVED,
|
||||||
|
message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function starredMessageUnstarred(messageId) {
|
||||||
|
return {
|
||||||
|
type: types.STARRED_MESSAGES.MESSAGE_UNSTARRED,
|
||||||
|
messageId
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View, TouchableHighlight, Text, TouchableOpacity, Vibration } from 'react-native';
|
import { View, TouchableHighlight, Text, TouchableOpacity, Vibration, ViewPropTypes } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
@ -33,17 +33,18 @@ import styles from './styles';
|
||||||
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,
|
||||||
reactions: PropTypes.object.isRequired,
|
reactions: PropTypes.any.isRequired,
|
||||||
baseUrl: PropTypes.string.isRequired,
|
baseUrl: PropTypes.string.isRequired,
|
||||||
Message_TimeFormat: PropTypes.string.isRequired,
|
Message_TimeFormat: PropTypes.string.isRequired,
|
||||||
message: PropTypes.object.isRequired,
|
message: PropTypes.object.isRequired,
|
||||||
user: PropTypes.object.isRequired,
|
user: PropTypes.object.isRequired,
|
||||||
editing: PropTypes.bool,
|
editing: PropTypes.bool,
|
||||||
actionsShow: PropTypes.func,
|
|
||||||
errorActionsShow: PropTypes.func,
|
errorActionsShow: PropTypes.func,
|
||||||
customEmojis: PropTypes.object,
|
customEmojis: PropTypes.object,
|
||||||
toggleReactionPicker: PropTypes.func,
|
toggleReactionPicker: PropTypes.func,
|
||||||
onReactionPress: PropTypes.func
|
onReactionPress: PropTypes.func,
|
||||||
|
style: ViewPropTypes.style,
|
||||||
|
onLongPress: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -73,7 +74,7 @@ export default class Message extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onLongPress() {
|
onLongPress() {
|
||||||
this.props.actionsShow(this.parseMessage());
|
this.props.onLongPress(this.parseMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
onErrorPress() {
|
onErrorPress() {
|
||||||
|
@ -222,7 +223,7 @@ export default class Message extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
item, message, editing, baseUrl, customEmojis
|
item, message, editing, baseUrl, customEmojis, style
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const username = item.alias || item.u.username;
|
const username = item.alias || item.u.username;
|
||||||
const isEditing = message._id === item._id && editing;
|
const isEditing = message._id === item._id && editing;
|
||||||
|
@ -235,7 +236,7 @@ export default class Message extends React.Component {
|
||||||
disabled={this.isDeleted() || this.hasError()}
|
disabled={this.isDeleted() || this.hasError()}
|
||||||
underlayColor='#FFFFFF'
|
underlayColor='#FFFFFF'
|
||||||
activeOpacity={0.3}
|
activeOpacity={0.3}
|
||||||
style={[styles.message, isEditing ? styles.editing : null]}
|
style={[styles.message, isEditing ? styles.editing : null, style]}
|
||||||
accessibilityLabel={accessibilityLabel}
|
accessibilityLabel={accessibilityLabel}
|
||||||
>
|
>
|
||||||
<View style={styles.flex}>
|
<View style={styles.flex}>
|
||||||
|
|
|
@ -4,9 +4,12 @@ import { StackNavigator, DrawerNavigator } from 'react-navigation';
|
||||||
import Sidebar from '../../containers/Sidebar';
|
import Sidebar from '../../containers/Sidebar';
|
||||||
import RoomsListView from '../../views/RoomsListView';
|
import RoomsListView from '../../views/RoomsListView';
|
||||||
import RoomView from '../../views/RoomView';
|
import RoomView from '../../views/RoomView';
|
||||||
|
import RoomActionsView from '../../views/RoomActionsView';
|
||||||
import CreateChannelView from '../../views/CreateChannelView';
|
import CreateChannelView from '../../views/CreateChannelView';
|
||||||
import SelectUsersView from '../../views/SelectUsersView';
|
import SelectUsersView from '../../views/SelectUsersView';
|
||||||
import NewServerView from '../../views/NewServerView';
|
import NewServerView from '../../views/NewServerView';
|
||||||
|
import StarredMessagesView from '../../views/StarredMessagesView';
|
||||||
|
import PinnedMessagesView from '../../views/PinnedMessagesView';
|
||||||
|
|
||||||
const AuthRoutes = StackNavigator(
|
const AuthRoutes = StackNavigator(
|
||||||
{
|
{
|
||||||
|
@ -33,6 +36,27 @@ const AuthRoutes = StackNavigator(
|
||||||
navigationOptions: {
|
navigationOptions: {
|
||||||
title: 'New server'
|
title: 'New server'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
RoomActions: {
|
||||||
|
screen: RoomActionsView,
|
||||||
|
navigationOptions: {
|
||||||
|
title: 'Actions',
|
||||||
|
headerTintColor: '#292E35'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
StarredMessages: {
|
||||||
|
screen: StarredMessagesView,
|
||||||
|
navigationOptions: {
|
||||||
|
title: 'Starred Messages',
|
||||||
|
headerTintColor: '#292E35'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PinnedMessages: {
|
||||||
|
screen: PinnedMessagesView,
|
||||||
|
navigationOptions: {
|
||||||
|
title: 'Pinned Messages',
|
||||||
|
headerTintColor: '#292E35'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createStore, applyMiddleware } from 'redux';
|
import { createStore, applyMiddleware, compose } from 'redux';
|
||||||
import createSagaMiddleware from 'redux-saga';
|
import createSagaMiddleware from 'redux-saga';
|
||||||
import logger from 'redux-logger';
|
import logger from 'redux-logger';
|
||||||
import { composeWithDevTools } from 'remote-redux-devtools';
|
import { composeWithDevTools } from 'remote-redux-devtools';
|
||||||
|
@ -13,14 +13,15 @@ if (__DEV__) {
|
||||||
/* eslint-disable global-require */
|
/* eslint-disable global-require */
|
||||||
const reduxImmutableStateInvariant = require('redux-immutable-state-invariant').default();
|
const reduxImmutableStateInvariant = require('redux-immutable-state-invariant').default();
|
||||||
|
|
||||||
enhacers = composeWithDevTools(
|
const devComposer = composeWithDevTools({ hostname: 'localhost', port: 8000 });
|
||||||
|
enhacers = devComposer(
|
||||||
applyAppStateListener(),
|
applyAppStateListener(),
|
||||||
applyMiddleware(reduxImmutableStateInvariant),
|
applyMiddleware(reduxImmutableStateInvariant),
|
||||||
applyMiddleware(sagaMiddleware),
|
applyMiddleware(sagaMiddleware),
|
||||||
applyMiddleware(logger)
|
applyMiddleware(logger)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
enhacers = composeWithDevTools(
|
enhacers = compose(
|
||||||
applyAppStateListener(),
|
applyAppStateListener(),
|
||||||
applyMiddleware(sagaMiddleware)
|
applyMiddleware(sagaMiddleware)
|
||||||
);
|
);
|
||||||
|
|
|
@ -82,6 +82,7 @@ const subscriptionSchema = {
|
||||||
// groupMentions: 0,
|
// groupMentions: 0,
|
||||||
roomUpdatedAt: { type: 'date', optional: true },
|
roomUpdatedAt: { type: 'date', optional: true },
|
||||||
ro: { type: 'bool', optional: true },
|
ro: { type: 'bool', optional: true },
|
||||||
|
lastOpen: { type: 'date', optional: true },
|
||||||
lastMessage: { type: 'messages', optional: true }
|
lastMessage: { type: 'messages', optional: true }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,8 @@ import { someoneTyping, roomMessageReceived } from '../actions/room';
|
||||||
import { setUser } from '../actions/login';
|
import { setUser } from '../actions/login';
|
||||||
import { disconnect, disconnect_by_user, connectSuccess, connectFailure } from '../actions/connect';
|
import { disconnect, disconnect_by_user, connectSuccess, connectFailure } from '../actions/connect';
|
||||||
import { requestActiveUser } from '../actions/activeUsers';
|
import { requestActiveUser } from '../actions/activeUsers';
|
||||||
|
import { starredMessageReceived, starredMessageUnstarred } from '../actions/starredMessages';
|
||||||
|
import { pinnedMessageReceived, pinnedMessageUnpinned } from '../actions/pinnedMessages';
|
||||||
import Ddp from './ddp';
|
import Ddp from './ddp';
|
||||||
|
|
||||||
export { Accounts } from 'react-native-meteor';
|
export { Accounts } from 'react-native-meteor';
|
||||||
|
@ -145,6 +147,30 @@ const RocketChat = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.ddp.on('rocketchat_starred_message', (ddpMessage) => {
|
||||||
|
if (ddpMessage.msg === 'added') {
|
||||||
|
const message = ddpMessage.fields;
|
||||||
|
message._id = ddpMessage.id;
|
||||||
|
const starredMessage = this._buildMessage(message);
|
||||||
|
return reduxStore.dispatch(starredMessageReceived(starredMessage));
|
||||||
|
}
|
||||||
|
if (ddpMessage.msg === 'removed') {
|
||||||
|
return reduxStore.dispatch(starredMessageUnstarred(ddpMessage.id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.ddp.on('rocketchat_pinned_message', (ddpMessage) => {
|
||||||
|
if (ddpMessage.msg === 'added') {
|
||||||
|
const message = ddpMessage.fields;
|
||||||
|
message._id = ddpMessage.id;
|
||||||
|
const pinnedMessage = this._buildMessage(message);
|
||||||
|
return reduxStore.dispatch(pinnedMessageReceived(pinnedMessage));
|
||||||
|
}
|
||||||
|
if (ddpMessage.msg === 'removed') {
|
||||||
|
return reduxStore.dispatch(pinnedMessageUnpinned(ddpMessage.id));
|
||||||
|
}
|
||||||
|
});
|
||||||
}).catch(console.log);
|
}).catch(console.log);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -272,9 +298,7 @@ const RocketChat = {
|
||||||
_buildMessage(message) {
|
_buildMessage(message) {
|
||||||
message.status = messagesStatus.SENT;
|
message.status = messagesStatus.SENT;
|
||||||
normalizeMessage(message);
|
normalizeMessage(message);
|
||||||
if (message.urls) {
|
message.urls = message.urls ? RocketChat._parseUrls(message.urls) : [];
|
||||||
message.urls = RocketChat._parseUrls(message.urls);
|
|
||||||
}
|
|
||||||
// loadHistory returns message.starred as object
|
// loadHistory returns message.starred as object
|
||||||
// stream-room-messages returns message.starred as an array
|
// stream-room-messages returns message.starred as an array
|
||||||
message.starred = message.starred && (Array.isArray(message.starred) ? message.starred.length > 0 : !!message.starred);
|
message.starred = message.starred && (Array.isArray(message.starred) ? message.starred.length > 0 : !!message.starred);
|
||||||
|
@ -358,8 +382,15 @@ const RocketChat = {
|
||||||
createDirectMessage(username) {
|
createDirectMessage(username) {
|
||||||
return call('createDirectMessage', username);
|
return call('createDirectMessage', username);
|
||||||
},
|
},
|
||||||
readMessages(rid) {
|
async readMessages(rid) {
|
||||||
return call('readMessages', rid);
|
const ret = await call('readMessages', rid);
|
||||||
|
|
||||||
|
const [subscription] = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||||
|
database.write(() => {
|
||||||
|
subscription.lastOpen = new Date();
|
||||||
|
});
|
||||||
|
|
||||||
|
return ret;
|
||||||
},
|
},
|
||||||
joinRoom(rid) {
|
joinRoom(rid) {
|
||||||
return call('joinRoom', rid);
|
return call('joinRoom', rid);
|
||||||
|
@ -610,6 +641,9 @@ const RocketChat = {
|
||||||
},
|
},
|
||||||
setReaction(emoji, messageId) {
|
setReaction(emoji, messageId) {
|
||||||
return call('setReaction', emoji, messageId);
|
return call('setReaction', emoji, messageId);
|
||||||
|
},
|
||||||
|
toggleFavorite(rid, f) {
|
||||||
|
return call('toggleFavorite', rid, !f);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ import app from './app';
|
||||||
import permissions from './permissions';
|
import permissions from './permissions';
|
||||||
import customEmojis from './customEmojis';
|
import customEmojis from './customEmojis';
|
||||||
import activeUsers from './activeUsers';
|
import activeUsers from './activeUsers';
|
||||||
|
import starredMessages from './starredMessages';
|
||||||
|
import pinnedMessages from './pinnedMessages';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
settings,
|
settings,
|
||||||
|
@ -26,5 +28,7 @@ export default combineReducers({
|
||||||
rooms,
|
rooms,
|
||||||
permissions,
|
permissions,
|
||||||
customEmojis,
|
customEmojis,
|
||||||
activeUsers
|
activeUsers,
|
||||||
|
starredMessages,
|
||||||
|
pinnedMessages
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { PINNED_MESSAGES } from '../actions/actionsTypes';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
messages: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function server(state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case PINNED_MESSAGES.MESSAGE_RECEIVED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
messages: [...state.messages, action.message]
|
||||||
|
};
|
||||||
|
case PINNED_MESSAGES.MESSAGE_UNPINNED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
messages: state.messages.filter(message => message._id !== action.messageId)
|
||||||
|
};
|
||||||
|
case PINNED_MESSAGES.CLOSE:
|
||||||
|
return initialState;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { STARRED_MESSAGES } from '../actions/actionsTypes';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
messages: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function server(state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case STARRED_MESSAGES.MESSAGE_RECEIVED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
messages: [...state.messages, action.message]
|
||||||
|
};
|
||||||
|
case STARRED_MESSAGES.MESSAGE_UNSTARRED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
messages: state.messages.filter(message => message._id !== action.messageId)
|
||||||
|
};
|
||||||
|
case STARRED_MESSAGES.CLOSE:
|
||||||
|
return initialState;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ import createChannel from './createChannel';
|
||||||
import init from './init';
|
import init from './init';
|
||||||
import state from './state';
|
import state from './state';
|
||||||
import activeUsers from './activeUsers';
|
import activeUsers from './activeUsers';
|
||||||
|
import starredMessages from './starredMessages';
|
||||||
|
import pinnedMessages from './pinnedMessages';
|
||||||
|
|
||||||
const root = function* root() {
|
const root = function* root() {
|
||||||
yield all([
|
yield all([
|
||||||
|
@ -21,7 +23,9 @@ const root = function* root() {
|
||||||
messages(),
|
messages(),
|
||||||
selectServer(),
|
selectServer(),
|
||||||
state(),
|
state(),
|
||||||
activeUsers()
|
activeUsers(),
|
||||||
|
starredMessages(),
|
||||||
|
pinnedMessages()
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { take, takeLatest } from 'redux-saga/effects';
|
||||||
|
import * as types from '../actions/actionsTypes';
|
||||||
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
|
||||||
|
const watchPinnedMessagesRoom = function* watchPinnedMessagesRoom({ rid }) {
|
||||||
|
const sub = yield RocketChat.subscribe('pinnedMessages', rid, 50);
|
||||||
|
yield take(types.PINNED_MESSAGES.CLOSE);
|
||||||
|
sub.unsubscribe().catch(e => alert(e));
|
||||||
|
};
|
||||||
|
|
||||||
|
const root = function* root() {
|
||||||
|
yield takeLatest(types.PINNED_MESSAGES.OPEN, watchPinnedMessagesRoom);
|
||||||
|
};
|
||||||
|
export default root;
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { take, takeLatest } from 'redux-saga/effects';
|
||||||
|
import * as types from '../actions/actionsTypes';
|
||||||
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
|
||||||
|
const watchStarredMessagesRoom = function* watchStarredMessagesRoom({ rid }) {
|
||||||
|
const sub = yield RocketChat.subscribe('starredMessages', rid, 50);
|
||||||
|
yield take(types.STARRED_MESSAGES.CLOSE);
|
||||||
|
sub.unsubscribe().catch(e => alert(e));
|
||||||
|
};
|
||||||
|
|
||||||
|
const root = function* root() {
|
||||||
|
yield takeLatest(types.STARRED_MESSAGES.OPEN, watchStarredMessagesRoom);
|
||||||
|
};
|
||||||
|
export default root;
|
|
@ -0,0 +1,111 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { FlatList, Text, View } from 'react-native';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ActionSheet from 'react-native-actionsheet';
|
||||||
|
|
||||||
|
import { openPinnedMessages, closePinnedMessages } from '../../actions/pinnedMessages';
|
||||||
|
import styles from './styles';
|
||||||
|
import Message from '../../containers/message';
|
||||||
|
import { togglePinRequest } from '../../actions/messages';
|
||||||
|
|
||||||
|
const PIN_INDEX = 0;
|
||||||
|
const CANCEL_INDEX = 1;
|
||||||
|
const options = ['Unpin', 'Cancel'];
|
||||||
|
|
||||||
|
@connect(
|
||||||
|
state => ({
|
||||||
|
messages: state.pinnedMessages.messages,
|
||||||
|
user: state.login.user,
|
||||||
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
openPinnedMessages: rid => dispatch(openPinnedMessages(rid)),
|
||||||
|
closePinnedMessages: () => dispatch(closePinnedMessages()),
|
||||||
|
togglePinRequest: message => dispatch(togglePinRequest(message))
|
||||||
|
})
|
||||||
|
)
|
||||||
|
export default class PinnedMessagesView extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
navigation: PropTypes.object,
|
||||||
|
messages: PropTypes.array,
|
||||||
|
user: PropTypes.object,
|
||||||
|
baseUrl: PropTypes.string,
|
||||||
|
openPinnedMessages: PropTypes.func,
|
||||||
|
closePinnedMessages: PropTypes.func,
|
||||||
|
togglePinRequest: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
message: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.props.openPinnedMessages(this.props.navigation.state.params.rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.props.closePinnedMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
onLongPress = (message) => {
|
||||||
|
this.setState({ message });
|
||||||
|
this.actionSheet.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleActionPress = (actionIndex) => {
|
||||||
|
switch (actionIndex) {
|
||||||
|
case PIN_INDEX:
|
||||||
|
this.props.togglePinRequest(this.state.message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEmpty = () => (
|
||||||
|
<View style={styles.listEmptyContainer}>
|
||||||
|
<Text>No pinned messages</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
renderItem = ({ item }) => (
|
||||||
|
<Message
|
||||||
|
item={item}
|
||||||
|
style={styles.message}
|
||||||
|
reactions={item.reactions}
|
||||||
|
user={this.props.user}
|
||||||
|
baseUrl={this.props.baseUrl}
|
||||||
|
Message_TimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||||
|
onLongPress={this.onLongPress}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.messages.length === 0) {
|
||||||
|
return this.renderEmpty();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
<FlatList
|
||||||
|
key='pinned-messages-view-list'
|
||||||
|
data={this.props.messages}
|
||||||
|
renderItem={this.renderItem}
|
||||||
|
style={styles.list}
|
||||||
|
keyExtractor={item => item._id}
|
||||||
|
/>,
|
||||||
|
<ActionSheet
|
||||||
|
key='pinned-messages-view-action-sheet'
|
||||||
|
ref={o => this.actionSheet = o}
|
||||||
|
title='Actions'
|
||||||
|
options={options}
|
||||||
|
cancelButtonIndex={CANCEL_INDEX}
|
||||||
|
onPress={this.handleActionPress}
|
||||||
|
/>
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
export default StyleSheet.create({
|
||||||
|
list: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#ffffff'
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
transform: [{ scaleY: 1 }]
|
||||||
|
},
|
||||||
|
listEmptyContainer: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: '#ffffff'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,175 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { View, SectionList, Text, StyleSheet } from 'react-native';
|
||||||
|
import Icon from 'react-native-vector-icons/Ionicons';
|
||||||
|
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import styles from './styles';
|
||||||
|
import Avatar from '../../containers/Avatar';
|
||||||
|
import Touch from '../../utils/touch';
|
||||||
|
import database from '../../lib/realm';
|
||||||
|
|
||||||
|
@connect(state => ({
|
||||||
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||||
|
}))
|
||||||
|
export default class RoomActionsView extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
baseUrl: PropTypes.string,
|
||||||
|
navigation: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const { rid } = props.navigation.state.params;
|
||||||
|
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||||
|
this.state = {
|
||||||
|
sections: [],
|
||||||
|
room: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.updateRoom();
|
||||||
|
this.updateSections();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.rooms.addListener(this.updateRoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRoom = () => {
|
||||||
|
const [room] = this.rooms;
|
||||||
|
this.setState({ room });
|
||||||
|
this.props.navigation.setParams({
|
||||||
|
f: room.f
|
||||||
|
});
|
||||||
|
this.updateSections();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSections = () => {
|
||||||
|
const { rid, t } = this.state.room;
|
||||||
|
const sections = [{
|
||||||
|
data: [{ icon: 'ios-star', name: 'USER' }],
|
||||||
|
renderItem: this.renderRoomInfo
|
||||||
|
}, {
|
||||||
|
data: [
|
||||||
|
{ icon: 'ios-call-outline', name: 'Voice call' },
|
||||||
|
{ icon: 'ios-videocam-outline', name: 'Video call' }
|
||||||
|
],
|
||||||
|
renderItem: this.renderItem
|
||||||
|
}, {
|
||||||
|
data: [
|
||||||
|
{ icon: 'ios-attach', name: 'Files' },
|
||||||
|
{ icon: 'ios-at-outline', name: 'Mentions' },
|
||||||
|
{
|
||||||
|
icon: 'ios-star-outline',
|
||||||
|
name: 'Starred',
|
||||||
|
route: 'StarredMessages',
|
||||||
|
params: { rid }
|
||||||
|
},
|
||||||
|
{ icon: 'ios-search', name: 'Search' },
|
||||||
|
{ icon: 'ios-share-outline', name: 'Share' },
|
||||||
|
{
|
||||||
|
icon: 'ios-pin',
|
||||||
|
name: 'Pinned',
|
||||||
|
route: 'PinnedMessages',
|
||||||
|
params: { rid }
|
||||||
|
},
|
||||||
|
{ icon: 'ios-code', name: 'Snippets' },
|
||||||
|
{ icon: 'ios-notifications-outline', name: 'Notifications preferences' }
|
||||||
|
],
|
||||||
|
renderItem: this.renderItem
|
||||||
|
}];
|
||||||
|
if (t === 'd') {
|
||||||
|
sections.push({
|
||||||
|
data: [
|
||||||
|
{ icon: 'ios-volume-off', name: 'Mute user' },
|
||||||
|
{ icon: 'block', name: 'Block user', type: 'danger' }
|
||||||
|
],
|
||||||
|
renderItem: this.renderItem
|
||||||
|
});
|
||||||
|
} else if (t === 'c' || t === 'p') {
|
||||||
|
sections[2].data.unshift({ icon: 'ios-people', name: 'Members', description: '42 members' });
|
||||||
|
sections.push({
|
||||||
|
data: [
|
||||||
|
{ icon: 'ios-volume-off', name: 'Mute channel' },
|
||||||
|
{ icon: 'block', name: 'Leave channel', type: 'danger' }
|
||||||
|
],
|
||||||
|
renderItem: this.renderItem
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.setState({ sections });
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRoomInfo = ({ item }) => this.renderTouchableItem([
|
||||||
|
<Avatar
|
||||||
|
key='avatar'
|
||||||
|
text={this.state.room.name}
|
||||||
|
size={50}
|
||||||
|
style={StyleSheet.flatten(styles.avatar)}
|
||||||
|
baseUrl={this.props.baseUrl}
|
||||||
|
type={this.state.room.t}
|
||||||
|
/>,
|
||||||
|
<View key='name' style={styles.roomTitleContainer}>
|
||||||
|
<Text style={styles.roomTitle}>{this.state.room.fname}</Text>
|
||||||
|
<Text style={styles.roomDescription}>@{this.state.room.name}</Text>
|
||||||
|
</View>,
|
||||||
|
<Icon key='icon' name='ios-arrow-forward' size={20} style={styles.sectionItemIcon} color='#cbced1' />
|
||||||
|
], item)
|
||||||
|
|
||||||
|
renderTouchableItem = (subview, item) => (
|
||||||
|
<Touch
|
||||||
|
onPress={() => item.route && this.props.navigation.navigate(item.route, item.params)}
|
||||||
|
underlayColor='#FFFFFF'
|
||||||
|
activeOpacity={0.5}
|
||||||
|
accessibilityLabel={item.name}
|
||||||
|
accessibilityTraits='button'
|
||||||
|
>
|
||||||
|
<View style={styles.sectionItem}>
|
||||||
|
{subview}
|
||||||
|
</View>
|
||||||
|
</Touch>
|
||||||
|
)
|
||||||
|
|
||||||
|
renderItem = ({ item }) => {
|
||||||
|
if (item.type === 'danger') {
|
||||||
|
const subview = [
|
||||||
|
<MaterialIcon key='icon' name={item.icon} size={20} style={[styles.sectionItemIcon, styles.textColorDanger]} />,
|
||||||
|
<Text key='name' style={[styles.sectionItemName, styles.textColorDanger]}>{ item.name }</Text>
|
||||||
|
];
|
||||||
|
return this.renderTouchableItem(subview, item);
|
||||||
|
}
|
||||||
|
const subview = [
|
||||||
|
<Icon key='left-icon' name={item.icon} size={24} style={styles.sectionItemIcon} />,
|
||||||
|
<Text key='name' style={styles.sectionItemName}>{ item.name }</Text>,
|
||||||
|
item.description && <Text key='description' style={styles.sectionItemDescription}>{ item.description }</Text>,
|
||||||
|
<Icon key='right-icon' name='ios-arrow-forward' size={20} style={styles.sectionItemIcon} color='#cbced1' />
|
||||||
|
];
|
||||||
|
return this.renderTouchableItem(subview, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSectionSeparator = (data) => {
|
||||||
|
if (!data.trailingItem) {
|
||||||
|
if (!data.trailingSection) {
|
||||||
|
return <View style={styles.sectionSeparatorBorder} />;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<View style={[styles.sectionSeparator, data.leadingSection && styles.sectionSeparatorBorder]} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<SectionList
|
||||||
|
style={styles.container}
|
||||||
|
stickySectionHeadersEnabled={false}
|
||||||
|
sections={this.state.sections}
|
||||||
|
SectionSeparatorComponent={this.renderSectionSeparator}
|
||||||
|
keyExtractor={(item, index) => index}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
export default StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
backgroundColor: '#F6F7F9'
|
||||||
|
},
|
||||||
|
headerButton: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
height: 44,
|
||||||
|
width: 44,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
sectionItem: {
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
paddingVertical: 10,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
sectionItemIcon: {
|
||||||
|
width: 45,
|
||||||
|
textAlign: 'center'
|
||||||
|
},
|
||||||
|
sectionItemName: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
sectionItemDescription: {
|
||||||
|
color: '#cbced1'
|
||||||
|
},
|
||||||
|
sectionSeparator: {
|
||||||
|
height: 10,
|
||||||
|
backgroundColor: '#F6F7F9'
|
||||||
|
},
|
||||||
|
sectionSeparatorBorder: {
|
||||||
|
borderColor: '#EBEDF1',
|
||||||
|
borderTopWidth: 1
|
||||||
|
},
|
||||||
|
textColorDanger: {
|
||||||
|
color: '#f5455c'
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
marginHorizontal: 10
|
||||||
|
},
|
||||||
|
roomTitleContainer: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
roomTitle: {
|
||||||
|
fontSize: 16
|
||||||
|
},
|
||||||
|
roomDescription: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#cbced1'
|
||||||
|
}
|
||||||
|
});
|
|
@ -5,11 +5,14 @@ import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { HeaderBackButton } from 'react-navigation';
|
import { HeaderBackButton } from 'react-navigation';
|
||||||
|
|
||||||
|
import RocketChat from '../../../lib/rocketchat';
|
||||||
import realm from '../../../lib/realm';
|
import realm from '../../../lib/realm';
|
||||||
import Avatar from '../../../containers/Avatar';
|
import Avatar from '../../../containers/Avatar';
|
||||||
import { STATUS_COLORS } from '../../../constants/colors';
|
import { STATUS_COLORS } from '../../../constants/colors';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { closeRoom } from '../../../actions/room';
|
import { closeRoom } from '../../../actions/room';
|
||||||
|
import Touch from '../../../utils/touch';
|
||||||
|
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
user: state.login.user,
|
user: state.login.user,
|
||||||
|
@ -108,9 +111,25 @@ export default class RoomHeaderView extends React.PureComponent {
|
||||||
|
|
||||||
renderRight = () => (
|
renderRight = () => (
|
||||||
<View style={styles.right}>
|
<View style={styles.right}>
|
||||||
|
<Touch
|
||||||
|
onPress={() => RocketChat.toggleFavorite(this.room[0].rid, this.room[0].f)}
|
||||||
|
accessibilityLabel='Star room'
|
||||||
|
accessibilityTraits='button'
|
||||||
|
underlayColor='#FFFFFF'
|
||||||
|
activeOpacity={0.5}
|
||||||
|
>
|
||||||
|
<View style={styles.headerButton}>
|
||||||
|
<Icon
|
||||||
|
name={`${ Platform.OS === 'ios' ? 'ios' : 'md' }-star${ this.room[0].f ? '' : '-outline' }`}
|
||||||
|
color='#f6c502'
|
||||||
|
size={24}
|
||||||
|
backgroundColor='transparent'
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</Touch>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.headerButton}
|
style={styles.headerButton}
|
||||||
onPress={() => {}}
|
onPress={() => this.props.navigation.navigate('RoomActions', { rid: this.room[0].rid })}
|
||||||
accessibilityLabel='Room actions'
|
accessibilityLabel='Room actions'
|
||||||
accessibilityTraits='button'
|
accessibilityTraits='button'
|
||||||
>
|
>
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default StyleSheet.create({
|
||||||
headerButton: {
|
headerButton: {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
height: 44,
|
height: 44,
|
||||||
width: 44,
|
width: 40,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center'
|
justifyContent: 'center'
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import equal from 'deep-equal';
|
||||||
import { List } from './ListView';
|
import { List } from './ListView';
|
||||||
import * as actions from '../../actions';
|
import * as actions from '../../actions';
|
||||||
import { openRoom, setLastOpen } from '../../actions/room';
|
import { openRoom, setLastOpen } from '../../actions/room';
|
||||||
import { editCancel, toggleReactionPicker } from '../../actions/messages';
|
import { editCancel, toggleReactionPicker, actionsShow } from '../../actions/messages';
|
||||||
import database from '../../lib/realm';
|
import database from '../../lib/realm';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import Message from '../../containers/message';
|
import Message from '../../containers/message';
|
||||||
|
@ -35,7 +35,8 @@ import styles from './styles';
|
||||||
openRoom: room => dispatch(openRoom(room)),
|
openRoom: room => dispatch(openRoom(room)),
|
||||||
editCancel: () => dispatch(editCancel()),
|
editCancel: () => dispatch(editCancel()),
|
||||||
setLastOpen: date => dispatch(setLastOpen(date)),
|
setLastOpen: date => dispatch(setLastOpen(date)),
|
||||||
toggleReactionPicker: message => dispatch(toggleReactionPicker(message))
|
toggleReactionPicker: message => dispatch(toggleReactionPicker(message)),
|
||||||
|
actionsShow: actionMessage => dispatch(actionsShow(actionMessage))
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
export default class RoomView extends React.Component {
|
export default class RoomView extends React.Component {
|
||||||
|
@ -51,8 +52,9 @@ export default class RoomView extends React.Component {
|
||||||
Message_TimeFormat: PropTypes.string,
|
Message_TimeFormat: PropTypes.string,
|
||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
actionMessage: PropTypes.object,
|
actionMessage: PropTypes.object,
|
||||||
toggleReactionPicker: PropTypes.func.isRequired
|
toggleReactionPicker: PropTypes.func.isRequired,
|
||||||
// layoutAnimation: PropTypes.instanceOf(Date)
|
// layoutAnimation: PropTypes.instanceOf(Date),
|
||||||
|
actionsShow: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
static navigationOptions = ({ navigation }) => ({
|
static navigationOptions = ({ navigation }) => ({
|
||||||
|
@ -124,6 +126,10 @@ export default class RoomView extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMessageLongPress = (message) => {
|
||||||
|
this.props.actionsShow(message);
|
||||||
|
}
|
||||||
|
|
||||||
onReactionPress = (shortname, messageId) => {
|
onReactionPress = (shortname, messageId) => {
|
||||||
if (!messageId) {
|
if (!messageId) {
|
||||||
RocketChat.setReaction(shortname, this.props.actionMessage._id);
|
RocketChat.setReaction(shortname, this.props.actionMessage._id);
|
||||||
|
@ -158,6 +164,7 @@ export default class RoomView extends React.Component {
|
||||||
Message_TimeFormat={this.props.Message_TimeFormat}
|
Message_TimeFormat={this.props.Message_TimeFormat}
|
||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
onReactionPress={this.onReactionPress}
|
onReactionPress={this.onReactionPress}
|
||||||
|
onLongPress={this.onMessageLongPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { FlatList, Text, View } from 'react-native';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ActionSheet from 'react-native-actionsheet';
|
||||||
|
|
||||||
|
import { openStarredMessages, closeStarredMessages } from '../../actions/starredMessages';
|
||||||
|
import styles from './styles';
|
||||||
|
import Message from '../../containers/message';
|
||||||
|
import { toggleStarRequest } from '../../actions/messages';
|
||||||
|
|
||||||
|
const STAR_INDEX = 0;
|
||||||
|
const CANCEL_INDEX = 1;
|
||||||
|
const options = ['Unstar', 'Cancel'];
|
||||||
|
|
||||||
|
@connect(
|
||||||
|
state => ({
|
||||||
|
messages: state.starredMessages.messages,
|
||||||
|
user: state.login.user,
|
||||||
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
openStarredMessages: rid => dispatch(openStarredMessages(rid)),
|
||||||
|
closeStarredMessages: () => dispatch(closeStarredMessages()),
|
||||||
|
toggleStarRequest: message => dispatch(toggleStarRequest(message))
|
||||||
|
})
|
||||||
|
)
|
||||||
|
export default class StarredMessagesView extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
navigation: PropTypes.object,
|
||||||
|
messages: PropTypes.array,
|
||||||
|
user: PropTypes.object,
|
||||||
|
baseUrl: PropTypes.string,
|
||||||
|
openStarredMessages: PropTypes.func,
|
||||||
|
closeStarredMessages: PropTypes.func,
|
||||||
|
toggleStarRequest: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
message: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.props.openStarredMessages(this.props.navigation.state.params.rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.props.closeStarredMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
onLongPress = (message) => {
|
||||||
|
this.setState({ message });
|
||||||
|
this.actionSheet.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleActionPress = (actionIndex) => {
|
||||||
|
switch (actionIndex) {
|
||||||
|
case STAR_INDEX:
|
||||||
|
this.props.toggleStarRequest(this.state.message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEmpty = () => (
|
||||||
|
<View style={styles.listEmptyContainer}>
|
||||||
|
<Text>No starred messages</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
renderItem = ({ item }) => (
|
||||||
|
<Message
|
||||||
|
item={item}
|
||||||
|
style={styles.message}
|
||||||
|
reactions={item.reactions}
|
||||||
|
user={this.props.user}
|
||||||
|
baseUrl={this.props.baseUrl}
|
||||||
|
Message_TimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||||
|
onLongPress={this.onLongPress}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.messages.length === 0) {
|
||||||
|
return this.renderEmpty();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
<FlatList
|
||||||
|
key='starred-messages-view-list'
|
||||||
|
data={this.props.messages}
|
||||||
|
renderItem={this.renderItem}
|
||||||
|
style={styles.list}
|
||||||
|
keyExtractor={item => item._id}
|
||||||
|
/>,
|
||||||
|
<ActionSheet
|
||||||
|
key='starred-messages-view-action-sheet'
|
||||||
|
ref={o => this.actionSheet = o}
|
||||||
|
title='Actions'
|
||||||
|
options={options}
|
||||||
|
cancelButtonIndex={CANCEL_INDEX}
|
||||||
|
onPress={this.handleActionPress}
|
||||||
|
/>
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
export default StyleSheet.create({
|
||||||
|
list: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#ffffff'
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
transform: [{ scaleY: 1 }]
|
||||||
|
},
|
||||||
|
listEmptyContainer: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: '#ffffff'
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in New Issue