[NEW] Rooms list layout (#413)
* RoomsListView layout * Rooms list layout * Sort component * Header icons * Default header colors * Add server dropdown * Close sort dropdown if server dropdown will open * UserItem * Room type icon * Search working * Tests updated * Android layout * Using realm queries instead of array iterates * Animation duration * Fixed render bug
|
@ -201,7 +201,7 @@ dependencies {
|
||||||
implementation project(':reactnativenotifications')
|
implementation project(':reactnativenotifications')
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
implementation "com.android.support:appcompat-v7:27.1.0"
|
implementation "com.android.support:appcompat-v7:27.1.0"
|
||||||
implementation "com.android.support:support-v4:27.1.0"
|
implementation "com.android.support:support-v4:27.1.+"
|
||||||
implementation 'com.android.support:customtabs:27.1.0'
|
implementation 'com.android.support:customtabs:27.1.0'
|
||||||
implementation 'com.android.support:design:27.1.0'
|
implementation 'com.android.support:design:27.1.0'
|
||||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||||
|
|
After Width: | Height: | Size: 266 B |
After Width: | Height: | Size: 602 B |
After Width: | Height: | Size: 508 B |
After Width: | Height: | Size: 132 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 575 B |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 867 B |
After Width: | Height: | Size: 722 B |
After Width: | Height: | Size: 883 B |
After Width: | Height: | Size: 942 B |
After Width: | Height: | Size: 897 B |
After Width: | Height: | Size: 174 B |
After Width: | Height: | Size: 335 B |
Before Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 172 B |
After Width: | Height: | Size: 384 B |
After Width: | Height: | Size: 370 B |
After Width: | Height: | Size: 114 B |
After Width: | Height: | Size: 689 B |
After Width: | Height: | Size: 454 B |
After Width: | Height: | Size: 769 B |
Before Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 591 B |
After Width: | Height: | Size: 396 B |
After Width: | Height: | Size: 568 B |
After Width: | Height: | Size: 597 B |
After Width: | Height: | Size: 563 B |
After Width: | Height: | Size: 118 B |
After Width: | Height: | Size: 198 B |
After Width: | Height: | Size: 299 B |
After Width: | Height: | Size: 830 B |
After Width: | Height: | Size: 550 B |
After Width: | Height: | Size: 138 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 761 B |
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 715 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 147 B |
After Width: | Height: | Size: 351 B |
After Width: | Height: | Size: 431 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 783 B |
After Width: | Height: | Size: 157 B |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 916 B |
After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 187 B |
After Width: | Height: | Size: 484 B |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 920 B |
After Width: | Height: | Size: 191 B |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 227 B |
After Width: | Height: | Size: 658 B |
|
@ -24,14 +24,22 @@ export const LOGIN = createRequestTypes('LOGIN', [
|
||||||
'OPEN',
|
'OPEN',
|
||||||
'CLOSE',
|
'CLOSE',
|
||||||
'SET_SERVICES',
|
'SET_SERVICES',
|
||||||
'REMOVE_SERVICES'
|
'REMOVE_SERVICES',
|
||||||
|
'SET_PREFERENCE'
|
||||||
]);
|
]);
|
||||||
export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [
|
export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [
|
||||||
...defaultTypes,
|
...defaultTypes,
|
||||||
'INIT'
|
'INIT'
|
||||||
]);
|
]);
|
||||||
export const USER = createRequestTypes('USER', ['SET']);
|
export const USER = createRequestTypes('USER', ['SET']);
|
||||||
export const ROOMS = createRequestTypes('ROOMS', [...defaultTypes, 'SET_SEARCH']);
|
export const ROOMS = createRequestTypes('ROOMS', [
|
||||||
|
...defaultTypes,
|
||||||
|
'SET_SEARCH',
|
||||||
|
'CLOSE_SERVER_DROPDOWN',
|
||||||
|
'TOGGLE_SERVER_DROPDOWN',
|
||||||
|
'CLOSE_SORT_DROPDOWN',
|
||||||
|
'TOGGLE_SORT_DROPDOWN'
|
||||||
|
]);
|
||||||
export const ROOM = createRequestTypes('ROOM', [
|
export const ROOM = createRequestTypes('ROOM', [
|
||||||
'ADD_USER_TYPING',
|
'ADD_USER_TYPING',
|
||||||
'REMOVE_USER_TYPING',
|
'REMOVE_USER_TYPING',
|
||||||
|
|
|
@ -151,3 +151,10 @@ export function removeLoginServices() {
|
||||||
type: types.LOGIN.REMOVE_SERVICES
|
type: types.LOGIN.REMOVE_SERVICES
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setPreference(preference) {
|
||||||
|
return {
|
||||||
|
type: types.LOGIN.SET_PREFERENCE,
|
||||||
|
preference
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -26,3 +26,27 @@ export function setSearch(searchText) {
|
||||||
searchText
|
searchText
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function closeServerDropdown() {
|
||||||
|
return {
|
||||||
|
type: types.ROOMS.CLOSE_SERVER_DROPDOWN
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleServerDropdown() {
|
||||||
|
return {
|
||||||
|
type: types.ROOMS.TOGGLE_SERVER_DROPDOWN
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeSortDropdown() {
|
||||||
|
return {
|
||||||
|
type: types.ROOMS.CLOSE_SORT_DROPDOWN
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleSortDropdown() {
|
||||||
|
return {
|
||||||
|
type: types.ROOMS.TOGGLE_SORT_DROPDOWN
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -71,6 +71,9 @@ export default {
|
||||||
Message_TimeFormat: {
|
Message_TimeFormat: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString'
|
||||||
},
|
},
|
||||||
|
Site_Name: {
|
||||||
|
type: 'valueAsString'
|
||||||
|
},
|
||||||
Site_Url: {
|
Site_Url: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString'
|
||||||
},
|
},
|
||||||
|
|
|
@ -38,7 +38,7 @@ export default class Avatar extends React.PureComponent {
|
||||||
text: '',
|
text: '',
|
||||||
size: 25,
|
size: 25,
|
||||||
type: 'd',
|
type: 'd',
|
||||||
borderRadius: 2,
|
borderRadius: 4,
|
||||||
forceInitials: false
|
forceInitials: false
|
||||||
};
|
};
|
||||||
state = { showInitials: true };
|
state = { showInitials: true };
|
||||||
|
|
|
@ -1,36 +1,33 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { Image, StyleSheet } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
type: {
|
style: {
|
||||||
marginRight: 5,
|
marginRight: 7,
|
||||||
marginTop: 3
|
marginTop: 3
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const RoomTypeIcon = ({ type, size }) => {
|
const RoomTypeIcon = ({ type, size, style }) => {
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const icon = {
|
if (type === 'c') {
|
||||||
c: 'pound',
|
return <Image source={{ uri: 'subscription_hashtag' }} style={[styles.style, style, { width: size, height: size }]} />;
|
||||||
p: 'lock',
|
}
|
||||||
l: 'account',
|
return <Image source={{ uri: 'subscription_lock' }} style={[styles.style, style, { width: size, height: size }]} />;
|
||||||
d: 'at'
|
|
||||||
}[type];
|
|
||||||
return <Icon name={icon} size={size} style={styles.type} />;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RoomTypeIcon.propTypes = {
|
RoomTypeIcon.propTypes = {
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
size: PropTypes.number
|
size: PropTypes.number,
|
||||||
|
style: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
RoomTypeIcon.defaultProps = {
|
RoomTypeIcon.defaultProps = {
|
||||||
size: 15
|
size: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RoomTypeIcon;
|
export default RoomTypeIcon;
|
||||||
|
|
|
@ -68,7 +68,6 @@ export default class Markdown extends React.Component {
|
||||||
hardbreak: () => null,
|
hardbreak: () => null,
|
||||||
blocklink: () => null,
|
blocklink: () => null,
|
||||||
image: node => (
|
image: node => (
|
||||||
// TODO: should use Image component
|
|
||||||
<Image key={node.key} style={styles.inlineImage} source={{ uri: node.attributes.src }} />
|
<Image key={node.key} style={styles.inlineImage} source={{ uri: node.attributes.src }} />
|
||||||
),
|
),
|
||||||
...rules
|
...rules
|
||||||
|
|
|
@ -76,6 +76,8 @@ export default {
|
||||||
'error-user-registration-secret': 'User registration is only allowed via Secret URL',
|
'error-user-registration-secret': 'User registration is only allowed via Secret URL',
|
||||||
'error-you-are-last-owner': 'You are the last owner. Please set new owner before leaving the room.',
|
'error-you-are-last-owner': 'You are the last owner. Please set new owner before leaving the room.',
|
||||||
Actions: 'Actions',
|
Actions: 'Actions',
|
||||||
|
activity: 'activity',
|
||||||
|
Activity: 'Activity',
|
||||||
Add_Reaction: 'Add Reaction',
|
Add_Reaction: 'Add Reaction',
|
||||||
Add_Server: 'Add Server',
|
Add_Server: 'Add Server',
|
||||||
Add_user: 'Add user',
|
Add_user: 'Add user',
|
||||||
|
@ -85,6 +87,7 @@ export default {
|
||||||
All_users_in_the_channel_can_write_new_messages: 'All users in the channel can write new messages',
|
All_users_in_the_channel_can_write_new_messages: 'All users in the channel can write new messages',
|
||||||
All: 'All',
|
All: 'All',
|
||||||
Allow_Reactions: 'Allow Reactions',
|
Allow_Reactions: 'Allow Reactions',
|
||||||
|
Alphabetical: 'Alphabetical',
|
||||||
and_more: 'and more',
|
and_more: 'and more',
|
||||||
and: 'and',
|
and: 'and',
|
||||||
announcement: 'announcement',
|
announcement: 'announcement',
|
||||||
|
@ -108,6 +111,7 @@ export default {
|
||||||
Cancel: 'Cancel',
|
Cancel: 'Cancel',
|
||||||
changing_avatar: 'changing avatar',
|
changing_avatar: 'changing avatar',
|
||||||
Channel_Name: 'Channel Name',
|
Channel_Name: 'Channel Name',
|
||||||
|
Channels: 'Channels',
|
||||||
Chats: 'Chats',
|
Chats: 'Chats',
|
||||||
Close: 'Close',
|
Close: 'Close',
|
||||||
Close_emoji_selector: 'Close emoji selector',
|
Close_emoji_selector: 'Close emoji selector',
|
||||||
|
@ -133,6 +137,7 @@ export default {
|
||||||
description: 'description',
|
description: 'description',
|
||||||
Description: 'Description',
|
Description: 'Description',
|
||||||
Disable_notifications: 'Disable notifications',
|
Disable_notifications: 'Disable notifications',
|
||||||
|
Direct_Messages: 'Direct Messages',
|
||||||
Do_you_really_want_to_key_this_room_question_mark: 'Do you really want to {{key}} this room?',
|
Do_you_really_want_to_key_this_room_question_mark: 'Do you really want to {{key}} this room?',
|
||||||
edit: 'edit',
|
edit: 'edit',
|
||||||
Edit: 'Edit',
|
Edit: 'Edit',
|
||||||
|
@ -141,6 +146,7 @@ export default {
|
||||||
Enable_notifications: 'Enable notifications',
|
Enable_notifications: 'Enable notifications',
|
||||||
Everyone_can_access_this_channel: 'Everyone can access this channel',
|
Everyone_can_access_this_channel: 'Everyone can access this channel',
|
||||||
Error_uploading: 'Error uploading',
|
Error_uploading: 'Error uploading',
|
||||||
|
Favorites: 'Favorites',
|
||||||
Files: 'Files',
|
Files: 'Files',
|
||||||
Finish_recording: 'Finish recording',
|
Finish_recording: 'Finish recording',
|
||||||
For_your_security_you_must_enter_your_current_password_to_continue: 'For your security, you must enter your current password to continue',
|
For_your_security_you_must_enter_your_current_password_to_continue: 'For your security, you must enter your current password to continue',
|
||||||
|
@ -148,6 +154,8 @@ export default {
|
||||||
Forgot_password_If_this_email_is_registered: 'If this email is registered, we\'ll send instructions on how to reset your password. If you do not receive an email shortly, please come back and try again.',
|
Forgot_password_If_this_email_is_registered: 'If this email is registered, we\'ll send instructions on how to reset your password. If you do not receive an email shortly, please come back and try again.',
|
||||||
Forgot_password: 'Forgot password',
|
Forgot_password: 'Forgot password',
|
||||||
Forgot_Password: 'Forgot Password',
|
Forgot_Password: 'Forgot Password',
|
||||||
|
Group_by_favorites: 'Group by favorites',
|
||||||
|
Group_by_type: 'Group by type',
|
||||||
Has_joined_the_channel: 'Has joined the channel',
|
Has_joined_the_channel: 'Has joined the channel',
|
||||||
Has_left_the_channel: 'Has left the channel',
|
Has_left_the_channel: 'Has left the channel',
|
||||||
I_have_an_account: 'I have an account',
|
I_have_an_account: 'I have an account',
|
||||||
|
@ -161,6 +169,7 @@ export default {
|
||||||
last_message: 'last message',
|
last_message: 'last message',
|
||||||
Leave_channel: 'Leave channel',
|
Leave_channel: 'Leave channel',
|
||||||
leave: 'leave',
|
leave: 'leave',
|
||||||
|
Livechat: 'Livechat',
|
||||||
Loading_messages_ellipsis: 'Loading messages...',
|
Loading_messages_ellipsis: 'Loading messages...',
|
||||||
Login: 'Login',
|
Login: 'Login',
|
||||||
Logout: 'Logout',
|
Logout: 'Logout',
|
||||||
|
@ -180,6 +189,7 @@ export default {
|
||||||
My_servers: 'My servers',
|
My_servers: 'My servers',
|
||||||
N_online_members: '{{n}} online members',
|
N_online_members: '{{n}} online members',
|
||||||
N_person_reacted: '{{n}} people reacted',
|
N_person_reacted: '{{n}} people reacted',
|
||||||
|
name: 'name',
|
||||||
Name: 'Name',
|
Name: 'Name',
|
||||||
New_in_RocketChat_question_mark: 'New in Rocket.Chat?',
|
New_in_RocketChat_question_mark: 'New in Rocket.Chat?',
|
||||||
New_Message: 'New Message',
|
New_Message: 'New Message',
|
||||||
|
@ -216,6 +226,7 @@ export default {
|
||||||
Preferences_saved: 'Preferences saved!',
|
Preferences_saved: 'Preferences saved!',
|
||||||
Privacy_Policy: ' Privacy Policy',
|
Privacy_Policy: ' Privacy Policy',
|
||||||
Private_Channel: 'Private Channel',
|
Private_Channel: 'Private Channel',
|
||||||
|
Private_Groups: 'Private Groups',
|
||||||
Private: 'Private',
|
Private: 'Private',
|
||||||
Profile_saved_successfully: 'Profile saved successfully!',
|
Profile_saved_successfully: 'Profile saved successfully!',
|
||||||
Profile: 'Profile',
|
Profile: 'Profile',
|
||||||
|
@ -257,6 +268,7 @@ export default {
|
||||||
Send: 'Send',
|
Send: 'Send',
|
||||||
Send_audio_message: 'Send audio message',
|
Send_audio_message: 'Send audio message',
|
||||||
Send_message: 'Send message',
|
Send_message: 'Send message',
|
||||||
|
Server: 'Server',
|
||||||
Servers: 'Servers',
|
Servers: 'Servers',
|
||||||
Settings: 'Settings',
|
Settings: 'Settings',
|
||||||
Settings_succesfully_changed: 'Settings succesfully changed!',
|
Settings_succesfully_changed: 'Settings succesfully changed!',
|
||||||
|
@ -268,6 +280,7 @@ export default {
|
||||||
snippeted: 'snippeted',
|
snippeted: 'snippeted',
|
||||||
Snippets: 'Snippets',
|
Snippets: 'Snippets',
|
||||||
Some_field_is_invalid_or_empty: 'Some field is invalid or empty',
|
Some_field_is_invalid_or_empty: 'Some field is invalid or empty',
|
||||||
|
Sorting_by: 'Sorting by {{key}}',
|
||||||
Star_room: 'Star room',
|
Star_room: 'Star room',
|
||||||
Star: 'Star',
|
Star: 'Star',
|
||||||
Starred_Messages: 'Starred Messages',
|
Starred_Messages: 'Starred Messages',
|
||||||
|
@ -296,6 +309,8 @@ export default {
|
||||||
unmuted: 'unmuted',
|
unmuted: 'unmuted',
|
||||||
Unpin: 'Unpin',
|
Unpin: 'Unpin',
|
||||||
unread_messages: 'unread messages',
|
unread_messages: 'unread messages',
|
||||||
|
Unread: 'Unread',
|
||||||
|
Unread_on_top: 'Unread on top',
|
||||||
Unstar: 'Unstar',
|
Unstar: 'Unstar',
|
||||||
Uploading: 'Uploading',
|
Uploading: 'Uploading',
|
||||||
User_added_by: 'User {{userAdded}} added by {{userBy}}',
|
User_added_by: 'User {{userAdded}} added by {{userBy}}',
|
||||||
|
|
|
@ -11,18 +11,36 @@ const getLastUpdate = () => {
|
||||||
return setting && setting._updatedAt;
|
return setting && setting._updatedAt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function updateServer(param) {
|
||||||
|
database.databases.serversDB.write(() => {
|
||||||
|
database.databases.serversDB.create('servers', { id: this.ddp.url, ...param }, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default async function() {
|
export default async function() {
|
||||||
try {
|
try {
|
||||||
const lastUpdate = getLastUpdate();
|
const lastUpdate = getLastUpdate();
|
||||||
const result = await (!lastUpdate ? this.ddp.call('public-settings/get') : this.ddp.call('public-settings/get', new Date(lastUpdate)));
|
const result = await (!lastUpdate ? this.ddp.call('public-settings/get') : this.ddp.call('public-settings/get', new Date(lastUpdate)));
|
||||||
|
const data = result.update || result || [];
|
||||||
|
|
||||||
const filteredSettings = this._prepareSettings(this._filterSettings(result.update || result));
|
const filteredSettings = this._prepareSettings(this._filterSettings(data));
|
||||||
|
|
||||||
InteractionManager.runAfterInteractions(() =>
|
InteractionManager.runAfterInteractions(() =>
|
||||||
database.write(() =>
|
database.write(() =>
|
||||||
filteredSettings.forEach(setting =>
|
filteredSettings.forEach((setting) => {
|
||||||
database.create('settings', { ...setting, _updatedAt: new Date() }, true))));
|
database.create('settings', { ...setting, _updatedAt: new Date() }, true);
|
||||||
|
|
||||||
|
if (setting._id === 'Site_Name') {
|
||||||
|
updateServer.call(this, { name: setting.valueAsString });
|
||||||
|
}
|
||||||
|
})));
|
||||||
reduxStore.dispatch(actions.addSettings(this.parseSettings(filteredSettings)));
|
reduxStore.dispatch(actions.addSettings(this.parseSettings(filteredSettings)));
|
||||||
|
|
||||||
|
const iconSetting = data.find(item => item._id === 'Assets_favicon_512');
|
||||||
|
if (iconSetting) {
|
||||||
|
const iconURL = `${ this.ddp.url }/${ iconSetting.value.url || iconSetting.value.defaultUrl }`;
|
||||||
|
updateServer.call(this, { iconURL });
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('getSettings', e);
|
log('getSettings', e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ const serversSchema = {
|
||||||
primaryKey: 'id',
|
primaryKey: 'id',
|
||||||
properties: {
|
properties: {
|
||||||
id: 'string',
|
id: 'string',
|
||||||
|
name: { type: 'string', optional: true },
|
||||||
|
iconURL: { type: 'string', optional: true },
|
||||||
current: 'bool'
|
current: 'bool'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -140,7 +140,8 @@ const RocketChat = {
|
||||||
user = { ...user, ...userInfo.user };
|
user = { ...user, ...userInfo.user };
|
||||||
}
|
}
|
||||||
RocketChat.registerPushToken(user.id);
|
RocketChat.registerPushToken(user.id);
|
||||||
return reduxStore.dispatch(loginSuccess(user));
|
reduxStore.dispatch(loginSuccess(user));
|
||||||
|
this.ddp.subscribe('userData');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('rocketchat.loginSuccess', e);
|
log('rocketchat.loginSuccess', e);
|
||||||
}
|
}
|
||||||
|
@ -468,7 +469,7 @@ const RocketChat = {
|
||||||
log('rocketchat.logout', e);
|
log('rocketchat.logout', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
database.deleteAll();
|
// database.deleteAll();
|
||||||
AsyncStorage.removeItem(TOKEN_KEY);
|
AsyncStorage.removeItem(TOKEN_KEY);
|
||||||
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
|
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,91 +1,101 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View, Text, StyleSheet, ViewPropTypes } from 'react-native';
|
import { View, Text, StyleSheet, Image, Platform } from 'react-native';
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { emojify } from 'react-emojione';
|
||||||
|
|
||||||
import Avatar from '../containers/Avatar';
|
import Avatar from '../containers/Avatar';
|
||||||
import Status from '../containers/status';
|
import Status from '../containers/status';
|
||||||
import Touch from '../utils/touch/index'; //eslint-disable-line
|
import Touch from '../utils/touch/index'; //eslint-disable-line
|
||||||
import Markdown from '../containers/message/Markdown';
|
|
||||||
import RoomTypeIcon from '../containers/RoomTypeIcon';
|
import RoomTypeIcon from '../containers/RoomTypeIcon';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
paddingHorizontal: 16,
|
alignItems: 'center'
|
||||||
paddingVertical: 12,
|
|
||||||
alignItems: 'center',
|
|
||||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
||||||
borderBottomColor: '#ddd'
|
|
||||||
},
|
},
|
||||||
number: {
|
centerContainer: {
|
||||||
minWidth: 25,
|
flex: 1,
|
||||||
|
height: '100%',
|
||||||
|
marginRight: 4
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
flex: 1,
|
||||||
|
fontSize: 18,
|
||||||
|
color: '#0C0D0F',
|
||||||
|
fontWeight: '400',
|
||||||
|
marginRight: 5,
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 0
|
||||||
|
},
|
||||||
|
alert: {
|
||||||
|
fontWeight: '600'
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'flex-start'
|
||||||
|
},
|
||||||
|
titleContainer: {
|
||||||
|
width: '100%',
|
||||||
|
marginTop: Platform.OS === 'ios' ? 5 : 2,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#9EA2A8',
|
||||||
|
fontWeight: 'normal',
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 0
|
||||||
|
},
|
||||||
|
updateAlert: {
|
||||||
|
color: '#1D74F5'
|
||||||
|
},
|
||||||
|
unreadNumberContainer: {
|
||||||
|
minWidth: 23,
|
||||||
|
padding: 3,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
backgroundColor: '#1d74f5',
|
backgroundColor: '#1D74F5',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
unreadNumberText: {
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
paddingVertical: 4,
|
fontWeight: '500',
|
||||||
paddingHorizontal: 5,
|
letterSpacing: 0.56
|
||||||
|
|
||||||
textAlign: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center'
|
|
||||||
},
|
|
||||||
roomNameView: {
|
|
||||||
flex: 1,
|
|
||||||
height: '100%',
|
|
||||||
marginLeft: 16,
|
|
||||||
marginRight: 4
|
|
||||||
},
|
|
||||||
roomName: {
|
|
||||||
flex: 1,
|
|
||||||
fontSize: 18,
|
|
||||||
color: '#444',
|
|
||||||
marginRight: 8
|
|
||||||
},
|
|
||||||
alert: {
|
|
||||||
fontWeight: 'bold'
|
|
||||||
},
|
|
||||||
favorite: {
|
|
||||||
// backgroundColor: '#eee'
|
|
||||||
},
|
|
||||||
row: {
|
|
||||||
// width: '100%',
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center'
|
|
||||||
// justifyContent: 'flex-end'
|
|
||||||
},
|
|
||||||
firstRow: {
|
|
||||||
width: '100%',
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center'
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
fontSize: 10,
|
|
||||||
color: '#888',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center'
|
|
||||||
},
|
|
||||||
updateAlert: {
|
|
||||||
color: '#1d74f5'
|
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
position: 'absolute',
|
borderRadius: 10,
|
||||||
bottom: -3,
|
width: 10,
|
||||||
right: -3,
|
height: 10,
|
||||||
borderWidth: 3,
|
marginRight: 7,
|
||||||
borderColor: '#fff'
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
marginRight: 5,
|
|
||||||
marginTop: 3
|
marginTop: 3
|
||||||
|
},
|
||||||
|
disclosureContainer: {
|
||||||
|
height: '100%',
|
||||||
|
marginLeft: 6,
|
||||||
|
marginRight: 9,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
disclosureIndicator: {
|
||||||
|
width: 20,
|
||||||
|
height: 20
|
||||||
|
},
|
||||||
|
emptyDisclosureAndroid: {
|
||||||
|
width: 15
|
||||||
|
},
|
||||||
|
markdownText: {
|
||||||
|
flex: 1,
|
||||||
|
color: '#9EA2A8',
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: 'normal'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -103,9 +113,9 @@ const renderNumber = (unread, userMentions) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style={styles.number}>
|
<View style={styles.unreadNumberContainer}>
|
||||||
{ unread }
|
<Text style={styles.unreadNumberText}>{ unread }</Text>
|
||||||
</Text>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -131,13 +141,13 @@ export default class RoomItem extends React.Component {
|
||||||
onLongPress: PropTypes.func,
|
onLongPress: PropTypes.func,
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
avatarSize: PropTypes.number,
|
avatarSize: PropTypes.number,
|
||||||
statusStyle: ViewPropTypes.style,
|
testID: PropTypes.string,
|
||||||
testID: PropTypes.string
|
height: PropTypes.number
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
showLastMessage: true,
|
showLastMessage: true,
|
||||||
avatarSize: 46
|
avatarSize: 48
|
||||||
}
|
}
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
const oldlastMessage = this.props.lastMessage;
|
const oldlastMessage = this.props.lastMessage;
|
||||||
|
@ -151,11 +161,9 @@ export default class RoomItem extends React.Component {
|
||||||
}
|
}
|
||||||
return attrs.some(key => nextProps[key] !== this.props[key]);
|
return attrs.some(key => nextProps[key] !== this.props[key]);
|
||||||
}
|
}
|
||||||
get icon() {
|
get avatar() {
|
||||||
const {
|
const { type, name, avatarSize } = this.props;
|
||||||
type, name, id, avatarSize, statusStyle
|
return <Avatar text={name} size={avatarSize} type={type} style={{ marginHorizontal: 15 }} />;
|
||||||
} = this.props;
|
|
||||||
return (<Avatar text={name} size={avatarSize} type={type}>{type === 'd' ? <Status style={[styles.status, statusStyle]} id={id} /> : null }</Avatar>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get lastMessage() {
|
get lastMessage() {
|
||||||
|
@ -178,19 +186,17 @@ export default class RoomItem extends React.Component {
|
||||||
prefix = `${ lastMessage.u.username }: `;
|
prefix = `${ lastMessage.u.username }: `;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msg = `${ prefix }${ lastMessage.msg.replace(/[\n\t\r]/igm, '') }`;
|
let msg = `${ prefix }${ lastMessage.msg.replace(/[\n\t\r]/igm, '') }`;
|
||||||
const maxChars = 35;
|
msg = emojify(msg, { output: 'unicode' });
|
||||||
return `${ msg.slice(0, maxChars) }${ msg.replace(/:[a-z0-9]+:/gi, ':::').length > maxChars ? '...' : '' }`;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
get type() {
|
get type() {
|
||||||
const icon = {
|
const { type, id } = this.props;
|
||||||
c: 'pound',
|
if (type === 'd') {
|
||||||
p: 'lock',
|
return <Status style={[styles.status]} id={id} />;
|
||||||
l: 'account',
|
}
|
||||||
d: 'at'
|
return <RoomTypeIcon type={type} />;
|
||||||
}[this.props.type];
|
|
||||||
return <Icon name={icon} size={15} style={styles.type} />;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
formatDate = date => moment(date).calendar(null, {
|
formatDate = date => moment(date).calendar(null, {
|
||||||
|
@ -200,9 +206,20 @@ export default class RoomItem extends React.Component {
|
||||||
sameElse: 'MMM D'
|
sameElse: 'MMM D'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
renderDisclosureIndicator = () => {
|
||||||
|
if (Platform.OS === 'ios') {
|
||||||
|
return (
|
||||||
|
<View style={styles.disclosureContainer}>
|
||||||
|
<Image source={{ uri: 'disclosure_indicator' }} style={styles.disclosureIndicator} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <View style={styles.emptyDisclosureAndroid} />;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
favorite, unread, userMentions, name, _updatedAt, alert, type, testID
|
favorite, unread, userMentions, name, _updatedAt, alert, testID, height
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const date = this.formatDate(_updatedAt);
|
const date = this.formatDate(_updatedAt);
|
||||||
|
@ -232,43 +249,22 @@ export default class RoomItem extends React.Component {
|
||||||
accessibilityTraits='selected'
|
accessibilityTraits='selected'
|
||||||
testID={testID}
|
testID={testID}
|
||||||
>
|
>
|
||||||
<View style={[styles.container, favorite && styles.favorite]}>
|
<View style={[styles.container, favorite && styles.favorite, height && { height }]}>
|
||||||
{this.icon}
|
{this.avatar}
|
||||||
<View style={styles.roomNameView}>
|
<View style={styles.centerContainer}>
|
||||||
<View style={styles.firstRow}>
|
<View style={styles.titleContainer}>
|
||||||
<RoomTypeIcon type={type} />
|
{this.type}
|
||||||
<Text style={[styles.roomName, alert && styles.alert]} ellipsizeMode='tail' numberOfLines={1}>{ name }</Text>
|
<Text style={[styles.title, alert && styles.alert]} ellipsizeMode='tail' numberOfLines={1}>{ name }</Text>
|
||||||
{_updatedAt ? <Text style={[styles.update, alert && styles.updateAlert]} ellipsizeMode='tail' numberOfLines={1}>{ date }</Text> : null}
|
{_updatedAt ? <Text style={[styles.date, alert && styles.updateAlert]} ellipsizeMode='tail' numberOfLines={1}>{ date }</Text> : null}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
<Markdown
|
<Text style={styles.markdownText} numberOfLines={2}>
|
||||||
msg={this.lastMessage}
|
{this.lastMessage}
|
||||||
style={{
|
|
||||||
root: {
|
|
||||||
flex: 1
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
rules={{
|
|
||||||
mention: node => (
|
|
||||||
<Text key={node.key}>
|
|
||||||
@{node.content}
|
|
||||||
</Text>
|
</Text>
|
||||||
),
|
|
||||||
hashtag: node => (
|
|
||||||
<Text key={node.key}>
|
|
||||||
#{node.content}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
link: (node, children) => (
|
|
||||||
<Text key={node.key}>
|
|
||||||
{children}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{renderNumber(unread, userMentions)}
|
{renderNumber(unread, userMentions)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
{this.renderDisclosureIndicator()}
|
||||||
</View>
|
</View>
|
||||||
</Touch>
|
</Touch>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text, View, StyleSheet, Platform } from 'react-native';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Avatar from '../containers/Avatar';
|
||||||
|
import Touch from '../utils/touch';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
height: 54
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row'
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
marginHorizontal: 15,
|
||||||
|
marginVertical: 12
|
||||||
|
},
|
||||||
|
textContainer: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'column'
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
fontSize: 18,
|
||||||
|
color: '#0C0D0F',
|
||||||
|
marginTop: Platform.OS === 'ios' ? 6 : 3,
|
||||||
|
marginBottom: 1
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#9EA2A8'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const UserItem = ({
|
||||||
|
name, username, onPress, testID, onLongPress
|
||||||
|
}) => (
|
||||||
|
<Touch onPress={onPress} onLongPress={onLongPress} style={styles.button} testID={testID}>
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Avatar text={username} size={30} type='d' style={styles.avatar} />
|
||||||
|
<View style={styles.textContainer}>
|
||||||
|
<Text style={styles.name}>{name}</Text>
|
||||||
|
<Text style={styles.username}>@{username}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Touch>
|
||||||
|
);
|
||||||
|
|
||||||
|
UserItem.propTypes = {
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
username: PropTypes.string.isRequired,
|
||||||
|
onPress: PropTypes.func.isRequired,
|
||||||
|
testID: PropTypes.string.isRequired,
|
||||||
|
onLongPress: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserItem;
|
48
app/push.js_
|
@ -1,48 +0,0 @@
|
||||||
import PushNotification from 'react-native-push-notification';
|
|
||||||
import { AsyncStorage } from 'react-native';
|
|
||||||
import EJSON from 'ejson';
|
|
||||||
|
|
||||||
import { NavigationActions } from './Navigation';
|
|
||||||
|
|
||||||
const handleNotification = (notification) => {
|
|
||||||
if (notification.userInteraction) {
|
|
||||||
const {
|
|
||||||
rid, name, sender, type
|
|
||||||
} = EJSON.parse(notification.ejson || notification.data.ejson);
|
|
||||||
NavigationActions.push({
|
|
||||||
screen: 'RoomView',
|
|
||||||
passProps: { rid, name: type === 'd' ? sender.username : name }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
PushNotification.configure({
|
|
||||||
|
|
||||||
// (optional) Called when Token is generated (iOS and Android)
|
|
||||||
async onRegister({ token }) {
|
|
||||||
AsyncStorage.setItem('pushId', token);
|
|
||||||
},
|
|
||||||
|
|
||||||
// (required) Called when a remote or local notification is opened or received
|
|
||||||
onNotification: handleNotification,
|
|
||||||
|
|
||||||
// ANDROID ONLY: GCM Sender ID (optional - not required for local notifications, but is need to receive remote push notifications)
|
|
||||||
senderID: '673693445664',
|
|
||||||
|
|
||||||
// IOS ONLY (optional): default: all - Permissions to register.
|
|
||||||
permissions: {
|
|
||||||
alert: true,
|
|
||||||
badge: true,
|
|
||||||
sound: true
|
|
||||||
},
|
|
||||||
|
|
||||||
// Should the initial notification be popped automatically
|
|
||||||
// default: true
|
|
||||||
popInitialNotification: true,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (optional) default: true
|
|
||||||
* - Specified if permissions (ios) and token (android and ios) will requested or not,
|
|
||||||
* - if not, you must call PushNotificationsHandler.requestPermissions() later
|
|
||||||
*/
|
|
||||||
requestPermissions: true
|
|
||||||
});
|
|
|
@ -28,7 +28,10 @@ export default function login(state = initialState, action) {
|
||||||
...state,
|
...state,
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
isAuthenticated: true,
|
isAuthenticated: true,
|
||||||
user: { ...state.user, ...action.user },
|
user: {
|
||||||
|
...state.user,
|
||||||
|
...action.user
|
||||||
|
},
|
||||||
token: action.user.token,
|
token: action.user.token,
|
||||||
failure: false,
|
failure: false,
|
||||||
error: ''
|
error: ''
|
||||||
|
@ -130,6 +133,20 @@ export default function login(state = initialState, action) {
|
||||||
...state,
|
...state,
|
||||||
services: {}
|
services: {}
|
||||||
};
|
};
|
||||||
|
case types.LOGIN.SET_PREFERENCE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
user: {
|
||||||
|
...state.user,
|
||||||
|
settings: {
|
||||||
|
...state.user.settings,
|
||||||
|
preferences: {
|
||||||
|
...state.user.settings.preferences,
|
||||||
|
...action.preference
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@ import * as types from '../actions/actionsTypes';
|
||||||
const initialState = {
|
const initialState = {
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
failure: false,
|
failure: false,
|
||||||
searchText: ''
|
searchText: '',
|
||||||
|
showServerDropdown: false,
|
||||||
|
closeServerDropdown: false,
|
||||||
|
showSortDropdown: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function login(state = initialState, action) {
|
export default function login(state = initialState, action) {
|
||||||
|
@ -30,6 +33,26 @@ export default function login(state = initialState, action) {
|
||||||
...state,
|
...state,
|
||||||
searchText: action.searchText
|
searchText: action.searchText
|
||||||
};
|
};
|
||||||
|
case types.ROOMS.CLOSE_SERVER_DROPDOWN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
closeServerDropdown: !state.closeServerDropdown
|
||||||
|
};
|
||||||
|
case types.ROOMS.TOGGLE_SERVER_DROPDOWN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
showServerDropdown: !state.showServerDropdown
|
||||||
|
};
|
||||||
|
case types.ROOMS.CLOSE_SORT_DROPDOWN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
closeSortDropdown: !state.closeSortDropdown
|
||||||
|
};
|
||||||
|
case types.ROOMS.TOGGLE_SORT_DROPDOWN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
showSortDropdown: !state.showSortDropdown
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|