[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 fileTree(dir: "libs", include: ["*.jar"])
|
||||
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:design:27.1.0'
|
||||
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',
|
||||
'CLOSE',
|
||||
'SET_SERVICES',
|
||||
'REMOVE_SERVICES'
|
||||
'REMOVE_SERVICES',
|
||||
'SET_PREFERENCE'
|
||||
]);
|
||||
export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [
|
||||
...defaultTypes,
|
||||
'INIT'
|
||||
]);
|
||||
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', [
|
||||
'ADD_USER_TYPING',
|
||||
'REMOVE_USER_TYPING',
|
||||
|
|
|
@ -151,3 +151,10 @@ export function removeLoginServices() {
|
|||
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
|
||||
};
|
||||
}
|
||||
|
||||
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: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
Site_Name: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
Site_Url: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
|
|
|
@ -38,7 +38,7 @@ export default class Avatar extends React.PureComponent {
|
|||
text: '',
|
||||
size: 25,
|
||||
type: 'd',
|
||||
borderRadius: 2,
|
||||
borderRadius: 4,
|
||||
forceInitials: false
|
||||
};
|
||||
state = { showInitials: true };
|
||||
|
|
|
@ -1,36 +1,33 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Image, StyleSheet } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
type: {
|
||||
marginRight: 5,
|
||||
style: {
|
||||
marginRight: 7,
|
||||
marginTop: 3
|
||||
}
|
||||
});
|
||||
|
||||
const RoomTypeIcon = ({ type, size }) => {
|
||||
const RoomTypeIcon = ({ type, size, style }) => {
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const icon = {
|
||||
c: 'pound',
|
||||
p: 'lock',
|
||||
l: 'account',
|
||||
d: 'at'
|
||||
}[type];
|
||||
return <Icon name={icon} size={size} style={styles.type} />;
|
||||
if (type === 'c') {
|
||||
return <Image source={{ uri: 'subscription_hashtag' }} style={[styles.style, style, { width: size, height: size }]} />;
|
||||
}
|
||||
return <Image source={{ uri: 'subscription_lock' }} style={[styles.style, style, { width: size, height: size }]} />;
|
||||
};
|
||||
|
||||
RoomTypeIcon.propTypes = {
|
||||
type: PropTypes.string,
|
||||
size: PropTypes.number
|
||||
size: PropTypes.number,
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
RoomTypeIcon.defaultProps = {
|
||||
size: 15
|
||||
size: 10
|
||||
};
|
||||
|
||||
export default RoomTypeIcon;
|
||||
|
|
|
@ -68,7 +68,6 @@ export default class Markdown extends React.Component {
|
|||
hardbreak: () => null,
|
||||
blocklink: () => null,
|
||||
image: node => (
|
||||
// TODO: should use Image component
|
||||
<Image key={node.key} style={styles.inlineImage} source={{ uri: node.attributes.src }} />
|
||||
),
|
||||
...rules
|
||||
|
|
|
@ -76,6 +76,8 @@ export default {
|
|||
'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.',
|
||||
Actions: 'Actions',
|
||||
activity: 'activity',
|
||||
Activity: 'Activity',
|
||||
Add_Reaction: 'Add Reaction',
|
||||
Add_Server: 'Add Server',
|
||||
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: 'All',
|
||||
Allow_Reactions: 'Allow Reactions',
|
||||
Alphabetical: 'Alphabetical',
|
||||
and_more: 'and more',
|
||||
and: 'and',
|
||||
announcement: 'announcement',
|
||||
|
@ -108,6 +111,7 @@ export default {
|
|||
Cancel: 'Cancel',
|
||||
changing_avatar: 'changing avatar',
|
||||
Channel_Name: 'Channel Name',
|
||||
Channels: 'Channels',
|
||||
Chats: 'Chats',
|
||||
Close: 'Close',
|
||||
Close_emoji_selector: 'Close emoji selector',
|
||||
|
@ -133,6 +137,7 @@ export default {
|
|||
description: 'description',
|
||||
Description: 'Description',
|
||||
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?',
|
||||
edit: 'edit',
|
||||
Edit: 'Edit',
|
||||
|
@ -141,6 +146,7 @@ export default {
|
|||
Enable_notifications: 'Enable notifications',
|
||||
Everyone_can_access_this_channel: 'Everyone can access this channel',
|
||||
Error_uploading: 'Error uploading',
|
||||
Favorites: 'Favorites',
|
||||
Files: 'Files',
|
||||
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',
|
||||
|
@ -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: '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_left_the_channel: 'Has left the channel',
|
||||
I_have_an_account: 'I have an account',
|
||||
|
@ -161,6 +169,7 @@ export default {
|
|||
last_message: 'last message',
|
||||
Leave_channel: 'Leave channel',
|
||||
leave: 'leave',
|
||||
Livechat: 'Livechat',
|
||||
Loading_messages_ellipsis: 'Loading messages...',
|
||||
Login: 'Login',
|
||||
Logout: 'Logout',
|
||||
|
@ -180,6 +189,7 @@ export default {
|
|||
My_servers: 'My servers',
|
||||
N_online_members: '{{n}} online members',
|
||||
N_person_reacted: '{{n}} people reacted',
|
||||
name: 'name',
|
||||
Name: 'Name',
|
||||
New_in_RocketChat_question_mark: 'New in Rocket.Chat?',
|
||||
New_Message: 'New Message',
|
||||
|
@ -216,6 +226,7 @@ export default {
|
|||
Preferences_saved: 'Preferences saved!',
|
||||
Privacy_Policy: ' Privacy Policy',
|
||||
Private_Channel: 'Private Channel',
|
||||
Private_Groups: 'Private Groups',
|
||||
Private: 'Private',
|
||||
Profile_saved_successfully: 'Profile saved successfully!',
|
||||
Profile: 'Profile',
|
||||
|
@ -257,6 +268,7 @@ export default {
|
|||
Send: 'Send',
|
||||
Send_audio_message: 'Send audio message',
|
||||
Send_message: 'Send message',
|
||||
Server: 'Server',
|
||||
Servers: 'Servers',
|
||||
Settings: 'Settings',
|
||||
Settings_succesfully_changed: 'Settings succesfully changed!',
|
||||
|
@ -268,6 +280,7 @@ export default {
|
|||
snippeted: 'snippeted',
|
||||
Snippets: 'Snippets',
|
||||
Some_field_is_invalid_or_empty: 'Some field is invalid or empty',
|
||||
Sorting_by: 'Sorting by {{key}}',
|
||||
Star_room: 'Star room',
|
||||
Star: 'Star',
|
||||
Starred_Messages: 'Starred Messages',
|
||||
|
@ -296,6 +309,8 @@ export default {
|
|||
unmuted: 'unmuted',
|
||||
Unpin: 'Unpin',
|
||||
unread_messages: 'unread messages',
|
||||
Unread: 'Unread',
|
||||
Unread_on_top: 'Unread on top',
|
||||
Unstar: 'Unstar',
|
||||
Uploading: 'Uploading',
|
||||
User_added_by: 'User {{userAdded}} added by {{userBy}}',
|
||||
|
|
|
@ -11,18 +11,36 @@ const getLastUpdate = () => {
|
|||
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() {
|
||||
try {
|
||||
const lastUpdate = getLastUpdate();
|
||||
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(() =>
|
||||
database.write(() =>
|
||||
filteredSettings.forEach(setting =>
|
||||
database.create('settings', { ...setting, _updatedAt: new Date() }, true))));
|
||||
filteredSettings.forEach((setting) => {
|
||||
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)));
|
||||
|
||||
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) {
|
||||
log('getSettings', e);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ const serversSchema = {
|
|||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'string',
|
||||
name: { type: 'string', optional: true },
|
||||
iconURL: { type: 'string', optional: true },
|
||||
current: 'bool'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -140,7 +140,8 @@ const RocketChat = {
|
|||
user = { ...user, ...userInfo.user };
|
||||
}
|
||||
RocketChat.registerPushToken(user.id);
|
||||
return reduxStore.dispatch(loginSuccess(user));
|
||||
reduxStore.dispatch(loginSuccess(user));
|
||||
this.ddp.subscribe('userData');
|
||||
} catch (e) {
|
||||
log('rocketchat.loginSuccess', e);
|
||||
}
|
||||
|
@ -468,7 +469,7 @@ const RocketChat = {
|
|||
log('rocketchat.logout', e);
|
||||
}
|
||||
}
|
||||
database.deleteAll();
|
||||
// database.deleteAll();
|
||||
AsyncStorage.removeItem(TOKEN_KEY);
|
||||
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
|
||||
},
|
||||
|
|
|
@ -1,91 +1,101 @@
|
|||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View, Text, StyleSheet, ViewPropTypes } from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import { View, Text, StyleSheet, Image, Platform } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { emojify } from 'react-emojione';
|
||||
|
||||
import Avatar from '../containers/Avatar';
|
||||
import Status from '../containers/status';
|
||||
import Touch from '../utils/touch/index'; //eslint-disable-line
|
||||
import Markdown from '../containers/message/Markdown';
|
||||
import RoomTypeIcon from '../containers/RoomTypeIcon';
|
||||
import I18n from '../i18n';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: '#ddd'
|
||||
alignItems: 'center'
|
||||
},
|
||||
number: {
|
||||
minWidth: 25,
|
||||
centerContainer: {
|
||||
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,
|
||||
backgroundColor: '#1d74f5',
|
||||
backgroundColor: '#1D74F5',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
unreadNumberText: {
|
||||
color: '#fff',
|
||||
overflow: 'hidden',
|
||||
fontSize: 14,
|
||||
paddingVertical: 4,
|
||||
paddingHorizontal: 5,
|
||||
|
||||
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'
|
||||
fontWeight: '500',
|
||||
letterSpacing: 0.56
|
||||
},
|
||||
status: {
|
||||
position: 'absolute',
|
||||
bottom: -3,
|
||||
right: -3,
|
||||
borderWidth: 3,
|
||||
borderColor: '#fff'
|
||||
},
|
||||
type: {
|
||||
marginRight: 5,
|
||||
borderRadius: 10,
|
||||
width: 10,
|
||||
height: 10,
|
||||
marginRight: 7,
|
||||
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 (
|
||||
<Text style={styles.number}>
|
||||
{ unread }
|
||||
</Text>
|
||||
<View style={styles.unreadNumberContainer}>
|
||||
<Text style={styles.unreadNumberText}>{ unread }</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -131,13 +141,13 @@ export default class RoomItem extends React.Component {
|
|||
onLongPress: PropTypes.func,
|
||||
username: PropTypes.string,
|
||||
avatarSize: PropTypes.number,
|
||||
statusStyle: ViewPropTypes.style,
|
||||
testID: PropTypes.string
|
||||
testID: PropTypes.string,
|
||||
height: PropTypes.number
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
showLastMessage: true,
|
||||
avatarSize: 46
|
||||
avatarSize: 48
|
||||
}
|
||||
shouldComponentUpdate(nextProps) {
|
||||
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]);
|
||||
}
|
||||
get icon() {
|
||||
const {
|
||||
type, name, id, avatarSize, statusStyle
|
||||
} = this.props;
|
||||
return (<Avatar text={name} size={avatarSize} type={type}>{type === 'd' ? <Status style={[styles.status, statusStyle]} id={id} /> : null }</Avatar>);
|
||||
get avatar() {
|
||||
const { type, name, avatarSize } = this.props;
|
||||
return <Avatar text={name} size={avatarSize} type={type} style={{ marginHorizontal: 15 }} />;
|
||||
}
|
||||
|
||||
get lastMessage() {
|
||||
|
@ -178,19 +186,17 @@ export default class RoomItem extends React.Component {
|
|||
prefix = `${ lastMessage.u.username }: `;
|
||||
}
|
||||
|
||||
const msg = `${ prefix }${ lastMessage.msg.replace(/[\n\t\r]/igm, '') }`;
|
||||
const maxChars = 35;
|
||||
return `${ msg.slice(0, maxChars) }${ msg.replace(/:[a-z0-9]+:/gi, ':::').length > maxChars ? '...' : '' }`;
|
||||
let msg = `${ prefix }${ lastMessage.msg.replace(/[\n\t\r]/igm, '') }`;
|
||||
msg = emojify(msg, { output: 'unicode' });
|
||||
return msg;
|
||||
}
|
||||
|
||||
get type() {
|
||||
const icon = {
|
||||
c: 'pound',
|
||||
p: 'lock',
|
||||
l: 'account',
|
||||
d: 'at'
|
||||
}[this.props.type];
|
||||
return <Icon name={icon} size={15} style={styles.type} />;
|
||||
const { type, id } = this.props;
|
||||
if (type === 'd') {
|
||||
return <Status style={[styles.status]} id={id} />;
|
||||
}
|
||||
return <RoomTypeIcon type={type} />;
|
||||
}
|
||||
|
||||
formatDate = date => moment(date).calendar(null, {
|
||||
|
@ -200,9 +206,20 @@ export default class RoomItem extends React.Component {
|
|||
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() {
|
||||
const {
|
||||
favorite, unread, userMentions, name, _updatedAt, alert, type, testID
|
||||
favorite, unread, userMentions, name, _updatedAt, alert, testID, height
|
||||
} = this.props;
|
||||
|
||||
const date = this.formatDate(_updatedAt);
|
||||
|
@ -232,43 +249,22 @@ export default class RoomItem extends React.Component {
|
|||
accessibilityTraits='selected'
|
||||
testID={testID}
|
||||
>
|
||||
<View style={[styles.container, favorite && styles.favorite]}>
|
||||
{this.icon}
|
||||
<View style={styles.roomNameView}>
|
||||
<View style={styles.firstRow}>
|
||||
<RoomTypeIcon type={type} />
|
||||
<Text style={[styles.roomName, alert && styles.alert]} ellipsizeMode='tail' numberOfLines={1}>{ name }</Text>
|
||||
{_updatedAt ? <Text style={[styles.update, alert && styles.updateAlert]} ellipsizeMode='tail' numberOfLines={1}>{ date }</Text> : null}
|
||||
<View style={[styles.container, favorite && styles.favorite, height && { height }]}>
|
||||
{this.avatar}
|
||||
<View style={styles.centerContainer}>
|
||||
<View style={styles.titleContainer}>
|
||||
{this.type}
|
||||
<Text style={[styles.title, alert && styles.alert]} ellipsizeMode='tail' numberOfLines={1}>{ name }</Text>
|
||||
{_updatedAt ? <Text style={[styles.date, alert && styles.updateAlert]} ellipsizeMode='tail' numberOfLines={1}>{ date }</Text> : null}
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Markdown
|
||||
msg={this.lastMessage}
|
||||
style={{
|
||||
root: {
|
||||
flex: 1
|
||||
}
|
||||
}}
|
||||
rules={{
|
||||
mention: node => (
|
||||
<Text key={node.key}>
|
||||
@{node.content}
|
||||
</Text>
|
||||
),
|
||||
hashtag: node => (
|
||||
<Text key={node.key}>
|
||||
#{node.content}
|
||||
</Text>
|
||||
),
|
||||
link: (node, children) => (
|
||||
<Text key={node.key}>
|
||||
{children}
|
||||
</Text>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Text style={styles.markdownText} numberOfLines={2}>
|
||||
{this.lastMessage}
|
||||
</Text>
|
||||
{renderNumber(unread, userMentions)}
|
||||
</View>
|
||||
</View>
|
||||
{this.renderDisclosureIndicator()}
|
||||
</View>
|
||||
</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,
|
||||
isFetching: false,
|
||||
isAuthenticated: true,
|
||||
user: { ...state.user, ...action.user },
|
||||
user: {
|
||||
...state.user,
|
||||
...action.user
|
||||
},
|
||||
token: action.user.token,
|
||||
failure: false,
|
||||
error: ''
|
||||
|
@ -130,6 +133,20 @@ export default function login(state = initialState, action) {
|
|||
...state,
|
||||
services: {}
|
||||
};
|
||||
case types.LOGIN.SET_PREFERENCE:
|
||||
return {
|
||||
...state,
|
||||
user: {
|
||||
...state.user,
|
||||
settings: {
|
||||
...state.user.settings,
|
||||
preferences: {
|
||||
...state.user.settings.preferences,
|
||||
...action.preference
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,10 @@ import * as types from '../actions/actionsTypes';
|
|||
const initialState = {
|
||||
isFetching: false,
|
||||
failure: false,
|
||||
searchText: ''
|
||||
searchText: '',
|
||||
showServerDropdown: false,
|
||||
closeServerDropdown: false,
|
||||
showSortDropdown: false
|
||||
};
|
||||
|
||||
export default function login(state = initialState, action) {
|
||||
|
@ -30,6 +33,26 @@ export default function login(state = initialState, action) {
|
|||
...state,
|
||||
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:
|
||||
return state;
|
||||
}
|
||||
|
|