Use Rest API pt 2 (#568)
* Room files * Pinned messages * Starred messages * Mentioned messages * Search messages * Bug fixes * Profile * Livechat * Block/unblock user * Erase room * Archive room * Remove unused method * Bug fix
This commit is contained in:
parent
dca2181b2c
commit
ad37586065
|
@ -77,10 +77,6 @@ export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DI
|
|||
export const LOGOUT = 'LOGOUT'; // logout is always success
|
||||
export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET']);
|
||||
export const ROLES = createRequestTypes('ROLES', ['SET']);
|
||||
export const STARRED_MESSAGES = createRequestTypes('STARRED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED', 'MESSAGE_UNSTARRED']);
|
||||
export const PINNED_MESSAGES = createRequestTypes('PINNED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED', 'MESSAGE_UNPINNED']);
|
||||
export const MENTIONED_MESSAGES = createRequestTypes('MENTIONED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||
export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||
export const ROOM_FILES = createRequestTypes('ROOM_FILES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
|
||||
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
import * as types from './actionsTypes';
|
||||
|
||||
export function openMentionedMessages(rid, limit) {
|
||||
return {
|
||||
type: types.MENTIONED_MESSAGES.OPEN,
|
||||
rid,
|
||||
limit
|
||||
};
|
||||
}
|
||||
|
||||
export function readyMentionedMessages() {
|
||||
return {
|
||||
type: types.MENTIONED_MESSAGES.READY
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function closeMentionedMessages() {
|
||||
return {
|
||||
type: types.MENTIONED_MESSAGES.CLOSE
|
||||
};
|
||||
}
|
||||
|
||||
export function mentionedMessagesReceived(messages) {
|
||||
return {
|
||||
type: types.MENTIONED_MESSAGES.MESSAGES_RECEIVED,
|
||||
messages
|
||||
};
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
import * as types from './actionsTypes';
|
||||
|
||||
export function openPinnedMessages(rid, limit) {
|
||||
return {
|
||||
type: types.PINNED_MESSAGES.OPEN,
|
||||
rid,
|
||||
limit
|
||||
};
|
||||
}
|
||||
|
||||
export function readyPinnedMessages() {
|
||||
return {
|
||||
type: types.PINNED_MESSAGES.READY
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function closePinnedMessages() {
|
||||
return {
|
||||
type: types.PINNED_MESSAGES.CLOSE
|
||||
};
|
||||
}
|
||||
|
||||
export function pinnedMessagesReceived(messages) {
|
||||
return {
|
||||
type: types.PINNED_MESSAGES.MESSAGES_RECEIVED,
|
||||
messages
|
||||
};
|
||||
}
|
||||
|
||||
export function pinnedMessageUnpinned(messageId) {
|
||||
return {
|
||||
type: types.PINNED_MESSAGES.MESSAGE_UNPINNED,
|
||||
messageId
|
||||
};
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import * as types from './actionsTypes';
|
||||
|
||||
export function openRoomFiles(rid, limit) {
|
||||
return {
|
||||
type: types.ROOM_FILES.OPEN,
|
||||
rid,
|
||||
limit
|
||||
};
|
||||
}
|
||||
|
||||
export function readyRoomFiles() {
|
||||
return {
|
||||
type: types.ROOM_FILES.READY
|
||||
};
|
||||
}
|
||||
|
||||
export function closeRoomFiles() {
|
||||
return {
|
||||
type: types.ROOM_FILES.CLOSE
|
||||
};
|
||||
}
|
||||
|
||||
export function roomFilesReceived(messages) {
|
||||
return {
|
||||
type: types.ROOM_FILES.MESSAGES_RECEIVED,
|
||||
messages
|
||||
};
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import * as types from './actionsTypes';
|
||||
|
||||
export function openStarredMessages(rid, limit) {
|
||||
return {
|
||||
type: types.STARRED_MESSAGES.OPEN,
|
||||
rid,
|
||||
limit
|
||||
};
|
||||
}
|
||||
|
||||
export function readyStarredMessages() {
|
||||
return {
|
||||
type: types.STARRED_MESSAGES.READY
|
||||
};
|
||||
}
|
||||
|
||||
export function closeStarredMessages() {
|
||||
return {
|
||||
type: types.STARRED_MESSAGES.CLOSE
|
||||
};
|
||||
}
|
||||
|
||||
export function starredMessagesReceived(messages) {
|
||||
return {
|
||||
type: types.STARRED_MESSAGES.MESSAGES_RECEIVED,
|
||||
messages
|
||||
};
|
||||
}
|
||||
|
||||
export function starredMessageUnstarred(messageId) {
|
||||
return {
|
||||
type: types.STARRED_MESSAGES.MESSAGE_UNSTARRED,
|
||||
messageId
|
||||
};
|
||||
}
|
|
@ -39,7 +39,8 @@ const SYSTEM_MESSAGES = [
|
|||
'room_changed_description',
|
||||
'room_changed_announcement',
|
||||
'room_changed_topic',
|
||||
'room_changed_privacy'
|
||||
'room_changed_privacy',
|
||||
'message_snippeted'
|
||||
];
|
||||
|
||||
const getInfoMessage = ({
|
||||
|
@ -76,6 +77,8 @@ const getInfoMessage = ({
|
|||
return I18n.t('Room_changed_topic', { topic: msg, userBy: username });
|
||||
} else if (type === 'room_changed_privacy') {
|
||||
return I18n.t('Room_changed_privacy', { type: msg, userBy: username });
|
||||
} else if (type === 'message_snippeted') {
|
||||
return I18n.t('Created_snippet');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
@ -107,7 +110,10 @@ export default class Message extends PureComponent {
|
|||
header: PropTypes.bool,
|
||||
avatar: PropTypes.string,
|
||||
alias: PropTypes.string,
|
||||
ts: PropTypes.instanceOf(Date),
|
||||
ts: PropTypes.oneOfType([
|
||||
PropTypes.instanceOf(Date),
|
||||
PropTypes.string
|
||||
]),
|
||||
edited: PropTypes.bool,
|
||||
attachments: PropTypes.oneOfType([
|
||||
PropTypes.array,
|
||||
|
|
|
@ -35,7 +35,10 @@ export default class User extends React.PureComponent {
|
|||
timeFormat: PropTypes.string.isRequired,
|
||||
username: PropTypes.string,
|
||||
alias: PropTypes.string,
|
||||
ts: PropTypes.instanceOf(Date),
|
||||
ts: PropTypes.oneOfType([
|
||||
PropTypes.instanceOf(Date),
|
||||
PropTypes.string
|
||||
]),
|
||||
temp: PropTypes.bool
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,7 @@ export default {
|
|||
Copy_Permalink: 'Copy Permalink',
|
||||
Create_account: 'Create an account',
|
||||
Create_Channel: 'Create Channel',
|
||||
Created_snippet: 'Created a snippet',
|
||||
Create_a_new_workspace: 'Create a new workspace',
|
||||
Create: 'Create',
|
||||
Delete_Room_Warning: 'Deleting a room will delete all messages posted within the room. This cannot be undone.',
|
||||
|
@ -209,6 +210,7 @@ export default {
|
|||
No_files: 'No files',
|
||||
No_mentioned_messages: 'No mentioned messages',
|
||||
No_pinned_messages: 'No pinned messages',
|
||||
No_results_found: 'No results found',
|
||||
No_snippeted_messages: 'No snippeted messages',
|
||||
No_starred_messages: 'No starred messages',
|
||||
No_announcement_provided: 'No announcement provided.',
|
||||
|
|
|
@ -138,6 +138,7 @@ export default {
|
|||
Copy_Permalink: 'Copiar Link-Permanente',
|
||||
Create_account: 'Criar conta',
|
||||
Create_Channel: 'Criar Canal',
|
||||
Created_snippet: 'Criou um snippet',
|
||||
Create_a_new_workspace: 'Criar nova área de trabalho',
|
||||
Create: 'Criar',
|
||||
Delete_Room_Warning: 'A exclusão de uma sala irá apagar todas as mensagens postadas na sala. Isso não pode ser desfeito.',
|
||||
|
@ -212,6 +213,7 @@ export default {
|
|||
No_files: 'Não há arquivos',
|
||||
No_mentioned_messages: 'Não há menções',
|
||||
No_pinned_messages: 'Não há mensagens fixadas',
|
||||
No_results_found: 'Nenhum resultado encontrado',
|
||||
No_snippeted_messages: 'Não há trechos de mensagens',
|
||||
No_starred_messages: 'Não há mensagens favoritas',
|
||||
No_announcement_provided: 'Sem anúncio.',
|
||||
|
|
|
@ -6,6 +6,19 @@ import database from '../realm';
|
|||
import log from '../../utils/log';
|
||||
|
||||
async function load({ rid: roomId, latest, t }) {
|
||||
if (t === 'l') {
|
||||
try {
|
||||
const data = await SDK.driver.asyncCall('loadHistory', roomId, null, 50, latest);
|
||||
if (!data || data.status === 'error') {
|
||||
return [];
|
||||
}
|
||||
return data.messages;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
let params = { roomId, count: 50 };
|
||||
if (latest) {
|
||||
params = { ...params, latest: new Date(latest).toISOString() };
|
||||
|
|
|
@ -14,11 +14,7 @@ import {
|
|||
} from '../actions/login';
|
||||
import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
|
||||
import { setActiveUser } from '../actions/activeUsers';
|
||||
import { starredMessagesReceived, starredMessageUnstarred } from '../actions/starredMessages';
|
||||
import { pinnedMessagesReceived, pinnedMessageUnpinned } from '../actions/pinnedMessages';
|
||||
import { mentionedMessagesReceived } from '../actions/mentionedMessages';
|
||||
import { snippetedMessagesReceived } from '../actions/snippetedMessages';
|
||||
import { roomFilesReceived } from '../actions/roomFiles';
|
||||
import { someoneTyping, roomMessageReceived } from '../actions/room';
|
||||
import { setRoles } from '../actions/roles';
|
||||
|
||||
|
@ -199,79 +195,6 @@ const RocketChat = {
|
|||
}
|
||||
}));
|
||||
|
||||
SDK.driver.on('rocketchat_starred_message', protectedFunction((error, ddpMessage) => {
|
||||
if (ddpMessage.msg === 'added') {
|
||||
this.starredMessages = this.starredMessages || [];
|
||||
|
||||
if (this.starredMessagesTimer) {
|
||||
clearTimeout(this.starredMessagesTimer);
|
||||
this.starredMessagesTimer = null;
|
||||
}
|
||||
|
||||
this.starredMessagesTimer = setTimeout(protectedFunction(() => {
|
||||
reduxStore.dispatch(starredMessagesReceived(this.starredMessages));
|
||||
this.starredMessagesTimer = null;
|
||||
return this.starredMessages = [];
|
||||
}), 1000);
|
||||
const message = ddpMessage.fields;
|
||||
message._id = ddpMessage.id;
|
||||
const starredMessage = _buildMessage(message);
|
||||
this.starredMessages = [...this.starredMessages, starredMessage];
|
||||
}
|
||||
if (ddpMessage.msg === 'removed') {
|
||||
if (reduxStore.getState().starredMessages.isOpen) {
|
||||
return reduxStore.dispatch(starredMessageUnstarred(ddpMessage.id));
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
SDK.driver.on('rocketchat_pinned_message', protectedFunction((error, ddpMessage) => {
|
||||
if (ddpMessage.msg === 'added') {
|
||||
this.pinnedMessages = this.pinnedMessages || [];
|
||||
|
||||
if (this.pinnedMessagesTimer) {
|
||||
clearTimeout(this.pinnedMessagesTimer);
|
||||
this.pinnedMessagesTimer = null;
|
||||
}
|
||||
|
||||
this.pinnedMessagesTimer = setTimeout(() => {
|
||||
reduxStore.dispatch(pinnedMessagesReceived(this.pinnedMessages));
|
||||
this.pinnedMessagesTimer = null;
|
||||
return this.pinnedMessages = [];
|
||||
}, 1000);
|
||||
const message = ddpMessage.fields;
|
||||
message._id = ddpMessage.id;
|
||||
const pinnedMessage = _buildMessage(message);
|
||||
this.pinnedMessages = [...this.pinnedMessages, pinnedMessage];
|
||||
}
|
||||
if (ddpMessage.msg === 'removed') {
|
||||
if (reduxStore.getState().pinnedMessages.isOpen) {
|
||||
return reduxStore.dispatch(pinnedMessageUnpinned(ddpMessage.id));
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
SDK.driver.on('rocketchat_mentioned_message', protectedFunction((error, ddpMessage) => {
|
||||
if (ddpMessage.msg === 'added') {
|
||||
this.mentionedMessages = this.mentionedMessages || [];
|
||||
|
||||
if (this.mentionedMessagesTimer) {
|
||||
clearTimeout(this.mentionedMessagesTimer);
|
||||
this.mentionedMessagesTimer = null;
|
||||
}
|
||||
|
||||
this.mentionedMessagesTimer = setTimeout(() => {
|
||||
reduxStore.dispatch(mentionedMessagesReceived(this.mentionedMessages));
|
||||
this.mentionedMessagesTimer = null;
|
||||
return this.mentionedMessages = [];
|
||||
}, 1000);
|
||||
const message = ddpMessage.fields;
|
||||
message._id = ddpMessage.id;
|
||||
const mentionedMessage = _buildMessage(message);
|
||||
this.mentionedMessages = [...this.mentionedMessages, mentionedMessage];
|
||||
}
|
||||
}));
|
||||
|
||||
SDK.driver.on('rocketchat_snippeted_message', protectedFunction((error, ddpMessage) => {
|
||||
if (ddpMessage.msg === 'added') {
|
||||
this.snippetedMessages = this.snippetedMessages || [];
|
||||
|
@ -293,50 +216,6 @@ const RocketChat = {
|
|||
}
|
||||
}));
|
||||
|
||||
SDK.driver.on('room_files', protectedFunction((error, ddpMessage) => {
|
||||
if (ddpMessage.msg === 'added') {
|
||||
this.roomFiles = this.roomFiles || [];
|
||||
|
||||
if (this.roomFilesTimer) {
|
||||
clearTimeout(this.roomFilesTimer);
|
||||
this.roomFilesTimer = null;
|
||||
}
|
||||
|
||||
this.roomFilesTimer = setTimeout(() => {
|
||||
reduxStore.dispatch(roomFilesReceived(this.roomFiles));
|
||||
this.roomFilesTimer = null;
|
||||
return this.roomFiles = [];
|
||||
}, 1000);
|
||||
const { fields } = ddpMessage;
|
||||
const message = {
|
||||
_id: ddpMessage.id,
|
||||
ts: fields.uploadedAt,
|
||||
msg: fields.description,
|
||||
status: 0,
|
||||
attachments: [{
|
||||
title: fields.name
|
||||
}],
|
||||
urls: [],
|
||||
reactions: [],
|
||||
u: {
|
||||
username: fields.user.username
|
||||
}
|
||||
};
|
||||
const fileUrl = `/file-upload/${ ddpMessage.id }/${ fields.name }`;
|
||||
if (/image/.test(fields.type)) {
|
||||
message.attachments[0].image_type = fields.type;
|
||||
message.attachments[0].image_url = fileUrl;
|
||||
} else if (/audio/.test(fields.type)) {
|
||||
message.attachments[0].audio_type = fields.type;
|
||||
message.attachments[0].audio_url = fileUrl;
|
||||
} else if (/video/.test(fields.type)) {
|
||||
message.attachments[0].video_type = fields.type;
|
||||
message.attachments[0].video_url = fileUrl;
|
||||
}
|
||||
this.roomFiles = [...this.roomFiles, message];
|
||||
}
|
||||
}));
|
||||
|
||||
SDK.driver.on('rocketchat_roles', protectedFunction((error, ddpMessage) => {
|
||||
this.roles = this.roles || {};
|
||||
|
||||
|
@ -678,7 +557,7 @@ const RocketChat = {
|
|||
async getRoomMember(rid, currentUserId) {
|
||||
try {
|
||||
const membersResult = await RocketChat.getRoomMembers(rid, true);
|
||||
return Promise.resolve(membersResult.records.find(m => m.id !== currentUserId));
|
||||
return Promise.resolve(membersResult.records.find(m => m._id !== currentUserId));
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
@ -692,8 +571,8 @@ const RocketChat = {
|
|||
leaveRoom(roomId, t) {
|
||||
return SDK.api.post(`${ this.roomTypeToApiType(t) }.leave`, { roomId });
|
||||
},
|
||||
eraseRoom(rid) {
|
||||
return call('eraseRoom', rid);
|
||||
eraseRoom(roomId, t) {
|
||||
return SDK.api.post(`${ this.roomTypeToApiType(t) }.delete`, { roomId });
|
||||
},
|
||||
toggleMuteUserInRoom(rid, username, mute) {
|
||||
if (mute) {
|
||||
|
@ -701,17 +580,17 @@ const RocketChat = {
|
|||
}
|
||||
return call('unmuteUserInRoom', { rid, username });
|
||||
},
|
||||
toggleArchiveRoom(rid, archive) {
|
||||
toggleArchiveRoom(roomId, t, archive) {
|
||||
if (archive) {
|
||||
return call('archiveRoom', rid);
|
||||
return SDK.api.post(`${ this.roomTypeToApiType(t) }.archive`, { roomId });
|
||||
}
|
||||
return call('unarchiveRoom', rid);
|
||||
return SDK.api.post(`${ this.roomTypeToApiType(t) }.unarchive`, { roomId });
|
||||
},
|
||||
saveRoomSettings(rid, params) {
|
||||
return call('saveRoomSettings', rid, params);
|
||||
},
|
||||
saveUserProfile(params, customFields) {
|
||||
return call('saveUserProfile', params, customFields);
|
||||
saveUserProfile(data) {
|
||||
return SDK.api.post('users.updateOwnBasicInfo', { data });
|
||||
},
|
||||
saveUserPreferences(params) {
|
||||
return call('saveUserPreferences', params);
|
||||
|
@ -719,9 +598,6 @@ const RocketChat = {
|
|||
saveNotificationSettings(roomId, notifications) {
|
||||
return SDK.api.post('rooms.saveNotification', { roomId, notifications });
|
||||
},
|
||||
messageSearch(text, rid, limit) {
|
||||
return call('messageSearch', text, rid, limit);
|
||||
},
|
||||
addUsersToRoom(rid) {
|
||||
let { users } = reduxStore.getState().selectedUsers;
|
||||
users = users.map(u => u.name);
|
||||
|
@ -756,8 +632,8 @@ const RocketChat = {
|
|||
getAvatarSuggestion() {
|
||||
return call('getAvatarSuggestion');
|
||||
},
|
||||
resetAvatar() {
|
||||
return call('resetAvatar');
|
||||
resetAvatar(userId) {
|
||||
return SDK.api.post('users.resetAvatar', { userId });
|
||||
},
|
||||
setAvatarFromService({ data, contentType = '', service = null }) {
|
||||
return call('setAvatarFromService', data, contentType, service);
|
||||
|
@ -804,6 +680,30 @@ const RocketChat = {
|
|||
c: 'channels', d: 'im', p: 'groups'
|
||||
};
|
||||
return types[t];
|
||||
},
|
||||
getFiles(roomId, type, offset) {
|
||||
return SDK.api.get(`${ this.roomTypeToApiType(type) }.files`, {
|
||||
roomId,
|
||||
offset,
|
||||
sort: { uploadedAt: -1 },
|
||||
fields: {
|
||||
name: 1, description: 1, size: 1, type: 1, uploadedAt: 1, url: 1, userId: 1
|
||||
}
|
||||
});
|
||||
},
|
||||
getMessages(roomId, type, query, offset) {
|
||||
return SDK.api.get(`${ this.roomTypeToApiType(type) }.messages`, {
|
||||
roomId,
|
||||
query,
|
||||
offset,
|
||||
sort: { ts: -1 }
|
||||
});
|
||||
},
|
||||
searchMessages(roomId, searchText) {
|
||||
return SDK.api.get('chat.search', {
|
||||
roomId,
|
||||
searchText
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -12,11 +12,7 @@ import app from './app';
|
|||
import customEmojis from './customEmojis';
|
||||
import activeUsers from './activeUsers';
|
||||
import roles from './roles';
|
||||
import starredMessages from './starredMessages';
|
||||
import pinnedMessages from './pinnedMessages';
|
||||
import mentionedMessages from './mentionedMessages';
|
||||
import snippetedMessages from './snippetedMessages';
|
||||
import roomFiles from './roomFiles';
|
||||
import sortPreferences from './sortPreferences';
|
||||
|
||||
export default combineReducers({
|
||||
|
@ -33,10 +29,6 @@ export default combineReducers({
|
|||
customEmojis,
|
||||
activeUsers,
|
||||
roles,
|
||||
starredMessages,
|
||||
pinnedMessages,
|
||||
mentionedMessages,
|
||||
snippetedMessages,
|
||||
roomFiles,
|
||||
sortPreferences
|
||||
});
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
import { MENTIONED_MESSAGES } from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
messages: [],
|
||||
ready: false
|
||||
};
|
||||
|
||||
export default function server(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case MENTIONED_MESSAGES.OPEN:
|
||||
return {
|
||||
...state,
|
||||
ready: false
|
||||
};
|
||||
case MENTIONED_MESSAGES.READY:
|
||||
return {
|
||||
...state,
|
||||
ready: true
|
||||
};
|
||||
case MENTIONED_MESSAGES.MESSAGES_RECEIVED:
|
||||
return {
|
||||
...state,
|
||||
messages: [...state.messages, ...action.messages]
|
||||
};
|
||||
case MENTIONED_MESSAGES.CLOSE:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
import { PINNED_MESSAGES } from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
messages: [],
|
||||
isOpen: false,
|
||||
ready: false
|
||||
};
|
||||
|
||||
export default function server(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case PINNED_MESSAGES.OPEN:
|
||||
return {
|
||||
...state,
|
||||
isOpen: true,
|
||||
ready: false
|
||||
};
|
||||
case PINNED_MESSAGES.READY:
|
||||
return {
|
||||
...state,
|
||||
ready: true
|
||||
};
|
||||
case PINNED_MESSAGES.MESSAGES_RECEIVED:
|
||||
return {
|
||||
...state,
|
||||
messages: [...state.messages, ...action.messages]
|
||||
};
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import { ROOM_FILES } from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
messages: [],
|
||||
ready: false
|
||||
};
|
||||
|
||||
export default function server(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case ROOM_FILES.OPEN:
|
||||
return {
|
||||
...state,
|
||||
ready: false
|
||||
};
|
||||
case ROOM_FILES.READY:
|
||||
return {
|
||||
...state,
|
||||
ready: true
|
||||
};
|
||||
case ROOM_FILES.MESSAGES_RECEIVED:
|
||||
return {
|
||||
...state,
|
||||
messages: [...state.messages, ...action.messages]
|
||||
};
|
||||
case ROOM_FILES.CLOSE:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
import { STARRED_MESSAGES } from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
messages: [],
|
||||
isOpen: false,
|
||||
ready: false
|
||||
};
|
||||
|
||||
export default function server(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case STARRED_MESSAGES.OPEN:
|
||||
return {
|
||||
...state,
|
||||
isOpen: true,
|
||||
ready: false
|
||||
};
|
||||
case STARRED_MESSAGES.READY:
|
||||
return {
|
||||
...state,
|
||||
ready: true
|
||||
};
|
||||
case STARRED_MESSAGES.MESSAGES_RECEIVED:
|
||||
return {
|
||||
...state,
|
||||
messages: [...state.messages, ...action.messages]
|
||||
};
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -6,11 +6,7 @@ import selectServer from './selectServer';
|
|||
import createChannel from './createChannel';
|
||||
import init from './init';
|
||||
import state from './state';
|
||||
import starredMessages from './starredMessages';
|
||||
import pinnedMessages from './pinnedMessages';
|
||||
import mentionedMessages from './mentionedMessages';
|
||||
import snippetedMessages from './snippetedMessages';
|
||||
import roomFiles from './roomFiles';
|
||||
import deepLinking from './deepLinking';
|
||||
|
||||
const root = function* root() {
|
||||
|
@ -22,11 +18,7 @@ const root = function* root() {
|
|||
messages(),
|
||||
selectServer(),
|
||||
state(),
|
||||
starredMessages(),
|
||||
pinnedMessages(),
|
||||
mentionedMessages(),
|
||||
snippetedMessages(),
|
||||
roomFiles(),
|
||||
deepLinking()
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -33,7 +33,8 @@ const handleLoginRequest = function* handleLoginRequest({ credentials }) {
|
|||
username: data.me.username,
|
||||
name: data.me.name,
|
||||
language: data.me.language,
|
||||
status: data.me.status
|
||||
status: data.me.status,
|
||||
customFields: data.me.customFields
|
||||
};
|
||||
return yield put(loginSuccess(user));
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import { put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { readyMentionedMessages } from '../actions/mentionedMessages';
|
||||
import log from '../utils/log';
|
||||
|
||||
let sub;
|
||||
let newSub;
|
||||
|
||||
const openMentionedMessagesRoom = function* openMentionedMessagesRoom({ rid, limit }) {
|
||||
try {
|
||||
newSub = yield RocketChat.subscribe('mentionedMessages', rid, limit);
|
||||
yield put(readyMentionedMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe().catch(err => console.warn(err));
|
||||
}
|
||||
sub = newSub;
|
||||
} catch (e) {
|
||||
log('openMentionedMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
const closeMentionedMessagesRoom = function* closeMentionedMessagesRoom() {
|
||||
try {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe();
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe();
|
||||
}
|
||||
} catch (e) {
|
||||
log('closeMentionedMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(types.MENTIONED_MESSAGES.OPEN, openMentionedMessagesRoom);
|
||||
yield takeLatest(types.MENTIONED_MESSAGES.CLOSE, closeMentionedMessagesRoom);
|
||||
};
|
||||
export default root;
|
|
@ -1,41 +0,0 @@
|
|||
import { put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { readyPinnedMessages } from '../actions/pinnedMessages';
|
||||
import log from '../utils/log';
|
||||
|
||||
let sub;
|
||||
let newSub;
|
||||
|
||||
const openPinnedMessagesRoom = function* openPinnedMessagesRoom({ rid, limit }) {
|
||||
try {
|
||||
newSub = yield RocketChat.subscribe('pinnedMessages', rid, limit);
|
||||
yield put(readyPinnedMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe().catch(err => console.warn(err));
|
||||
}
|
||||
sub = newSub;
|
||||
} catch (e) {
|
||||
log('openPinnedMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
const closePinnedMessagesRoom = function* closePinnedMessagesRoom() {
|
||||
try {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe();
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe();
|
||||
}
|
||||
} catch (e) {
|
||||
log('closePinnedMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(types.PINNED_MESSAGES.OPEN, openPinnedMessagesRoom);
|
||||
yield takeLatest(types.PINNED_MESSAGES.CLOSE, closePinnedMessagesRoom);
|
||||
};
|
||||
export default root;
|
|
@ -1,43 +0,0 @@
|
|||
import { put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { readyRoomFiles } from '../actions/roomFiles';
|
||||
import log from '../utils/log';
|
||||
|
||||
let sub;
|
||||
let newSub;
|
||||
|
||||
const openRoomFiles = function* openRoomFiles({ rid, limit }) {
|
||||
try {
|
||||
sub = yield RocketChat.subscribe('roomFiles', rid, limit);
|
||||
yield put(readyRoomFiles());
|
||||
if (sub) {
|
||||
sub.unsubscribe().catch(err => console.warn(err));
|
||||
}
|
||||
sub = newSub;
|
||||
} catch (e) {
|
||||
log('openRoomFiles', e);
|
||||
}
|
||||
};
|
||||
|
||||
const closeRoomFiles = function* closeRoomFiles() {
|
||||
try {
|
||||
// yield sub.unsubscribe(sub);
|
||||
// sub = null;
|
||||
if (sub) {
|
||||
yield sub.unsubscribe();
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe();
|
||||
}
|
||||
} catch (e) {
|
||||
log('closeRoomFiles', e);
|
||||
}
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(types.ROOM_FILES.OPEN, openRoomFiles);
|
||||
yield takeLatest(types.ROOM_FILES.CLOSE, closeRoomFiles);
|
||||
};
|
||||
export default root;
|
|
@ -13,8 +13,6 @@ import database from '../lib/realm';
|
|||
import log from '../utils/log';
|
||||
import I18n from '../i18n';
|
||||
|
||||
const eraseRoom = rid => RocketChat.eraseRoom(rid);
|
||||
|
||||
let sub;
|
||||
let thread;
|
||||
|
||||
|
@ -120,25 +118,13 @@ const watchuserTyping = function* watchuserTyping({ status }) {
|
|||
}
|
||||
};
|
||||
|
||||
const goRoomsListAndDelete = function* goRoomsListAndDelete(rid) {
|
||||
yield Navigation.popToRoot('RoomsListView');
|
||||
try {
|
||||
database.write(() => {
|
||||
const messages = database.objects('messages').filtered('rid = $0', rid);
|
||||
database.delete(messages);
|
||||
const subscription = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||
database.delete(subscription);
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('goRoomsListAndDelete', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLeaveRoom = function* handleLeaveRoom({ rid, t }) {
|
||||
try {
|
||||
sub.stop();
|
||||
yield RocketChat.leaveRoom(rid, t);
|
||||
yield goRoomsListAndDelete(rid);
|
||||
const result = yield RocketChat.leaveRoom(rid, t);
|
||||
if (result.success) {
|
||||
yield Navigation.popToRoot('RoomsListView');
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.data && e.data.errorType === 'error-you-are-last-owner') {
|
||||
Alert.alert(I18n.t('Oops'), I18n.t(e.data.errorType));
|
||||
|
@ -148,11 +134,13 @@ const handleLeaveRoom = function* handleLeaveRoom({ rid, t }) {
|
|||
}
|
||||
};
|
||||
|
||||
const handleEraseRoom = function* handleEraseRoom({ rid }) {
|
||||
const handleEraseRoom = function* handleEraseRoom({ rid, t }) {
|
||||
try {
|
||||
sub.stop();
|
||||
yield eraseRoom(rid);
|
||||
yield goRoomsListAndDelete(rid, 'erase');
|
||||
const result = yield RocketChat.eraseRoom(rid, t);
|
||||
if (result.success) {
|
||||
yield Navigation.popToRoot('RoomsListView');
|
||||
}
|
||||
} catch (e) {
|
||||
Alert.alert(I18n.t('Oops'), I18n.t('There_was_an_error_while_action', { action: I18n.t('erasing_room') }));
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import { put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { readyStarredMessages } from '../actions/starredMessages';
|
||||
import log from '../utils/log';
|
||||
|
||||
let sub;
|
||||
let newSub;
|
||||
|
||||
const openStarredMessagesRoom = function* openStarredMessagesRoom({ rid, limit }) {
|
||||
try {
|
||||
newSub = yield RocketChat.subscribe('starredMessages', rid, limit);
|
||||
yield put(readyStarredMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe().catch(err => console.warn(err));
|
||||
}
|
||||
sub = newSub;
|
||||
} catch (e) {
|
||||
log('openStarredMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
const closeStarredMessagesRoom = function* closeStarredMessagesRoom() {
|
||||
try {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe();
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe();
|
||||
}
|
||||
} catch (e) {
|
||||
log('closeStarredMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(types.STARRED_MESSAGES.OPEN, openStarredMessagesRoom);
|
||||
yield takeLatest(types.STARRED_MESSAGES.CLOSE, closeStarredMessagesRoom);
|
||||
};
|
||||
export default root;
|
|
@ -3,26 +3,25 @@ import PropTypes from 'prop-types';
|
|||
import { FlatList, View, Text } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
import equal from 'deep-equal';
|
||||
|
||||
import { openMentionedMessages as openMentionedMessagesAction, closeMentionedMessages as closeMentionedMessagesAction } from '../../actions/mentionedMessages';
|
||||
import LoggedView from '../View';
|
||||
import styles from './styles';
|
||||
import Message from '../../containers/message';
|
||||
import Message from '../../containers/message/Message';
|
||||
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||
import I18n from '../../i18n';
|
||||
import { DEFAULT_HEADER } from '../../constants/headerOptions';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import database from '../../lib/realm';
|
||||
|
||||
@connect(state => ({
|
||||
messages: state.mentionedMessages.messages,
|
||||
ready: state.mentionedMessages.ready,
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||
customEmojis: state.customEmojis,
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
username: state.login.user && state.login.user.username,
|
||||
token: state.login.user && state.login.user.token
|
||||
}
|
||||
}), dispatch => ({
|
||||
openMentionedMessages: (rid, limit) => dispatch(openMentionedMessagesAction(rid, limit)),
|
||||
closeMentionedMessages: () => dispatch(closeMentionedMessagesAction())
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class MentionedMessagesView extends LoggedView {
|
||||
|
@ -41,53 +40,57 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
|
||||
static propTypes = {
|
||||
rid: PropTypes.string,
|
||||
messages: PropTypes.array,
|
||||
ready: PropTypes.bool,
|
||||
user: PropTypes.object,
|
||||
openMentionedMessages: PropTypes.func,
|
||||
closeMentionedMessages: PropTypes.func
|
||||
baseUrl: PropTypes.string,
|
||||
customEmojis: PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super('MentionedMessagesView', props);
|
||||
super('StarredMessagesView', props);
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', props.rid);
|
||||
this.state = {
|
||||
loading: true,
|
||||
loadingMore: false
|
||||
loading: false,
|
||||
room: this.rooms[0],
|
||||
messages: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.limit = 20;
|
||||
this.load();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { ready } = this.props;
|
||||
if (nextProps.ready && nextProps.ready !== ready) {
|
||||
this.setState({ loading: false, loadingMore: false });
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !equal(this.state, nextState);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { closeMentionedMessages } = this.props;
|
||||
closeMentionedMessages();
|
||||
}
|
||||
|
||||
load = () => {
|
||||
const { openMentionedMessages, rid } = this.props;
|
||||
openMentionedMessages(rid, this.limit);
|
||||
}
|
||||
|
||||
moreData = () => {
|
||||
const { loadingMore } = this.state;
|
||||
const { messages } = this.props;
|
||||
if (messages.length < this.limit) {
|
||||
load = async() => {
|
||||
const {
|
||||
messages, total, loading, room
|
||||
} = this.state;
|
||||
const { user } = this.props;
|
||||
if (messages.length === total || loading) {
|
||||
return;
|
||||
}
|
||||
if (!loadingMore) {
|
||||
this.setState({ loadingMore: true });
|
||||
this.limit += 20;
|
||||
this.load();
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
try {
|
||||
const result = await RocketChat.getMessages(
|
||||
room.rid,
|
||||
room.t,
|
||||
{ 'mentions._id': { $in: [user.id] } },
|
||||
messages.length
|
||||
);
|
||||
if (result.success) {
|
||||
this.setState(prevState => ({
|
||||
messages: [...prevState.messages, ...result.messages],
|
||||
total: result.total,
|
||||
loading: false
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({ loading: false });
|
||||
console.log('MentionedMessagesView -> load -> catch -> error', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,23 +101,28 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
)
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const { user } = this.props;
|
||||
const { user, customEmojis, baseUrl } = this.props;
|
||||
return (
|
||||
<Message
|
||||
item={item}
|
||||
style={styles.message}
|
||||
reactions={item.reactions}
|
||||
customEmojis={customEmojis}
|
||||
baseUrl={baseUrl}
|
||||
user={user}
|
||||
customTimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||
author={item.u}
|
||||
ts={item.ts}
|
||||
msg={item.msg}
|
||||
attachments={item.attachments || []}
|
||||
timeFormat='MMM Do YYYY, h:mm:ss a'
|
||||
edited={!!item.editedAt}
|
||||
header
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, loadingMore } = this.state;
|
||||
const { messages, ready } = this.props;
|
||||
const { messages, loading } = this.state;
|
||||
|
||||
if (ready && messages.length === 0) {
|
||||
if (!loading && messages.length === 0) {
|
||||
return this.renderEmpty();
|
||||
}
|
||||
|
||||
|
@ -125,9 +133,8 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
renderItem={this.renderItem}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
onEndReached={this.moreData}
|
||||
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||
onEndReached={this.load}
|
||||
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
|
|
@ -4,32 +4,29 @@ import { FlatList, View, Text } from 'react-native';
|
|||
import { connect } from 'react-redux';
|
||||
import ActionSheet from 'react-native-actionsheet';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
import equal from 'deep-equal';
|
||||
|
||||
import { openPinnedMessages as openPinnedMessagesAction, closePinnedMessages as closePinnedMessagesAction } from '../../actions/pinnedMessages';
|
||||
import { togglePinRequest as togglePinRequestAction } from '../../actions/messages';
|
||||
import LoggedView from '../View';
|
||||
import styles from './styles';
|
||||
import Message from '../../containers/message';
|
||||
import Message from '../../containers/message/Message';
|
||||
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||
import I18n from '../../i18n';
|
||||
import { DEFAULT_HEADER } from '../../constants/headerOptions';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import database from '../../lib/realm';
|
||||
|
||||
const PIN_INDEX = 0;
|
||||
const CANCEL_INDEX = 1;
|
||||
const options = [I18n.t('Unpin'), I18n.t('Cancel')];
|
||||
|
||||
@connect(state => ({
|
||||
messages: state.pinnedMessages.messages,
|
||||
ready: state.pinnedMessages.ready,
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||
customEmojis: state.customEmojis,
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
username: state.login.user && state.login.user.username,
|
||||
token: state.login.user && state.login.user.token
|
||||
}
|
||||
}), dispatch => ({
|
||||
openPinnedMessages: (rid, limit) => dispatch(openPinnedMessagesAction(rid, limit)),
|
||||
closePinnedMessages: () => dispatch(closePinnedMessagesAction()),
|
||||
togglePinRequest: message => dispatch(togglePinRequestAction(message))
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class PinnedMessagesView extends LoggedView {
|
||||
|
@ -48,38 +45,27 @@ export default class PinnedMessagesView extends LoggedView {
|
|||
|
||||
static propTypes = {
|
||||
rid: PropTypes.string,
|
||||
messages: PropTypes.array,
|
||||
ready: PropTypes.bool,
|
||||
user: PropTypes.object,
|
||||
openPinnedMessages: PropTypes.func,
|
||||
closePinnedMessages: PropTypes.func,
|
||||
togglePinRequest: PropTypes.func
|
||||
baseUrl: PropTypes.string,
|
||||
customEmojis: PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super('PinnedMessagesView', props);
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', props.rid);
|
||||
this.state = {
|
||||
message: {},
|
||||
loading: true,
|
||||
loadingMore: false
|
||||
loading: false,
|
||||
room: this.rooms[0],
|
||||
messages: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.limit = 20;
|
||||
this.load();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { ready } = this.props;
|
||||
if (nextProps.ready && nextProps.ready !== ready) {
|
||||
this.setState({ loading: false, loadingMore: false });
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { closePinnedMessages } = this.props;
|
||||
closePinnedMessages();
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !equal(this.state, nextState);
|
||||
}
|
||||
|
||||
onLongPress = (message) => {
|
||||
|
@ -90,33 +76,51 @@ export default class PinnedMessagesView extends LoggedView {
|
|||
}
|
||||
|
||||
handleActionPress = (actionIndex) => {
|
||||
const { message } = this.state;
|
||||
const { togglePinRequest } = this.props;
|
||||
|
||||
switch (actionIndex) {
|
||||
case PIN_INDEX:
|
||||
togglePinRequest(message);
|
||||
this.unPin();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
load = () => {
|
||||
const { openPinnedMessages, rid } = this.props;
|
||||
openPinnedMessages(rid, this.limit);
|
||||
unPin = async() => {
|
||||
const { message } = this.state;
|
||||
try {
|
||||
const result = await RocketChat.togglePinMessage(message);
|
||||
if (result.success) {
|
||||
this.setState(prevState => ({
|
||||
messages: prevState.messages.filter(item => item._id !== message._id)
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('PinnedMessagesView -> unPin -> catch -> error', error);
|
||||
}
|
||||
}
|
||||
|
||||
moreData = () => {
|
||||
const { loadingMore } = this.state;
|
||||
const { messages } = this.props;
|
||||
if (messages.length < this.limit) {
|
||||
load = async() => {
|
||||
const {
|
||||
messages, total, loading, room
|
||||
} = this.state;
|
||||
if (messages.length === total || loading) {
|
||||
return;
|
||||
}
|
||||
if (!loadingMore) {
|
||||
this.setState({ loadingMore: true });
|
||||
this.limit += 20;
|
||||
this.load();
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
try {
|
||||
const result = await RocketChat.getMessages(room.rid, room.t, { pinned: true }, messages.length);
|
||||
if (result.success) {
|
||||
this.setState(prevState => ({
|
||||
messages: [...prevState.messages, ...result.messages],
|
||||
total: result.total,
|
||||
loading: false
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({ loading: false });
|
||||
console.log('PinnedMessagesView -> catch -> error', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,24 +131,29 @@ export default class PinnedMessagesView extends LoggedView {
|
|||
)
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const { user } = this.props;
|
||||
const { user, customEmojis, baseUrl } = this.props;
|
||||
return (
|
||||
<Message
|
||||
item={item}
|
||||
style={styles.message}
|
||||
reactions={item.reactions}
|
||||
customEmojis={customEmojis}
|
||||
baseUrl={baseUrl}
|
||||
user={user}
|
||||
customTimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||
onLongPress={this.onLongPress}
|
||||
author={item.u}
|
||||
ts={item.ts}
|
||||
msg={item.msg}
|
||||
attachments={item.attachments || []}
|
||||
timeFormat='MMM Do YYYY, h:mm:ss a'
|
||||
header
|
||||
edited={!!item.editedAt}
|
||||
onLongPress={() => this.onLongPress(item)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, loadingMore } = this.state;
|
||||
const { messages, ready } = this.props;
|
||||
const { messages, loading } = this.state;
|
||||
|
||||
if (ready && messages.length === 0) {
|
||||
if (!loading && messages.length === 0) {
|
||||
return this.renderEmpty();
|
||||
}
|
||||
|
||||
|
@ -155,9 +164,8 @@ export default class PinnedMessagesView extends LoggedView {
|
|||
renderItem={this.renderItem}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
onEndReached={this.moreData}
|
||||
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||
onEndReached={this.load}
|
||||
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
|
||||
/>
|
||||
<ActionSheet
|
||||
ref={o => this.actionSheet = o}
|
||||
|
|
|
@ -20,7 +20,6 @@ import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
|||
import { showErrorAlert, showToast } from '../../utils/info';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import RCTextInput from '../../containers/TextInput';
|
||||
import Loading from '../../containers/Loading';
|
||||
import log from '../../utils/log';
|
||||
import I18n from '../../i18n';
|
||||
import Button from '../../containers/Button';
|
||||
|
@ -29,9 +28,11 @@ import Touch from '../../utils/touch';
|
|||
import Drawer from '../../Drawer';
|
||||
import { DEFAULT_HEADER } from '../../constants/headerOptions';
|
||||
import { appStart as appStartAction } from '../../actions';
|
||||
import { setUser as setUserAction } from '../../actions/login';
|
||||
|
||||
@connect(state => ({
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
name: state.login.user && state.login.user.name,
|
||||
username: state.login.user && state.login.user.username,
|
||||
customFields: state.login.user && state.login.user.customFields,
|
||||
|
@ -40,7 +41,8 @@ import { appStart as appStartAction } from '../../actions';
|
|||
Accounts_CustomFields: state.settings.Accounts_CustomFields,
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||
}), dispatch => ({
|
||||
appStart: () => dispatch(appStartAction())
|
||||
appStart: () => dispatch(appStartAction()),
|
||||
setUser: params => dispatch(setUserAction(params))
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class ProfileView extends LoggedView {
|
||||
|
@ -75,7 +77,8 @@ export default class ProfileView extends LoggedView {
|
|||
componentId: PropTypes.string,
|
||||
user: PropTypes.object,
|
||||
Accounts_CustomFields: PropTypes.string,
|
||||
appStart: PropTypes.func
|
||||
appStart: PropTypes.func,
|
||||
setUser: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -87,7 +90,7 @@ export default class ProfileView extends LoggedView {
|
|||
username: null,
|
||||
email: null,
|
||||
newPassword: null,
|
||||
typedPassword: null,
|
||||
currentPassword: null,
|
||||
avatarUrl: null,
|
||||
avatar: {},
|
||||
avatarSuggestions: {},
|
||||
|
@ -146,7 +149,7 @@ export default class ProfileView extends LoggedView {
|
|||
username,
|
||||
email: emails ? emails[0].address : null,
|
||||
newPassword: null,
|
||||
typedPassword: null,
|
||||
currentPassword: null,
|
||||
avatarUrl: null,
|
||||
avatar: {},
|
||||
customFields: customFields || {}
|
||||
|
@ -163,7 +166,7 @@ export default class ProfileView extends LoggedView {
|
|||
const customFieldsKeys = Object.keys(customFields);
|
||||
if (customFieldsKeys.length) {
|
||||
customFieldsKeys.forEach((key) => {
|
||||
if (user.customFields[key] !== customFields[key]) {
|
||||
if (!user.customFields || user.customFields[key] !== customFields[key]) {
|
||||
customFieldsChanged = true;
|
||||
}
|
||||
});
|
||||
|
@ -183,13 +186,8 @@ export default class ProfileView extends LoggedView {
|
|||
}
|
||||
|
||||
handleError = (e, func, action) => {
|
||||
if (e && e.error && e.error !== 500) {
|
||||
if (e.details && e.details.timeToReset) {
|
||||
return showErrorAlert(I18n.t('error-too-many-requests', {
|
||||
seconds: parseInt(e.details.timeToReset / 1000, 10)
|
||||
}));
|
||||
}
|
||||
return showErrorAlert(I18n.t(e.error, e.details));
|
||||
if (e.data && e.data.errorType === 'error-too-many-requests') {
|
||||
return showErrorAlert(e.data.error);
|
||||
}
|
||||
showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t(action) }));
|
||||
log(func, e);
|
||||
|
@ -205,14 +203,14 @@ export default class ProfileView extends LoggedView {
|
|||
this.setState({ saving: true, showPasswordAlert: false });
|
||||
|
||||
const {
|
||||
name, username, email, newPassword, typedPassword, avatar, customFields
|
||||
name, username, email, newPassword, currentPassword, avatar, customFields
|
||||
} = this.state;
|
||||
const { user } = this.props;
|
||||
const { user, setUser } = this.props;
|
||||
const params = {};
|
||||
|
||||
// Name
|
||||
if (user.name !== name) {
|
||||
params.realname = name;
|
||||
params.name = name;
|
||||
}
|
||||
|
||||
// Username
|
||||
|
@ -230,13 +228,13 @@ export default class ProfileView extends LoggedView {
|
|||
params.newPassword = newPassword;
|
||||
}
|
||||
|
||||
// typedPassword
|
||||
if (typedPassword) {
|
||||
params.typedPassword = SHA256(typedPassword);
|
||||
// currentPassword
|
||||
if (currentPassword) {
|
||||
params.currentPassword = SHA256(currentPassword);
|
||||
}
|
||||
|
||||
const requirePassword = !!params.email || newPassword;
|
||||
if (requirePassword && !params.typedPassword) {
|
||||
if (requirePassword && !params.currentPassword) {
|
||||
return this.setState({ showPasswordAlert: true, saving: false });
|
||||
}
|
||||
|
||||
|
@ -245,28 +243,32 @@ export default class ProfileView extends LoggedView {
|
|||
try {
|
||||
await RocketChat.setAvatarFromService(avatar);
|
||||
} catch (e) {
|
||||
this.setState({ saving: false, typedPassword: null });
|
||||
return setTimeout(() => this.handleError(e, 'setAvatarFromService', 'changing_avatar'), 300);
|
||||
this.setState({ saving: false, currentPassword: null });
|
||||
return this.handleError(e, 'setAvatarFromService', 'changing_avatar');
|
||||
}
|
||||
}
|
||||
|
||||
await RocketChat.saveUserProfile(params, customFields);
|
||||
this.setState({ saving: false });
|
||||
setTimeout(() => {
|
||||
params.customFields = customFields;
|
||||
|
||||
const result = await RocketChat.saveUserProfile(params);
|
||||
if (result.success) {
|
||||
if (params.customFields) {
|
||||
setUser({ customFields });
|
||||
}
|
||||
this.setState({ saving: false });
|
||||
showToast(I18n.t('Profile_saved_successfully'));
|
||||
this.init();
|
||||
}, 300);
|
||||
}
|
||||
} catch (e) {
|
||||
this.setState({ saving: false, typedPassword: null });
|
||||
setTimeout(() => {
|
||||
this.handleError(e, 'saveUserProfile', 'saving_profile');
|
||||
}, 300);
|
||||
this.setState({ saving: false, currentPassword: null });
|
||||
this.handleError(e, 'saveUserProfile', 'saving_profile');
|
||||
}
|
||||
}
|
||||
|
||||
resetAvatar = async() => {
|
||||
try {
|
||||
await RocketChat.resetAvatar();
|
||||
const { user } = this.props;
|
||||
await RocketChat.resetAvatar(user.id);
|
||||
showToast(I18n.t('Avatar_changed_successfully'));
|
||||
this.init();
|
||||
} catch (e) {
|
||||
|
@ -353,53 +355,57 @@ export default class ProfileView extends LoggedView {
|
|||
if (!Accounts_CustomFields) {
|
||||
return null;
|
||||
}
|
||||
const parsedCustomFields = JSON.parse(Accounts_CustomFields);
|
||||
return Object.keys(parsedCustomFields).map((key, index, array) => {
|
||||
if (parsedCustomFields[key].type === 'select') {
|
||||
const options = parsedCustomFields[key].options.map(option => ({ label: option, value: option }));
|
||||
try {
|
||||
const parsedCustomFields = JSON.parse(Accounts_CustomFields);
|
||||
return Object.keys(parsedCustomFields).map((key, index, array) => {
|
||||
if (parsedCustomFields[key].type === 'select') {
|
||||
const options = parsedCustomFields[key].options.map(option => ({ label: option, value: option }));
|
||||
return (
|
||||
<RNPickerSelect
|
||||
key={key}
|
||||
items={options}
|
||||
onValueChange={(value) => {
|
||||
const newValue = {};
|
||||
newValue[key] = value;
|
||||
this.setState({ customFields: { ...customFields, ...newValue } });
|
||||
}}
|
||||
value={customFields[key]}
|
||||
>
|
||||
<RCTextInput
|
||||
inputRef={(e) => { this[key] = e; }}
|
||||
label={key}
|
||||
placeholder={key}
|
||||
value={customFields[key]}
|
||||
testID='settings-view-language'
|
||||
/>
|
||||
</RNPickerSelect>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<RNPickerSelect
|
||||
<RCTextInput
|
||||
inputRef={(e) => { this[key] = e; }}
|
||||
key={key}
|
||||
items={options}
|
||||
onValueChange={(value) => {
|
||||
label={key}
|
||||
placeholder={key}
|
||||
value={customFields[key]}
|
||||
onChangeText={(value) => {
|
||||
const newValue = {};
|
||||
newValue[key] = value;
|
||||
this.setState({ customFields: { ...customFields, ...newValue } });
|
||||
}}
|
||||
value={customFields[key]}
|
||||
>
|
||||
<RCTextInput
|
||||
inputRef={(e) => { this[key] = e; }}
|
||||
label={key}
|
||||
placeholder={key}
|
||||
value={customFields[key]}
|
||||
testID='settings-view-language'
|
||||
/>
|
||||
</RNPickerSelect>
|
||||
onSubmitEditing={() => {
|
||||
if (array.length - 1 > index) {
|
||||
return this[array[index + 1]].focus();
|
||||
}
|
||||
this.avatarUrl.focus();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<RCTextInput
|
||||
inputRef={(e) => { this[key] = e; }}
|
||||
key={key}
|
||||
label={key}
|
||||
placeholder={key}
|
||||
value={customFields[key]}
|
||||
onChangeText={(value) => {
|
||||
const newValue = {};
|
||||
newValue[key] = value;
|
||||
this.setState({ customFields: { ...customFields, ...newValue } });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
if (array.length - 1 > index) {
|
||||
return this[array[index + 1]].focus();
|
||||
}
|
||||
this.avatarUrl.focus();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -480,16 +486,14 @@ export default class ProfileView extends LoggedView {
|
|||
testID='profile-view-avatar-url'
|
||||
/>
|
||||
{this.renderAvatarButtons()}
|
||||
<View style={sharedStyles.alignItemsFlexStart}>
|
||||
<Button
|
||||
title={I18n.t('Save_Changes')}
|
||||
type='primary'
|
||||
onPress={this.submit}
|
||||
disabled={!this.formIsChanged()}
|
||||
testID='profile-view-submit'
|
||||
/>
|
||||
</View>
|
||||
<Loading visible={saving} />
|
||||
<Button
|
||||
title={I18n.t('Save_Changes')}
|
||||
type='primary'
|
||||
onPress={this.submit}
|
||||
disabled={!this.formIsChanged()}
|
||||
testID='profile-view-submit'
|
||||
loading={saving}
|
||||
/>
|
||||
<Dialog.Container visible={showPasswordAlert}>
|
||||
<Dialog.Title>
|
||||
{I18n.t('Please_enter_your_password')}
|
||||
|
@ -498,7 +502,7 @@ export default class ProfileView extends LoggedView {
|
|||
{I18n.t('For_your_security_you_must_enter_your_current_password_to_continue')}
|
||||
</Dialog.Description>
|
||||
<Dialog.Input
|
||||
onChangeText={value => this.setState({ typedPassword: value })}
|
||||
onChangeText={value => this.setState({ currentPassword: value })}
|
||||
secureTextEntry
|
||||
testID='profile-view-typed-password'
|
||||
style={styles.dialogInput}
|
||||
|
|
|
@ -68,7 +68,9 @@ export default class RoomActionsView extends LoggedView {
|
|||
this.state = {
|
||||
room: this.rooms[0] || {},
|
||||
membersCount: 0,
|
||||
member: {}
|
||||
member: {},
|
||||
joined: false,
|
||||
canViewMembers: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -86,6 +88,9 @@ export default class RoomActionsView extends LoggedView {
|
|||
}
|
||||
}
|
||||
|
||||
if (room.t === 'd') {
|
||||
this.updateRoomMember();
|
||||
}
|
||||
this.rooms.addListener(this.updateRoom);
|
||||
}
|
||||
|
||||
|
@ -310,6 +315,20 @@ export default class RoomActionsView extends LoggedView {
|
|||
this.setState({ room: this.rooms[0] || {} });
|
||||
}
|
||||
|
||||
updateRoomMember = async() => {
|
||||
const { room } = this.state;
|
||||
const { rid } = room;
|
||||
const { userId } = this.props;
|
||||
|
||||
try {
|
||||
const member = await RocketChat.getRoomMember(rid, userId);
|
||||
this.setState({ member });
|
||||
} catch (e) {
|
||||
log('RoomActions updateRoomMember', e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
toggleBlockUser = () => {
|
||||
const { room } = this.state;
|
||||
const { rid, blocker } = room;
|
||||
|
|
|
@ -3,26 +3,25 @@ import PropTypes from 'prop-types';
|
|||
import { FlatList, View, Text } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
import equal from 'deep-equal';
|
||||
|
||||
import { openRoomFiles as openRoomFilesAction, closeRoomFiles as closeRoomFilesAction } from '../../actions/roomFiles';
|
||||
import LoggedView from '../View';
|
||||
import styles from './styles';
|
||||
import Message from '../../containers/message';
|
||||
import Message from '../../containers/message/Message';
|
||||
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||
import I18n from '../../i18n';
|
||||
import { DEFAULT_HEADER } from '../../constants/headerOptions';
|
||||
import database from '../../lib/realm';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
|
||||
@connect(state => ({
|
||||
messages: state.roomFiles.messages,
|
||||
ready: state.roomFiles.ready,
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||
customEmojis: state.customEmojis,
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
username: state.login.user && state.login.user.username,
|
||||
token: state.login.user && state.login.user.token
|
||||
}
|
||||
}), dispatch => ({
|
||||
openRoomFiles: (rid, limit) => dispatch(openRoomFilesAction(rid, limit)),
|
||||
closeRoomFiles: () => dispatch(closeRoomFilesAction())
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class RoomFilesView extends LoggedView {
|
||||
|
@ -41,53 +40,51 @@ export default class RoomFilesView extends LoggedView {
|
|||
|
||||
static propTypes = {
|
||||
rid: PropTypes.string,
|
||||
messages: PropTypes.array,
|
||||
ready: PropTypes.bool,
|
||||
user: PropTypes.object,
|
||||
openRoomFiles: PropTypes.func,
|
||||
closeRoomFiles: PropTypes.func
|
||||
baseUrl: PropTypes.string,
|
||||
customEmojis: PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super('RoomFilesView', props);
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', props.rid);
|
||||
this.state = {
|
||||
loading: true,
|
||||
loadingMore: false
|
||||
loading: false,
|
||||
room: this.rooms[0],
|
||||
messages: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.limit = 20;
|
||||
this.load();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { ready } = this.props;
|
||||
if (nextProps.ready && nextProps.ready !== ready) {
|
||||
this.setState({ loading: false, loadingMore: false });
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !equal(this.state, nextState);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { closeRoomFiles } = this.props;
|
||||
closeRoomFiles();
|
||||
}
|
||||
|
||||
load = () => {
|
||||
const { openRoomFiles, rid } = this.props;
|
||||
openRoomFiles(rid, this.limit);
|
||||
}
|
||||
|
||||
moreData = () => {
|
||||
const { loadingMore } = this.state;
|
||||
const { messages } = this.props;
|
||||
if (messages.length < this.limit) {
|
||||
load = async() => {
|
||||
const {
|
||||
messages, total, loading, room
|
||||
} = this.state;
|
||||
if (messages.length === total || loading) {
|
||||
return;
|
||||
}
|
||||
if (!loadingMore) {
|
||||
this.setState({ loadingMore: true });
|
||||
this.limit += 20;
|
||||
this.load();
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
try {
|
||||
const result = await RocketChat.getFiles(room.rid, room.t, messages.length);
|
||||
if (result.success) {
|
||||
this.setState(prevState => ({
|
||||
messages: [...prevState.messages, ...result.files],
|
||||
total: result.total,
|
||||
loading: false
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({ loading: false });
|
||||
console.log('RoomFilesView -> catch -> error', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,26 +95,49 @@ export default class RoomFilesView extends LoggedView {
|
|||
)
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const { user } = this.props;
|
||||
const { user, baseUrl, customEmojis } = this.props;
|
||||
|
||||
let url = {};
|
||||
if (/image/.test(item.type)) {
|
||||
url = { image_url: item.url };
|
||||
} else if (/audio/.test(item.type)) {
|
||||
url = { audio_url: item.url };
|
||||
} else if (/video/.test(item.type)) {
|
||||
url = { video_url: item.url };
|
||||
} else {
|
||||
url = {
|
||||
title_link: item.url,
|
||||
type: 'file'
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Message
|
||||
item={item}
|
||||
style={styles.message}
|
||||
reactions={item.reactions}
|
||||
customEmojis={customEmojis}
|
||||
baseUrl={baseUrl}
|
||||
user={user}
|
||||
customTimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||
author={item.user}
|
||||
ts={item.uploadedAt}
|
||||
attachments={[{
|
||||
title: item.name,
|
||||
description: item.description,
|
||||
...url
|
||||
}]}
|
||||
timeFormat='MMM Do YYYY, h:mm:ss a'
|
||||
edited={!!item.editedAt}
|
||||
header
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { messages, ready } = this.props;
|
||||
if (ready && messages.length === 0) {
|
||||
const { messages, loading } = this.state;
|
||||
|
||||
if (!loading && messages.length === 0) {
|
||||
return this.renderEmpty();
|
||||
}
|
||||
|
||||
const { loading, loadingMore } = this.state;
|
||||
return (
|
||||
<SafeAreaView style={styles.list} testID='room-files-view' forceInset={{ bottom: 'never' }}>
|
||||
<FlatList
|
||||
|
@ -125,9 +145,8 @@ export default class RoomFilesView extends LoggedView {
|
|||
renderItem={this.renderItem}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
onEndReached={this.moreData}
|
||||
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||
onEndReached={this.load}
|
||||
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
|
|
@ -39,7 +39,7 @@ const PERMISSIONS_ARRAY = [
|
|||
];
|
||||
|
||||
@connect(null, dispatch => ({
|
||||
eraseRoom: rid => dispatch(eraseRoomAction(rid))
|
||||
eraseRoom: (rid, t) => dispatch(eraseRoomAction(rid, t))
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class RoomInfoEditView extends LoggedView {
|
||||
|
@ -231,7 +231,7 @@ export default class RoomInfoEditView extends LoggedView {
|
|||
{
|
||||
text: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
|
||||
style: 'destructive',
|
||||
onPress: () => eraseRoom(room.rid)
|
||||
onPress: () => eraseRoom(room.rid, room.t)
|
||||
}
|
||||
],
|
||||
{ cancelable: false }
|
||||
|
@ -240,7 +240,7 @@ export default class RoomInfoEditView extends LoggedView {
|
|||
|
||||
toggleArchive = () => {
|
||||
const { room } = this.state;
|
||||
const { rid, archived } = room;
|
||||
const { rid, archived, t } = room;
|
||||
|
||||
const action = I18n.t(`${ archived ? 'un' : '' }archive`);
|
||||
Alert.alert(
|
||||
|
@ -254,9 +254,9 @@ export default class RoomInfoEditView extends LoggedView {
|
|||
{
|
||||
text: I18n.t('Yes_action_it', { action }),
|
||||
style: 'destructive',
|
||||
onPress: () => {
|
||||
onPress: async() => {
|
||||
try {
|
||||
RocketChat.toggleArchiveRoom(rid, !archived);
|
||||
await RocketChat.toggleArchiveRoom(rid, t, !archived);
|
||||
} catch (e) {
|
||||
log('toggleArchive', e);
|
||||
}
|
||||
|
|
|
@ -81,8 +81,8 @@ export default class RoomMembersView extends LoggedView {
|
|||
this.rooms.removeAllListeners();
|
||||
}
|
||||
|
||||
navigationButtonPressed = async({ buttonId }) => {
|
||||
const { rid, allUsers } = this.state;
|
||||
navigationButtonPressed = ({ buttonId }) => {
|
||||
const { allUsers } = this.state;
|
||||
const { componentId } = this.props;
|
||||
|
||||
if (buttonId === 'toggleOnline') {
|
||||
|
@ -97,10 +97,7 @@ export default class RoomMembersView extends LoggedView {
|
|||
}]
|
||||
}
|
||||
});
|
||||
const allUsersFilter = !allUsers;
|
||||
const membersResult = await RocketChat.getRoomMembers(rid, allUsersFilter);
|
||||
const members = membersResult.records;
|
||||
this.setState({ allUsers: allUsersFilter, members });
|
||||
this.fetchMembers(!allUsers);
|
||||
} catch (e) {
|
||||
log('RoomMembers.onNavigationButtonPressed', e);
|
||||
}
|
||||
|
|
|
@ -133,6 +133,12 @@ export default class RoomView extends LoggedView {
|
|||
return true;
|
||||
} else if (room.f !== nextState.room.f) {
|
||||
return true;
|
||||
} else if (room.blocked !== nextState.room.blocked) {
|
||||
return true;
|
||||
} else if (room.blocker !== nextState.room.blocker) {
|
||||
return true;
|
||||
} else if (room.archived !== nextState.room.archived) {
|
||||
return true;
|
||||
} else if (loaded !== nextState.loaded) {
|
||||
return true;
|
||||
} else if (joined !== nextState.joined) {
|
||||
|
@ -156,17 +162,21 @@ export default class RoomView extends LoggedView {
|
|||
const { componentId, appState } = this.props;
|
||||
|
||||
if (prevState.room.f !== room.f) {
|
||||
const rightButtons = [{
|
||||
id: 'star',
|
||||
testID: 'room-view-header-star',
|
||||
icon: room.f ? iconsMap.star : iconsMap.starOutline
|
||||
}];
|
||||
if (room.t !== 'l') {
|
||||
rightButtons.unshift({
|
||||
id: 'more',
|
||||
testID: 'room-view-header-actions',
|
||||
icon: iconsMap.more
|
||||
});
|
||||
}
|
||||
Navigation.mergeOptions(componentId, {
|
||||
topBar: {
|
||||
rightButtons: [{
|
||||
id: 'more',
|
||||
testID: 'room-view-header-actions',
|
||||
icon: iconsMap.more
|
||||
}, {
|
||||
id: 'star',
|
||||
testID: 'room-view-header-star',
|
||||
icon: room.f ? iconsMap.star : iconsMap.starOutline
|
||||
}]
|
||||
rightButtons
|
||||
}
|
||||
});
|
||||
} else if (appState === 'foreground' && appState !== prevProps.appState) {
|
||||
|
@ -375,14 +385,14 @@ export default class RoomView extends LoggedView {
|
|||
}
|
||||
if (room.archived || this.isReadOnly()) {
|
||||
return (
|
||||
<View style={styles.readOnly}>
|
||||
<View style={styles.readOnly} key='room-view-read-only'>
|
||||
<Text>{I18n.t('This_room_is_read_only')}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
if (this.isBlocked()) {
|
||||
return (
|
||||
<View style={styles.blockedOrBlocker}>
|
||||
<View style={styles.readOnly} key='room-view-block'>
|
||||
<Text>{I18n.t('This_room_is_blocked')}</Text>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -32,10 +32,9 @@ export default StyleSheet.create({
|
|||
color: '#ccc'
|
||||
},
|
||||
readOnly: {
|
||||
padding: 10
|
||||
},
|
||||
blockedOrBlocker: {
|
||||
padding: 10
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
marginVertical: 15
|
||||
},
|
||||
reactionPickerContainer: {
|
||||
// width: width - 20,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View, FlatList } from 'react-native';
|
||||
import { View, FlatList, Text } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
|
@ -11,20 +11,20 @@ import styles from './styles';
|
|||
import Markdown from '../../containers/message/Markdown';
|
||||
import debounce from '../../utils/debounce';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import buildMessage from '../../lib/methods/helpers/buildMessage';
|
||||
import Message from '../../containers/message';
|
||||
import Message from '../../containers/message/Message';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import log from '../../utils/log';
|
||||
import I18n from '../../i18n';
|
||||
import { DEFAULT_HEADER } from '../../constants/headerOptions';
|
||||
import database from '../../lib/realm';
|
||||
|
||||
@connect(state => ({
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||
customEmojis: state.customEmojis,
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
username: state.login.user && state.login.user.username,
|
||||
token: state.login.user && state.login.user.token
|
||||
},
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||
}
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class SearchMessagesView extends LoggedView {
|
||||
|
@ -43,18 +43,19 @@ export default class SearchMessagesView extends LoggedView {
|
|||
|
||||
static propTypes = {
|
||||
rid: PropTypes.string,
|
||||
componentId: PropTypes.string,
|
||||
user: PropTypes.object,
|
||||
baseUrl: PropTypes.string
|
||||
baseUrl: PropTypes.string,
|
||||
customEmojis: PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super('SearchMessagesView', props);
|
||||
this.limit = 0;
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', props.rid);
|
||||
this.state = {
|
||||
loading: false,
|
||||
room: this.rooms[0],
|
||||
messages: [],
|
||||
searching: false,
|
||||
loadingMore: false
|
||||
searchText: ''
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -63,100 +64,88 @@ export default class SearchMessagesView extends LoggedView {
|
|||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.onChangeSearch.stop();
|
||||
this.search.stop();
|
||||
}
|
||||
|
||||
onChangeSearch = debounce((search) => {
|
||||
const { searching } = this.state;
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
search = debounce(async(searchText) => {
|
||||
const { room } = this.state;
|
||||
this.setState({ searchText, loading: true, messages: [] });
|
||||
|
||||
this.searchText = search;
|
||||
this.limit = 0;
|
||||
if (!searching) {
|
||||
this.setState({ searching: true });
|
||||
try {
|
||||
const result = await RocketChat.searchMessages(room.rid, searchText);
|
||||
if (result.success) {
|
||||
this.setState({
|
||||
messages: result.messages || [],
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({ loading: false });
|
||||
console.log('SearchMessagesView -> search -> catch -> error', error);
|
||||
}
|
||||
this.search();
|
||||
}, 1000)
|
||||
|
||||
search = async() => {
|
||||
const { rid } = this.props;
|
||||
|
||||
if (this._cancel) {
|
||||
this._cancel('cancel');
|
||||
}
|
||||
const cancel = new Promise((r, reject) => this._cancel = reject);
|
||||
let messages = [];
|
||||
try {
|
||||
const result = await Promise.race([RocketChat.messageSearch(this.searchText, rid, this.limit), cancel]);
|
||||
messages = result.message.docs.map(message => buildMessage(message));
|
||||
this.setState({ messages, searching: false, loadingMore: false });
|
||||
} catch (e) {
|
||||
this._cancel = null;
|
||||
if (e !== 'cancel') {
|
||||
return this.setState({ searching: false, loadingMore: false });
|
||||
}
|
||||
log('SearchMessagesView.search', e);
|
||||
}
|
||||
}
|
||||
|
||||
moreData = () => {
|
||||
const { loadingMore, messages } = this.state;
|
||||
if (messages.length < this.limit) {
|
||||
return;
|
||||
}
|
||||
if (this.searchText && !loadingMore) {
|
||||
this.setState({ loadingMore: true });
|
||||
this.limit += 20;
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
renderEmpty = () => (
|
||||
<View style={styles.listEmptyContainer}>
|
||||
<Text>{I18n.t('No_results_found')}</Text>
|
||||
</View>
|
||||
)
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const { user } = this.props;
|
||||
const { user, customEmojis, baseUrl } = this.props;
|
||||
return (
|
||||
<Message
|
||||
item={item}
|
||||
style={styles.message}
|
||||
reactions={item.reactions}
|
||||
customEmojis={customEmojis}
|
||||
baseUrl={baseUrl}
|
||||
user={user}
|
||||
customTimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||
onReactionPress={async(emoji) => {
|
||||
try {
|
||||
await RocketChat.setReaction(emoji, item._id);
|
||||
this.search();
|
||||
this.forceUpdate();
|
||||
} catch (e) {
|
||||
log('SearchMessagesView.onReactionPress', e);
|
||||
}
|
||||
}}
|
||||
author={item.u}
|
||||
ts={item.ts}
|
||||
msg={item.msg}
|
||||
attachments={item.attachments || []}
|
||||
timeFormat='MMM Do YYYY, h:mm:ss a'
|
||||
edited={!!item.editedAt}
|
||||
header
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderList = () => {
|
||||
const { messages, loading, searchText } = this.state;
|
||||
|
||||
if (!loading && messages.length === 0 && searchText.length) {
|
||||
return this.renderEmpty();
|
||||
}
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={messages}
|
||||
renderItem={this.renderItem}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
onEndReached={this.load}
|
||||
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
|
||||
{...scrollPersistTaps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { searching, loadingMore, messages } = this.state;
|
||||
return (
|
||||
<SafeAreaView style={styles.container} testID='search-messages-view' forceInset={{ bottom: 'never' }}>
|
||||
<View style={styles.searchContainer}>
|
||||
<RCTextInput
|
||||
inputRef={(e) => { this.name = e; }}
|
||||
label={I18n.t('Search')}
|
||||
onChangeText={this.onChangeSearch}
|
||||
onChangeText={this.search}
|
||||
placeholder={I18n.t('Search_Messages')}
|
||||
testID='search-message-view-input'
|
||||
/>
|
||||
<Markdown msg={I18n.t('You_can_search_using_RegExp_eg')} username='' baseUrl='' customEmojis={{}} />
|
||||
<View style={styles.divider} />
|
||||
</View>
|
||||
<FlatList
|
||||
data={messages}
|
||||
renderItem={this.renderItem}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
onEndReached={this.moreData}
|
||||
ListHeaderComponent={searching ? <RCActivityIndicator /> : null}
|
||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||
{...scrollPersistTaps}
|
||||
/>
|
||||
{this.renderList()}
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ export default StyleSheet.create({
|
|||
transform: [{ scaleY: 1 }]
|
||||
},
|
||||
divider: {
|
||||
width: '100%',
|
||||
height: StyleSheet.hairlineWidth,
|
||||
borderColor: '#ddd',
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
backgroundColor: '#E7EBF2',
|
||||
marginVertical: 20
|
||||
}
|
||||
});
|
||||
|
|
|
@ -105,7 +105,7 @@ export default class SnippetedMessagesView extends LoggedView {
|
|||
style={styles.message}
|
||||
reactions={item.reactions}
|
||||
user={user}
|
||||
customTimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||
customTimeFormat='MMM Do YYYY, h:mm:ss a'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,32 +4,29 @@ import { FlatList, View, Text } from 'react-native';
|
|||
import { connect } from 'react-redux';
|
||||
import ActionSheet from 'react-native-actionsheet';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
import equal from 'deep-equal';
|
||||
|
||||
import { openStarredMessages as openStarredMessagesAction, closeStarredMessages as closeStarredMessagesAction } from '../../actions/starredMessages';
|
||||
import { toggleStarRequest as toggleStarRequestAction } from '../../actions/messages';
|
||||
import LoggedView from '../View';
|
||||
import styles from './styles';
|
||||
import Message from '../../containers/message';
|
||||
import Message from '../../containers/message/Message';
|
||||
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||
import I18n from '../../i18n';
|
||||
import { DEFAULT_HEADER } from '../../constants/headerOptions';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import database from '../../lib/realm';
|
||||
|
||||
const STAR_INDEX = 0;
|
||||
const CANCEL_INDEX = 1;
|
||||
const options = [I18n.t('Unstar'), I18n.t('Cancel')];
|
||||
|
||||
@connect(state => ({
|
||||
messages: state.starredMessages.messages,
|
||||
ready: state.starredMessages.ready,
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||
customEmojis: state.customEmojis,
|
||||
user: {
|
||||
id: state.login.user && state.login.user.id,
|
||||
username: state.login.user && state.login.user.username,
|
||||
token: state.login.user && state.login.user.token
|
||||
}
|
||||
}), dispatch => ({
|
||||
openStarredMessages: (rid, limit) => dispatch(openStarredMessagesAction(rid, limit)),
|
||||
closeStarredMessages: () => dispatch(closeStarredMessagesAction()),
|
||||
toggleStarRequest: message => dispatch(toggleStarRequestAction(message))
|
||||
}))
|
||||
/** @extends React.Component */
|
||||
export default class StarredMessagesView extends LoggedView {
|
||||
|
@ -48,38 +45,27 @@ export default class StarredMessagesView extends LoggedView {
|
|||
|
||||
static propTypes = {
|
||||
rid: PropTypes.string,
|
||||
messages: PropTypes.array,
|
||||
ready: PropTypes.bool,
|
||||
user: PropTypes.object,
|
||||
openStarredMessages: PropTypes.func,
|
||||
closeStarredMessages: PropTypes.func,
|
||||
toggleStarRequest: PropTypes.func
|
||||
baseUrl: PropTypes.string,
|
||||
customEmojis: PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super('StarredMessagesView', props);
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', props.rid);
|
||||
this.state = {
|
||||
message: {},
|
||||
loading: true,
|
||||
loadingMore: false
|
||||
loading: false,
|
||||
room: this.rooms[0],
|
||||
messages: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.limit = 20;
|
||||
this.load();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { ready } = this.props;
|
||||
if (nextProps.ready && nextProps.ready !== ready) {
|
||||
this.setState({ loading: false, loadingMore: false });
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { closeStarredMessages } = this.props;
|
||||
closeStarredMessages();
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !equal(this.state, nextState);
|
||||
}
|
||||
|
||||
onLongPress = (message) => {
|
||||
|
@ -90,33 +76,57 @@ export default class StarredMessagesView extends LoggedView {
|
|||
}
|
||||
|
||||
handleActionPress = (actionIndex) => {
|
||||
const { message } = this.state;
|
||||
const { toggleStarRequest } = this.props;
|
||||
|
||||
switch (actionIndex) {
|
||||
case STAR_INDEX:
|
||||
toggleStarRequest(message);
|
||||
this.unStar();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
load = () => {
|
||||
const { rid, openStarredMessages } = this.props;
|
||||
openStarredMessages(rid, this.limit);
|
||||
unStar = async() => {
|
||||
const { message } = this.state;
|
||||
try {
|
||||
const result = await RocketChat.toggleStarMessage(message);
|
||||
if (result.success) {
|
||||
this.setState(prevState => ({
|
||||
messages: prevState.messages.filter(item => item._id !== message._id)
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('StarredMessagesView -> unStar -> catch -> error', error);
|
||||
}
|
||||
}
|
||||
|
||||
moreData = () => {
|
||||
const { loadingMore } = this.state;
|
||||
const { messages } = this.props;
|
||||
if (messages.length < this.limit) {
|
||||
load = async() => {
|
||||
const {
|
||||
messages, total, loading, room
|
||||
} = this.state;
|
||||
const { user } = this.props;
|
||||
if (messages.length === total || loading) {
|
||||
return;
|
||||
}
|
||||
if (!loadingMore) {
|
||||
this.setState({ loadingMore: true });
|
||||
this.limit += 20;
|
||||
this.load();
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
try {
|
||||
const result = await RocketChat.getMessages(
|
||||
room.rid,
|
||||
room.t,
|
||||
{ 'starred._id': { $in: [user.id] } },
|
||||
messages.length
|
||||
);
|
||||
if (result.success) {
|
||||
this.setState(prevState => ({
|
||||
messages: [...prevState.messages, ...result.messages],
|
||||
total: result.total,
|
||||
loading: false
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({ loading: false });
|
||||
console.log('StarredMessagesView -> load -> catch -> error', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,24 +137,29 @@ export default class StarredMessagesView extends LoggedView {
|
|||
)
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const { user } = this.props;
|
||||
const { user, customEmojis, baseUrl } = this.props;
|
||||
return (
|
||||
<Message
|
||||
item={item}
|
||||
style={styles.message}
|
||||
reactions={item.reactions}
|
||||
customEmojis={customEmojis}
|
||||
baseUrl={baseUrl}
|
||||
user={user}
|
||||
customTimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||
onLongPress={this.onLongPress}
|
||||
author={item.u}
|
||||
ts={item.ts}
|
||||
msg={item.msg}
|
||||
attachments={item.attachments || []}
|
||||
timeFormat='MMM Do YYYY, h:mm:ss a'
|
||||
edited={!!item.editedAt}
|
||||
header
|
||||
onLongPress={() => this.onLongPress(item)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, loadingMore } = this.state;
|
||||
const { messages, ready } = this.props;
|
||||
const { messages, loading } = this.state;
|
||||
|
||||
if (ready && messages.length === 0) {
|
||||
if (!loading && messages.length === 0) {
|
||||
return this.renderEmpty();
|
||||
}
|
||||
|
||||
|
@ -155,9 +170,8 @@ export default class StarredMessagesView extends LoggedView {
|
|||
renderItem={this.renderItem}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
onEndReached={this.moreData}
|
||||
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||
onEndReached={this.load}
|
||||
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
|
||||
/>
|
||||
<ActionSheet
|
||||
ref={o => this.actionSheet = o}
|
||||
|
|
Loading…
Reference in New Issue