Commit 9d86db94 authored by Juan Manuel Solaro's avatar Juan Manuel Solaro

(feat) use discovery store for suggested groups

1 merge request!472WIP: oboarding/welcome-screen
......@@ -264,7 +264,7 @@
"removeOwner":"Remove as Owner",
"makeModerator":"Make Moderator",
"removeModerator":"Remove as Moderator",
"listMembersCount":"Members {{count}}",
"listMembersCount":"{{count}} members",
"disableConversations":"Disable conversations",
"enableConversations":"Enable conversations"
},
......
......@@ -148,7 +148,7 @@ export default class RegisterFormNew extends Component {
<SubTitle>
{i18n.to('auth.alreadyHaveAccount', null, {
login: (
<Text style={[ComponentsStyle.linkNew, CommonStyle.fontL]} onPress={this.onPressBack}>
<Text style={[ComponentsStyle.linkNew, CommonStyle.fontL]} onPress={this.props.onBack}>
{i18n.t('auth.login')}
</Text>
),
......@@ -167,13 +167,6 @@ export default class RegisterFormNew extends Component {
);
}
/**
* On press back
*/
onPressBack() {
this.props.onBack();
}
/**
* On press register
*/
......
......@@ -48,6 +48,7 @@ export default class Input extends Component {
style={[ComponentsStyle.loginInputNew, styles.shadow, this.props.style]}
textStyle={{color: '#FFFFFF'}}
onChangePhoneNumber={this.props.onChangeText}
onEndEditing={this.props.onEndEditing}
ref="phoneInput"
placeholder=''
/>
......
import React, {Component} from 'react';
import {StyleSheet, TouchableOpacity} from 'react-native';
export default class ListItemButton extends Component {
render() {
return (
<TouchableOpacity
onPress={this.props.onPress}
borderRadius={2}
style={styles.container}>
{this.props.children}
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'transparent',
borderRadius: 4,
borderColor: '#404A4E',
borderWidth: 1,
padding:4,
}
});
\ No newline at end of file
......@@ -33,7 +33,7 @@ const Container = styled.View`
`;
const Body = styled.View`
flex: 9;
flex: 10;
flex-direction: row;
justify-content: center;
background-color: ${(props) => props.theme[props.backgroundColor] || props.theme['primary_background']};
......@@ -41,7 +41,7 @@ const Body = styled.View`
`;
const Footer = styled.View`
flex: 3;
flex: 2;
flex-direction: row;
justify-content: center;
background-color: ${(props) => props.theme[props.backgroundColor] || props.theme['primary_background']};
......
import React, {
Component
} from 'react';
import {
observer,
inject
} from 'mobx-react/native'
import {
MINDS_CDN_URI
} from '../config/Config';
import { ListItem, Avatar } from 'react-native-elements';
import colors from '../styles/Colors';
import i18n from '../common/services/i18n.service';
import { CommonStyle as CS } from '../styles/Common';
import { FLAG_JOIN } from '../common/Permissions';
import { StyleSheet } from 'react-native';
import abbrev from '../common/helpers/abbrev';
import ListItemButton from '../common/components/ListItemButton';
import Icon from 'react-native-vector-icons/MaterialIcons';
import FastImage from 'react-native-fast-image';
export default
@inject('groupView')
@observer
class GroupsListItemNew extends Component {
/**
* Render
*/
render() {
const button = this.getButton();
return (
<ListItem
containerStyle={styles.container}
title={this.props.group.name}
titleStyle={styles.title}
keyExtractor={item => item.rowKey}
avatar={
<FastImage
source={{ uri: this.getAvatar(this.props.group) }}
style={[{
width: 42,
height: 42,
borderRadius: 21,
backgroundColor: 'transparent'
}]}
/>
}
subtitle={i18n.t('groups.listMembersCount', {count: abbrev(this.props.group['members:count'])})}
subtitleStyle={styles.subtitle}
hideChevron={!button}
rightIcon={button}
/>
);
}
getAvatar = (group) => {
return `${MINDS_CDN_URI}fs/v1/avatars/${group.guid}/large/${group.icontime}`;
}
/**
* On press
*/
_onPress = () => {
if (this.props.onPress) {
this.props.onPress(this.props.group)
}
}
/**
* Get button
*/
getButton = () => {
return this.props.group['is:member'] ? this.getLeaveButton() : this.getJoinButton();
}
getJoinButton = () => {
return (
<ListItemButton onPress={this.join}>
<Icon name="add" size={26} color={'#A5A5A5'} />
</ListItemButton>
);
};
getLeaveButton = () => {
return (
<ListItemButton onPress={this.leave}>
<Icon name="check" size={26} color={'#4C92A4'} />
</ListItemButton>
);
}
/**
* Join the group
*/
join = () => {
if (!this.props.group.can(FLAG_JOIN, true)) return;
this.props.groupView.setGroup(this.props.group);
this.props.groupView.join(this.props.group.guid);
}
/**
* Leave the group
*/
leave = () => {
this.props.groupView.setGroup(this.props.group);
this.props.groupView.leave(this.props.group.guid);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'transparent'
},
title: {
color: '#FFF',
fontSize: 17,
fontWeight: '500'
},
subtitle: {
color: '#AEB0B8',
fontSize: 14,
}
});
import React, { Component } from 'react';
import {View, Text, TouchableHighlight, StyleSheet} from 'react-native';
import {View, Text, TouchableHighlight, StyleSheet, TouchableOpacity, Image} from 'react-native';
import { observer, inject } from 'mobx-react';
import { CommonStyle as CS } from '../../styles/Common';
import i18n from '../../common/services/i18n.service';
import { ComponentsStyle } from '../../styles/Components';
import { TouchableOpacity, ScrollView } from 'react-native-gesture-handler';
import { ScrollView } from 'react-native-gesture-handler';
import Input from '../../common/components/Input';
import MindsLayout from '../../common/components/MindsLayout';
......@@ -15,31 +15,142 @@ import { CommonStyled } from '../../styles/CommonStyled';
import OnboardingButtons from '../OnboardingButtons';
import OnboardingBackButton from '../OnboardingBackButton';
import sessionService from '../../common/services/session.service';
import imagePicker from '../../common/services/image-picker.service';
import withPreventDoubleTap from '../../common/components/PreventDoubleTap';
import { UserError } from '../../common/UserError';
import Icon from 'react-native-vector-icons/Ionicons';
import * as Progress from 'react-native-progress';
const TouchableCustom = withPreventDoubleTap(TouchableOpacity);
@inject('channel', 'user')
@observer
export default class ChannelSetupStepNew extends Component {
state = {
phoneNumber: '+1',
location: '',
birthDate: '',
preview_avatar: null,
preview_banner: null,
saving: false,
dirty: false
};
uploads = {
avatar: null,
banner: null
};
store;
constructor(props) {
super(props);
this.store = this.props.channel.store(sessionService.guid);
}
changeAvatarAction = async () => {
try {
const response = await imagePicker.show('Select avatar', 'photo');
if (response) {
this.selectMedia(response);
}
} catch (err) {
alert(err);
}
};
selectMedia(file) {
this.setState({
preview_avatar: file.uri
});
this.store.uploadAvatar(file);
this.uploads['avatar'] = file;
}
getAvatar() {
if (this.state.preview_avatar) {
return { uri: this.state.preview_avatar };
}
return this.props.user.me.getAvatarSource();
}
setPhoneNumber = phoneNumber => this.setState({phoneNumber});
setLocation = location => this.setState({location});
setBirthDate = birthDate => this.setState({birthDate});
save = async () => {
if (this.store.isUploading) throw new UserError('Avatar is uploading, please wait');
if (!this.state.dirty) return;
const {phoneNumber, location, birthDate} = this.state;
payload = {
phoneNumber,
location,
birthDate
};
this.setState({saving: true});
const response = await this.store.save(payload);
if (response === true) {
await this.props.user.load(true);
this.setState({saving: false});
this.uploads = {
avatar: null,
banner: null
};
} else if (response === false) {
alert('Error saving channel');
this.setState({saving: false});
} else {
alert(response)
this.setState({saving: false});
}
}
getBody = () => {
const hasAvatar = this.props.user.hasAvatar() || this.state.preview_avatar;
const avatar = this.getAvatar();
return (
<View style={[CS.flexContainer, CS.columnAlignCenter]}>
<OnboardingBackButton onBack={this.props.onBack} />
<View style={styles.textsContainer}>
<View style={[styles.textsContainer]}>
<Text style={[CS.onboardingTitle, CS.marginBottom2x]}>{i18n.t('onboarding.profileSetup')}</Text>
<TitleText>{i18n.t('onboarding.infoTitle')}</TitleText>
<Step>{i18n.t('onboarding.step',{step: 2, total: 4})}</Step>
<SubTitle>{i18n.t('onboarding.suggestedGroupsDescription')}</SubTitle>
</View>
<ScrollView style={styles.inputContainer}>
<View style={[CS.padding4x, CS.flexContainer, CS.rowJustifyStart, CS.alignCenter, CS.marginBottom2x, CS.marginTop2x]}>
<Text style={[CS.fontXXL, {color: '#AEB0B8'}, CS.fontMedium]}>{i18n.t('onboarding.chooseAvatar')}</Text>
<View style={[CS.rowJustifyEnd, CS.flexContainer]}>
<TouchableCustom
onPress={this.changeAvatarAction}
style={[styles.avatar, CS.marginLeft3x, CS.border, {borderColor: '#404A4E'} ]}
disabled={this.saving}
testID="selectAvatar"
>
{hasAvatar && <Image source={avatar} style={styles.wrappedAvatar} />}
<View style={[styles.tapOverlayView, hasAvatar ? null : CS.backgroundTransparent]}/>
<View style={[styles.overlay, CS.centered]}>
<Icon name="md-cloud-upload" size={40} color={hasAvatar ? '#FFF': '#404A4E'} />
</View>
{(this.store.isUploading && this.store.avatarProgress) ? <View style={[styles.tapOverlayView, styles.progress]}>
<Progress.Pie progress={this.store.avatarProgress} size={36} />
</View>: null}
</TouchableCustom>
</View>
</View>
<Input
placeholder={i18n.t('onboarding.infoMobileNumber')}
onChangeText={this.setPhoneNumber}
onEndEditing={(e) => console.log(e.nativeEvent.text)}
value={this.state.phoneNumber}
editable={true}
optional={true}
......@@ -107,11 +218,43 @@ const styles = StyleSheet.create({
justifyContent: 'flex-end',
},
inputContainer: {
flex: 6,
width: '100%',
},
textsContainer: {
flex: 1.5,
alignItems: 'center',
},
avatar: {
height: 90,
width: 90,
borderRadius: 45
},
progress: {
opacity: 0.8
},
overlay: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
},
tapOverlayView: {
position: 'absolute',
height: 90,
width: 90,
borderRadius: 45,
top: 0,
right: 0,
bottom: 0,
left: 0,
backgroundColor: '#000',
opacity: 0.12,
alignItems: 'center',
justifyContent: 'center',
},
wrappedAvatar: {
height: 90,
width: 90,
borderRadius: 45
}
});
\ No newline at end of file
......@@ -88,10 +88,8 @@ const Step = styled.Text`
const styles = StyleSheet.create({
hashtagContainer: {
flex: 3,
},
textsContainer: {
flex: 4,
alignItems: 'center',
},
hashtag: {
......
......@@ -9,7 +9,7 @@ import {
import { observer, inject } from 'mobx-react';
import { CommonStyle as CS } from '../../styles/Common';
import GroupsListItem from '../../groups/GroupsListItem';
import GroupsListItemNew from '../../groups/GroupsListItemNew';
import i18n from '../../common/services/i18n.service';
import MindsLayout from '../../common/components/MindsLayout';
......@@ -18,21 +18,28 @@ import { CommonStyled } from '../../styles/CommonStyled';
import OnboardingButtons from '../OnboardingButtons';
import OnboardingBackButton from '../OnboardingBackButton';
@inject('groups', 'hashtag')
@inject('discovery')
@observer
export default class SuggestedGroupsStepNew extends Component {
constructor(props) {
super(props);
this.props.discovery.init();
}
componentDidMount() {
this.props.hashtag.setAll(true);
this.props.groups.reset();
this.props.groups.loadList('suggested');
this.props.discovery.filters.setType('groups');
this.props.discovery.filters.setPeriod('30d');
}
renderGroup = (group) => {
return <GroupsListItem key={group.guid} group={group}/>
return <GroupsListItemNew key={group.guid} group={group}/>
}
getBody = () => {
const discovery = this.props.discovery;
return (
<View style={[CS.flexContainer, CS.columnAlignCenter]}>
<OnboardingBackButton onBack={this.props.onBack} />
......@@ -43,7 +50,7 @@ export default class SuggestedGroupsStepNew extends Component {
<SubTitle>{i18n.t('onboarding.suggestedGroupsDescription')}</SubTitle>
</View>
<ScrollView style={styles.groupContainer}>
{this.props.groups.list.entities.map(group => this.renderGroup(group))}
{discovery.listStore.entities.slice().map(group => this.renderGroup(group))}
</ScrollView>
</View>
);
......@@ -84,11 +91,9 @@ const Step = styled.Text`
const styles = StyleSheet.create({
groupContainer: {
flex: 3.5,
width: '100%',
},
textsContainer: {
flex: 1.5,
alignItems: 'center',
},
});
\ No newline at end of file
Please register or to comment