Commit 723b9eb8 authored by Martin Santangelo's avatar Martin Santangelo

(feat) refactor for navigator 5

1 merge request!521Refactor to use react navigator 5, theme swithing, some layout fixing of new screens
......@@ -120,7 +120,7 @@ sessionService.onLogin(async () => {
try {
// handle deep link (if the app is opened by one)
if (deepLinkUrl) {
deeplinkService.navigate(deepLinkUrl);
deeplinkService.navigate('App', { screen: deepLinkUrl});
deepLinkUrl = '';
}
......@@ -215,6 +215,13 @@ class App extends Component<Props, State> {
* On component did mount
*/
async componentDidMount() {
this.appInit();
}
/**
* App initialization
*/
async appInit() {
try {
// load app setting before start
const results = await Promise.all([settingsStore.init(), Linking.getInitialURL()]);
......@@ -233,7 +240,7 @@ class App extends Component<Props, State> {
if (!token) {
logService.info('[App] there is no active session');
RNBootSplash.hide({ duration: 250 });
NavigationService.navigate('Login');
// NavigationService.navigate('Auth', { screen: 'Login'});
} else {
logService.info('[App] session initialized');
}
......@@ -318,16 +325,18 @@ class App extends Component<Props, State> {
return null;
}
const isLoggedIn = sessionService.userLoggedIn;
const app = (
<SafeAreaProvider>
<NavigationContainer
ref={navigatorRef => setTopLevelNavigator(navigatorRef)}
theme={ThemedStyles.theme === 1 ? DarkTheme : DefaultTheme}
theme={ThemedStyles.navTheme}
>
<Provider key="app" {...stores}>
<ErrorBoundary message="An error occurred" containerStyle={CS.centered}>
<StatusBar barStyle={statusBarStyle} />
<NavigationStack key={ThemedStyles.theme}/>
<NavigationStack key={ThemedStyles.theme} isLoggedIn={isLoggedIn}/>
<FlashMessage renderCustomContent={this.renderNotification} />
</ErrorBoundary>
</Provider>
......
......@@ -43,13 +43,13 @@ describe('Activity screen component', () => {
it('renders correctly with an entity as param', async () => {
navigation = {
push: jest.fn(),
state: {
route: {
routeName: 'some',
params: {entity: activitiesServiceFaker().load(1).activities[0]}
}
};
entitiesService.single.mockResolvedValue(ActivityModel.create(navigation.state.params.entity));
entitiesService.single.mockResolvedValue(ActivityModel.create(navigation.route.entity));
screen = shallow(
<ActivityScreen navigation={navigation}/>
......
......@@ -20,8 +20,8 @@
android.useAndroidX=true
android.enableJetifier=true
# MYAPP_RELEASE_STORE_FILE=minds.keystore
# MYAPP_RELEASE_KEY_ALIAS=alias_name
MYAPP_RELEASE_STORE_FILE=minds.keystore
MYAPP_RELEASE_KEY_ALIAS=alias_name
org.gradle.jvmargs=-Xmx2048m
systemProp.org.gradle.internal.http.connectionTimeout=180000
systemProp.org.gradle.internal.http.socketTimeout=180000
......
......@@ -5,7 +5,7 @@ import "./global";
import { AppRegistry, Platform } from 'react-native';
import App from './App';
import { useScreens } from 'react-native-screens';
useScreens(Platform.OS !== 'ios');
useScreens();
// const modules = require.getModules();
// const moduleIds = Object.keys(modules);
......
{
"name": "Minds",
"version": "3.12.0",
"version": "3.16.0",
"private": true,
"scripts": {
"android": "react-native run-android",
......@@ -23,9 +23,9 @@
"@react-native-community/cameraroll": "^1.3.0",
"@react-native-community/masked-view": "^0.1.6",
"@react-native-community/netinfo": "^4.4.0",
"@react-navigation/bottom-tabs": "^5.0.1",
"@react-navigation/native": "^5.0.1",
"@react-navigation/stack": "^5.0.1",
"@react-navigation/bottom-tabs": "^5.0.3",
"@react-navigation/native": "^5.0.3",
"@react-navigation/stack": "^5.0.3",
"@sentry/react-native": "^1.2.2",
"crypto-js": "^3.1.9-1",
"detox": "^14.8.1",
......
......@@ -22,8 +22,8 @@ export default class ForgotScreen extends Component {
*/
render() {
const code =
this.props.navigation.state.params &&
this.props.navigation.state.params.code;
this.props.route.params &&
this.props.route.params.code;
const CS = ThemedStyles.style;
return (
......
......@@ -115,7 +115,7 @@ export default class ResetPassword extends PureComponent {
if (!this.state.sent ) {
this.setState({sending: true});
const state = navigation.getCurrentState();
const state = navigation.dangerouslyGetState();
try {
const data = await authService.reset(state.params.username, this.state.password, state.params.code);
......
......@@ -69,15 +69,15 @@ export default class BlockchainWalletDetailsScreen extends Component {
this.setState({
editable: false,
importable: false,
address: this.props.navigation.state.params.address,
edit: !!this.props.navigation.state.params.edit,
new: this.props.navigation.state.params.new,
address: this.props.route.params.address,
edit: !!this.props.route.params.edit,
new: this.props.route.params.new,
offchain: false,
tokens: null,
eth: null,
});
this.load(this.props.navigation.state.params.address);
this.load(this.props.route.params.address);
}
componentDidMount() {
......
......@@ -31,7 +31,7 @@ export default class BlockchainWalletImportScreen extends Component {
};
componentWillMount() {
const params = this.props.navigation.state.params || {};
const params = this.props.route.params || {};
this.setState({
importingRemote: !!params.address,
......@@ -81,7 +81,7 @@ export default class BlockchainWalletImportScreen extends Component {
async import() {
await this.props.blockchainWallet.import(this.state.privateKey);
const params = this.props.navigation.state.params || {};
const params = this.props.route.params || {};
if (params.onSuccess) {
params.onSuccess();
......
......@@ -41,7 +41,7 @@ import ThumbDownAction from '../newsfeed/activity/actions/ThumbDownAction';
import RemindAction from '../newsfeed/activity/actions/RemindAction';
import CommentsAction from '../newsfeed/activity/actions/CommentsAction';
import shareService from '../share/ShareService';
import commentsStoreProvider from '../comments/CommentsStoreProvider';
import Provider from '../comments/CommentsStoreProvider';
import CommentList from '../comments/CommentList';
import CenteredLoading from '../common/components/CenteredLoading';
import logService from '../common/services/log.service';
......@@ -86,7 +86,7 @@ export default class BlogsViewScreen extends Component {
* Component did mount
*/
async componentDidMount() {
const params = this.props.navigation.state.params;
const params = this.props.route.params;
try {
if (params.blog) {
if (params.blog._list && params.blog._list.metadataService) {
......@@ -267,6 +267,7 @@ export default class BlogsViewScreen extends Component {
entity={this.props.blogsView.blog}
store={this.comments}
navigation={this.props.navigation}
route={this.props.route}
keyboardVerticalOffset = {Header.HEIGHT - 65}
/>
:
......
......@@ -37,7 +37,7 @@ export default class BoostConsoleScreen extends Component {
* On component will mount
*/
componentWillMount() {
const filter = this.props.navigation.state.params.filter;
const filter = this.props.route.params.filter;
if (filter) {
this.props.boost.setFilter(filter);
......
......@@ -271,7 +271,7 @@ export default class BoostScreen extends Component {
}
buildAllowedTypes() {
const entity = this.props.navigation.state.params.entity;
const entity = this.props.route.params.entity;
if (!entity || !entity.type) {
this.setState({});
......@@ -400,7 +400,7 @@ export default class BoostScreen extends Component {
}
async _submitBoost() {
const entity = this.props.navigation.state.params.entity;
const entity = this.props.route.params.entity;
this.setState({ inProgress: true });
let guid = null;
......
......@@ -4,8 +4,8 @@ import {
StyleSheet,
TouchableOpacity,
Image,
FlatList,
Platform,
FlatList
} from 'react-native';
import CameraRoll from '@react-native-community/cameraroll';
......@@ -14,11 +14,9 @@ import Icon from 'react-native-vector-icons/Ionicons';
import CenteredLoading from '../common/components/CenteredLoading';
import androidPermissionsService from '../common/services/android-permissions.service';
import testID from '../common/helpers/testID';
import logService from '../common/services/log.service';
import { CommonStyle } from '../styles/Common';
import { View } from 'react-native-animatable';
/**
* Gallery View
*/
......@@ -109,20 +107,23 @@ export default class CaptureGallery extends PureComponent {
* Render
*/
render() {
if (!this.state.imagesLoaded) {
return <CenteredLoading />;
}
return (
<FlatList
ref={this.setListRef}
ListHeaderComponent={this.props.header}
data={this.state.photos}
renderItem={this.renderTile}
onEndReached={this._loadPhotos}
ListFooterComponent={this.state.loading ? <CenteredLoading /> : null}
numColumns={3}
/>
<View style={[CommonStyle.backgroundWhite, CommonStyle.flexContainer]}>
{this.state.imagesLoaded ? (
<FlatList
ref={this.setListRef}
ListHeaderComponent={this.props.header}
data={this.state.photos}
renderItem={this.renderTile}
style={CommonStyle.flexContainer}
onEndReached={this._loadPhotos}
ListFooterComponent={this.state.loading ? <CenteredLoading /> : null}
numColumns={3}
/>
) : (
<CenteredLoading />
)}
</View>
);
}
......
......@@ -40,19 +40,20 @@ import TextInput from '../common/components/TextInput';
import TabIcon from '../tabs/TabIcon';
import TopbarNew from '../topbar/TopbarNew';
export default
@inject('user', 'capture')
@observer
export default class CapturePoster extends Component {
/**
* Disable navigation bar
*/
static navigationOptions = ({ navigation }) => ({
tabBarIcon: ({ tintColor }) => (
<TabIcon name="md-add-circle-outline" size={44} color={tintColor} />
),
headerRight: navigation.state.params && navigation.state.params.headerRight
});
class CapturePoster extends Component {
// /**
// * Disable navigation bar
// */
// static navigationOptions = ({ route }) => ({
// tabBarIcon: ({ tintColor }) => (
// <TabIcon name="md-add-circle-outline" size={44} color={tintColor} />
// ),
// headerRight: route && route.headerRight
// });
state = {
postImageUri: '',
......@@ -67,27 +68,11 @@ export default class CapturePoster extends Component {
time_created: null,
};
/**
* On component will mount
*/
componentWillMount() {
const { setParams } = this.props.navigation;
let { params } = this.props.navigation.state;
if (!params) params = {};
setParams({
headerRight: <CapturePostButton
onPress={() => !params.isRemind ? this.submit() : this.remind()}
text={params.isRemind ? i18n.t('capture.remind').toUpperCase() : i18n.t('capture.post').toUpperCase()}
testID="CapturePostButton"
/>
});
}
/**
* On component did mount
*/
componentDidMount() {
const { params } = this.props.navigation.state;
let { params } = this.props.route;
if (params) {
this.props.capture.reset();
......@@ -106,6 +91,24 @@ export default class CapturePoster extends Component {
}
}
const { setOptions } = this.props.navigation;
if (!params) {
params = {};
}
setOptions({
headerRight: () => (
<CapturePostButton
onPress={() => (!params.isRemind ? this.submit() : this.remind())}
text={
params.isRemind
? i18n.t('capture.remind').toUpperCase()
: i18n.t('capture.post').toUpperCase()
}
testID="CapturePostButton"
/>
)
});
this.loadNsfwFromPersistentStorage();
}
......@@ -130,7 +133,7 @@ export default class CapturePoster extends Component {
* Show context
*/
showContext () {
let group = this.props.navigation.state.params ? this.props.navigation.state.params.group : null;
let group = this.props.route.params ? this.props.route.params.group : null;
return group? <Text style={styles.title}> {i18n.t('capture.postingIn', {group: group.name})} </Text> :null;
}
......@@ -140,17 +143,19 @@ export default class CapturePoster extends Component {
*/
navToPrevious(entity, group) {
const {state, dispatch, goBack} = this.props.navigation;
const {dispatch, goBack} = this.props.navigation;
const params = {
const { params } = this.props.route;
const routeParams = {
prepend: ActivityModel.checkOrCreate(entity),
};
if (group) params.group = group;
if (group) routeParams.group = group;
dispatch(NavigationActions.setParams({
params,
key: state.params.parentKey, // passed from index
routeParams,
key: params.parentKey, // passed from index
}));
goBack(null);
......@@ -204,7 +209,7 @@ export default class CapturePoster extends Component {
* Render
*/
render() {
const params = this.props.navigation.state.params || {};
const params = this.props.route.params || {};
return params.isRemind ? this.renderRemind() : this.renderNormal();
}
......@@ -215,11 +220,10 @@ export default class CapturePoster extends Component {
renderNormal() {
const navigation = this.props.navigation;
const params = navigation.state.params || {};
const params = this.props.route.params || {};
return (
<View style={CS.flexContainer} testID="capturePosterView">
<TopbarNew title={i18n.t('tabTitleNotifications')}/>
<CaptureGallery
onSelected={this.onAttachedMedia}
header={this.getHeader(true)}
......@@ -238,9 +242,6 @@ export default class CapturePoster extends Component {
*/
renderRemind() {
const text = this.props.capture.text;
const navigation = this.props.navigation;
const params = navigation.state.params || {};
return (
<View style={CS.flexContainer}>
......@@ -261,9 +262,9 @@ export default class CapturePoster extends Component {
* Get remind card
*/
getRemind() {
const { params } = this.props.navigation.state;
const ShowComponent = params.entity.subtype == 'blog' ? BlogCard : Activity;
return <ShowComponent hideTabs={true} entity={params.entity} />
const { params } = this.props.route;
const ShowComponent = params.entity.subtype === 'blog' ? BlogCard : Activity;
return <ShowComponent hideTabs={true} entity={params.entity} />;
}
/**
......@@ -311,7 +312,7 @@ export default class CapturePoster extends Component {
*/
onAttachedMedia = async (response) => {
const attachment = this.props.capture.attachment;
let group = this.props.navigation.state.params ? this.props.navigation.state.params.group : null
let group = this.props.route.params ? this.props.route.params.group : null
let extra = null;
if (group) {
......@@ -342,7 +343,7 @@ export default class CapturePoster extends Component {
* Create a remind
*/
async remind() {
const { params } = this.props.navigation.state;
const { params } = this.props.route;
const message = this.props.capture.text;
const metadata = params.entity.getClientMetadata();
......@@ -351,7 +352,7 @@ export default class CapturePoster extends Component {
...metadata
};
let group = this.props.navigation.state.params ? this.props.navigation.state.params.group : null
let group = this.props.route.params ? this.props.route.params.group : null
if(HashtagService.slice(message).length > HashtagService.maxHashtags){ //if hashtag count greater than 5
Alert.alert(i18n.t('capture.maxHashtags', {maxHashtags: HashtagService.maxHashtags}));
......@@ -417,8 +418,8 @@ export default class CapturePoster extends Component {
newPost = Object.assign(newPost, this.props.capture.embed.meta);
}
if (this.props.navigation.state.params && this.props.navigation.state.params.group) {
newPost.container_guid = this.props.navigation.state.params.group.guid;
if (this.props.route.params && this.props.route.params.group) {
newPost.container_guid = this.props.route.params.group.guid;
}
if (this.props.capture.tags && this.props.capture.tags.length) {
......@@ -446,8 +447,8 @@ export default class CapturePoster extends Component {
if (this.props.onComplete) {
this.props.onComplete(response.entity);
} else if (this.props.navigation.state.params && this.props.navigation.state.params.group) {
this.navToPrevious(response.entity, this.props.navigation.state.params.group);
} else if (this.props.route.params && this.props.route.params.group) {
this.navToPrevious(response.entity, this.props.route.params.group);
} else {
this.navToPrevious(response.entity);
}
......
......@@ -50,19 +50,12 @@ class ChannelScreen extends Component {
guid: null,
};
/**
* Disable navigation bar
*/
static navigationOptions = {
header: null,
};
/**
* Load data on mount
*/
async componentWillMount() {
this.disposeEnter = this.props.navigation.addListener('didFocus', (s) => {
const params = this.props.navigation.state.params;
const params = this.props.route.params;
const store = this.props.channel.store(this.guid);
if (params && params.prepend) {
if (store.channel && store.channel.isOwner && store.channel.isOwner()) {
......@@ -84,7 +77,7 @@ class ChannelScreen extends Component {
* Initial load
*/
async initialLoad() {
const params = this.props.navigation.state.params;
const params = this.props.route.params;
if (params.entity) {
// load channel from endpoint
......@@ -182,7 +175,7 @@ class ChannelScreen extends Component {
}
get guid() {
const params = this.props.navigation.state.params;
const params = this.props.route.params;
let guid = params.entity ? params.entity.guid : params.guid;
......
......@@ -120,6 +120,7 @@ class Comment extends Component {
onInputFocus={this.onInputFocus}
onCommentFocus={this.onCommentFocus}
navigation={this.props.navigation}
route={this.props.route}
/>
}
......
......@@ -54,6 +54,7 @@ type PropsType = {
store: any,
user: any,
navigation: any,
route: any,
onInputFocus?: Function,
onCommentFocus?: Function
};
......@@ -280,7 +281,7 @@ class CommentList extends React.Component<PropsType, StateType> {
*/
loadComments = async (loadingMore: boolean = false, descending: boolean = true): Promise<void> => {
let guid;
const scrollToBottom = this.props.navigation.state.params.scrollToBottom;
const scrollToBottom = this.props.route.params.scrollToBottom;
if (this.props.entity) {
guid = this.props.entity.guid;
......@@ -453,6 +454,7 @@ class CommentList extends React.Component<PropsType, StateType> {
onTextInputfocus={this.onChildFocus}
onCommentFocus={this.onCommentFocus}
navigation={this.props.navigation}
route={this.props.route}
commentFocusCall={this.commentFocusCall}
index={row.index}
/>
......
......@@ -94,7 +94,7 @@ class DiscoveryScreen extends Component {
constructor(props) {
super(props);
this.props.discovery.init();
// this.props.discovery.init();
const params = this.props.route.params;
if (params && params.type) {
......
......@@ -85,7 +85,7 @@ export default class GroupViewScreen extends Component {
* Load initial data
*/
async initialLoad() {
const params = this.props.navigation.state.params;
const params = this.props.route.params;
if (params.group) {
// load group and update async
......@@ -113,13 +113,13 @@ export default class GroupViewScreen extends Component {
}
componentDidMount() {
const params = this.props.navigation.state.params;
const params = this.props.route.params;
// load data async
this.initialLoad();
this.disposeEnter = this.props.navigation.addListener('didFocus', (s) => {
const params = this.props.navigation.state.params;
const params = this.props.route.params;
if (params && params.prepend) {
this.props.groupView.prepend(params.prepend);
// we clear the parameter to prevent prepend it again on goBack
......@@ -214,6 +214,7 @@ export default class GroupViewScreen extends Component {
entity={group.group}
store={this.comments}
navigation={this.props.navigation}
route={this.props.route}
keyboardVerticalOffset = {Header.HEIGHT - 65}
/>
);
......
......@@ -39,7 +39,7 @@ export default class GroupsBarItem extends Component {
}
navToGroup = () => {
navigationService.push('GroupView', {group: this.props.group});
navigationService.navigate('GroupView', {group: this.props.group});
}
render() {
......
......@@ -3,11 +3,10 @@ import { NavigationActions, StackActions, SwitchActions } from '@react-navigatio
let _navigator = null;
function getStateFrom(nav) {
let state = nav.routes[nav.index];
if (state.routes) {
state = getStateFrom(state);
if (nav.routes && nav.routes[nav.index].state) {
return getStateFrom(nav.routes[nav.index].state);
}
return state;
return nav.routes[nav.index];
}
export function setTopLevelNavigator(navigatorRef) {
......@@ -19,19 +18,17 @@ function getState() {
}
function getCurrentState() {
return getStateFrom(_navigator.state.nav);
const root = _navigator.getRootState();
return getStateFrom(root);
}
function navigate(routeName, params) {
_navigator.navigate(routeName, params);
function navigate(...args) {
_navigator.navigate(...args);
}
function push(routeName, params) {
function push(...args) {
_navigator.dispatch(
StackActions.push({
routeName,
params,
})
StackActions.push(...args)
);
}
......
import React from 'react';
import React, { Fragment } from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import LoadingScreen from '../LoadingScreen';
......@@ -54,6 +54,8 @@ import Gathering from '../gathering/Gathering';
import OnboardingScreenNew from '../onboarding/OnboardingScreenNew';
import EmailConfirmationScreen from '../onboarding/EmailConfirmationScreen';
import featuresService from '../common/services/features.service';
import ThemedStyles from '../styles/ThemedStyles';
import { View } from 'react-native';
const hideHeader = {headerShown: false};
......@@ -64,9 +66,8 @@ const AppStack = function(props) {
// const tabScreen = featuresService.has('navigation-2020')
// ? withErrorBoundaryScreen(TabsScreenNew)
// : withErrorBoundaryScreen(TabsScreen);
return (
<Stack.Navigator>
<Stack.Navigator screenOptions={ThemedStyles.defaultScreenOptions}>
<Stack.Screen name="Tabs" component={TabsScreenNew} options={hideHeader} />
<Stack.Screen name="EmailConfirmation" component={EmailConfirmationScreen}/>
<Stack.Screen name="Update" component={UpdatingScreen}/>
......@@ -74,8 +75,8 @@ const AppStack = function(props) {
<Stack.Screen name="DeleteChannel" component={DeleteChannelScreen}/>
<Stack.Screen name="Notifications" component={NotificationsScreen}/>
<Stack.Screen name="NotificationsSettings" component={NotificationsSettingsScreen}/>
<Stack.Screen name="Channel" component={ChannelScreen}/>
<Stack.Screen name="Capture" component={CapturePoster}/>
<Stack.Screen name="Channel" component={ChannelScreen} options={hideHeader}/>
<Stack.Screen name="Capture" component={CapturePoster} />
<Stack.Screen name="Activity" component={ActivityScreen}/>
<Stack.Screen name="Conversation" component={ConversationScreen}/>
<Stack.Screen name="DiscoveryFeed" component={DiscoveryFeedScreen}/>
......@@ -87,7 +88,7 @@ const AppStack = function(props) {
<Stack.Screen name="SettingsRekey" component={RekeyScreen}/>
<Stack.Screen name="SettingsBilling" component={BillingScreen}/>
<Stack.Screen name="GroupsList" component={GroupsListScreen}/>
<Stack.Screen name="GroupView" component={GroupViewScreen}/>
<Stack.Screen name="GroupView" component={GroupViewScreen} options={hideHeader}/>
<Stack.Screen name="Wallet" component={WalletScreen}/>
<Stack.Screen name="BlogList" component={BlogsListScreen}/>
<Stack.Screen name="BoostConsole" component={BoostConsoleScreen}/>
......@@ -125,9 +126,14 @@ const AuthStack = function(props) {
const RootStack = function(props) {
return (
<Stack.Navigator mode="modal" headerMode="none">
<Stack.Screen name="Auth" component={AuthStack} />
<Stack.Screen name="App" component={AppStack} />
<Stack.Screen name="Gathering" component={Gathering} />
{props.isLoggedIn ? (
<Fragment>
<Stack.Screen name="App" component={AppStack} />
<Stack.Screen name="Gathering" component={Gathering} />
</Fragment>
) : (
<Stack.Screen name="Auth" component={AuthStack} />
)}
</Stack.Navigator>
);
};
......
......@@ -46,8 +46,11 @@ class ActivityScreen extends Component {
this.loadEntity();
}
/**
* Load entity
*/
async loadEntity() {
const params = this.props.navigation.state.params;
const params = this.props.route.params;
if (params.entity && (params.entity.guid || params.entity.entity_guid)) {
......@@ -141,6 +144,7 @@ class ActivityScreen extends Component {
store={this.comments}
navigation={this.props.navigation}
onInputFocus={this.onFocus}
route={this.props.route}
/>
:
<View style={CS.flexColumnCentered}>
......
......@@ -63,6 +63,7 @@ export default class NewsfeedScreen extends Component {
* Load data on mount
*/
componentDidMount() {
this.loadFeed();
// this.props.newsfeed.loadBoosts();
......@@ -84,6 +85,7 @@ export default class NewsfeedScreen extends Component {
// load groups after the feed
await this.groupsBar.wrappedInstance.initialLoad();
// load discovery after the feed is loaded
this.props.discovery.init();
this.props.discovery.fetch();
}
......
import React, {
PureComponent
useCallback
} from 'react';
import {
......@@ -12,6 +12,7 @@ import Counter from './Counter';
import withPreventDoubleTap from '../../../common/components/PreventDoubleTap';
import testID from '../../../common/helpers/testID';
import { FLAG_REMIND } from '../../../common/Permissions';
import { useRoute, useNavigation } from '@react-navigation/native';
// prevent double tap in touchable
const TouchableOpacityCustom = withPreventDoubleTap(TouchableOpacity);
......@@ -19,44 +20,38 @@ const TouchableOpacityCustom = withPreventDoubleTap(TouchableOpacity);
/**
* Remind Action Component
*/
export default class RemindAction extends PureComponent {
export default function({ entity, size = 20, vertical = false }) {
const color = entity.can(FLAG_REMIND)
? entity.reminds > 0
? CS.colorPrimary
: CS.colorAction
: CS.colorLightGreyed;
static defaultProps = {
size: 20,
};
/**
* Render
*/
render() {
const canRemind = this.props.entity.can(FLAG_REMIND);
const color = canRemind ? (this.props.entity['reminds'] > 0 ? CS.colorPrimary : CS.colorAction) : CS.colorLightGreyed;
return (
<TouchableOpacityCustom
style={[CS.flexContainer, CS.centered, this.props.vertical === true ? CS.columnAlignCenter : CS.rowJustifyCenter]}
onPress={this.remind}
{...testID('Remind activity button')}
>
<Icon style={[color, CS.marginRight]} name='repeat' size={this.props.size} />
<Counter count={this.props.entity['reminds']} size={this.props.size * 0.70} />
</TouchableOpacityCustom>
)
}
const route = useRoute();
const navigation = useNavigation();
/**
* Open remind
*/
remind = () => {
const remind = useCallback(() => {
// check permission and show alert
if (!this.props.entity.can(FLAG_REMIND, true)) return;
const { state } = this.props.navigation
this.props.navigation.push('Capture', {isRemind: true, entity: this.props.entity, parentKey: state.key});
}
if (!entity.can(FLAG_REMIND, true)) return;
const { key } = route;
navigation.push('Capture', {isRemind: true, entity, parentKey: key});
}, [route, entity, navigation]);
return (
<TouchableOpacityCustom
style={[
CS.flexContainer,
CS.centered,
vertical === true ? CS.columnAlignCenter : CS.rowJustifyCenter,
]}
onPress={remind}
{...testID('Remind activity button')}>
<Icon style={[color, CS.marginRight]} name='repeat' size={size} />
<Counter count={entity['reminds']} size={size * 0.70} />
</TouchableOpacityCustom>
);
}
......@@ -40,10 +40,10 @@ export default
@observer
class SettingsScreen extends Component {
static navigationOptions = {
title: 'Settings',
leftHandedInitial: false,
};
// static navigationOptions = {
// title: 'Settings',
// leftHandedInitial: false,
// };
state = {
showLanguages: false,
......@@ -158,8 +158,6 @@ class SettingsScreen extends Component {
icon: (<Icon name='power-settings-new' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]} />),
onPress: () => {
authService.logout();
this.props.navigation.navigate( 'Login');
}
},
{
......
......@@ -55,7 +55,6 @@ class SettingsService {
try {
await api.delete('api/v1/channel');
await sessionService.logout();
navigationService.navigate('Login');
} catch (e) {
Alert.alert('Error disabling the channel');
}
......@@ -69,7 +68,6 @@ class SettingsService {
try {
await api.post('api/v2/settings/delete', { password });
await sessionService.logout();
navigationService.navigate('Login');
} catch (e) {
Alert.alert('Error deleting the channel');
}
......
......@@ -2,7 +2,7 @@ import { StyleSheet } from 'react-native';
import { observable, action, reaction } from 'mobx';
import { DARK_THEME, LIGHT_THEME } from './Colors';
import { DefaultTheme } from '@react-navigation/native';
import { DefaultTheme, DarkTheme } from '@react-navigation/native';
const repetitions = 8;
const step = 5;
......@@ -43,6 +43,7 @@ class ThemedStylesStore {
@observable theme = -1;
navTheme = null;
defaultScreenOptions = null;
/**
* Style
......@@ -111,10 +112,24 @@ class ThemedStylesStore {
const theme = this.theme ? DARK_THEME : LIGHT_THEME;
// this.navTheme = {
// ...DefaultTheme,
// dark: this.theme === 1,
// };
const baseTheme = this.theme === 0 ? DefaultTheme : DarkTheme;
this.navTheme = {
...baseTheme,
colors: {
...baseTheme.colors,
background: 'transparent',
// card: theme.backgroundSecondary, // generates an error on ios
text: theme.primary_text,
primary: theme.icon
},
};
this.defaultScreenOptions = {
headerStyle: {
backgroundColor: theme.secondary_background,
},
};
this.style = StyleSheet.create({
...dynamicStyles,
......
......@@ -99,7 +99,6 @@ export default class MoreScreen extends Component {
icon: (<Icon name='power-settings-new' size={ICON_SIZE} style={ styles.icon } />),
onPress: () => {
authService.logout();
this.props.navigation.navigate('Login');
}
}
];
......
......@@ -118,7 +118,6 @@ class MoreScreenNew extends Component {
icon: (<Icon name='power-settings-new' size={ICON_SIZE} style={ CS.colorIcon } />),
onPress: () => {
authService.logout();
this.props.navigation.navigate('Login');
}
}
];
......@@ -126,23 +125,36 @@ class MoreScreenNew extends Component {
return list;
}
/**
* Recieve a list a return a view container with a list of items
* @param {Array} list
*/
renderList = list => {
render() {
const avatar = this.getAvatar(),
channel = this.props.user.me;
const CS = ThemedStyles.style;
return (
<SafeAreaView style={[
CS.flexContainer,
CS.backgroundPrimary,
]}>
<ScrollView
style={[
styles.container,
CS.flexContainer,
CS.backgroundPrimary,
CS.marginTop4x,
CS.marginTop11x,
]}
>
<View style={styles.headerContainer} >
<TouchableOpacity onPress={this.navToChannel}>
<Image source={avatar} style={styles.wrappedAvatar}/>
</TouchableOpacity>
<Text style={[CS.titleText, CS.colorPrimaryText, CS.marginTop2x]}>{channel.name}</Text>
<Text style={[CS.subTitleText, CS.colorSecondaryText, CS.fontNormal]}>@{channel.username}</Text>
<Text style={[CS.subTitleText, CS.colorSecondaryText, CS.fontNormal, CS.marginTop3x]}>
{`${abbrev(channel.subscribers_count, 0)} ${i18n.t('subscribers')} · ${abbrev(channel.subscriptions_count, 0)} ${i18n.t('subscriptions')}`}
</Text>
</View>
{
list.map((l, i) => (
this.getOptionsList().map((l, i) => (
<ListItem
key={i}
title={l.name}
......@@ -158,35 +170,7 @@ class MoreScreenNew extends Component {
))
}
</ScrollView>
)
}
render() {
const avatar = this.getAvatar(),
channel = this.props.user.me;
const CS = ThemedStyles.style;
return (
<SafeAreaView style={[
CS.flexContainer,
CS.backgroundPrimary,
]}>
{/* CHANNEL DATA */}
<View style={styles.headerContainer} >
<TouchableOpacity onPress={this.navToChannel}>
<Image source={avatar} style={styles.wrappedAvatar}/>
</TouchableOpacity>
<Text style={[CS.titleText, CS.colorPrimaryText, CS.marginTop2x]}>{channel.name}</Text>
<Text style={[CS.subTitleText, CS.colorSecondaryText, CS.fontNormal]}>@{channel.username}</Text>
<Text style={[CS.subTitleText, CS.colorSecondaryText, CS.fontNormal, CS.marginTop3x]}>
{`${abbrev(channel.subscribers_count, 0)} ${i18n.t('subscribers')} · ${abbrev(channel.subscriptions_count, 0)} ${i18n.t('subscriptions')}`}
</Text>
</View>
{/* MENU */}
{this.renderList(this.getOptionsList())}
</SafeAreaView>
);
}
......
import React from 'react';
import React, { useCallback } from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
import { View } from 'react-native';
import { View, Platform, TouchableOpacity } from 'react-native';
import NewsfeedScreen from '../newsfeed/NewsfeedScreen';
import NotificationsScreen from '../notifications/NotificationsScreen';
......@@ -21,7 +21,13 @@ import { Avatar } from 'react-native-elements';
* Main tabs
* @param {Object} props
*/
const Tabs = function(props) {
const Tabs = function({navigation}) {
const isIOS = Platform.OS === 'ios';
const navToCapture = useCallback(() => navigation.push('Capture'), [
navigation,
]);
return (
<Tab.Navigator
initialRouteName="Newsfeed"
......@@ -33,33 +39,41 @@ const Tabs = function(props) {
style: {
borderTopWidth: 0,
backgroundColor: ThemedStyles.getColor('secondary_background'),
height: 100,
paddingTop: 10
height: isIOS ? 90 : 65,
paddingTop: isIOS ? 20 : 2,
},
tabStyle: {
height: 66,
width: 66,
...ThemedStyles.style.centered,
}
},
}}
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName, iconsize = 28;
let iconName,
iconsize = 28;
switch (route.name) {
case 'Menu':
return (
<View>
{focused && <View style={[styles.acitivity]}/>}
{focused && <View style={[styles.acitivity]} />}
<Avatar
rounded
source={{ uri: MINDS_CDN_URI + 'icon/' + AppStores.user.me.guid + '/medium/' + AppStores.user.me.icontime}}
source={{
uri:
MINDS_CDN_URI +
'icon/' +
AppStores.user.me.guid +
'/medium/' +
AppStores.user.me.icontime,
}}
width={34}
height={34}
testID="AvatarButton"
/>
</View>
)
);
break;
case 'Newsfeed':
iconName = 'home';
......@@ -77,18 +91,42 @@ const Tabs = function(props) {
}
// You can return any component that you like here!
return <TabIcon name={iconName} size={iconsize} color={color} />
return <TabIcon name={iconName} size={iconsize} color={color} />;
},
})}
>
<Tab.Screen name="Newsfeed" component={NewsfeedScreen} navigationOptions={{tabBarTestID:'Menu tab button'}} options={{headerShown: false}} />
<Tab.Screen name="Discovery" component={DiscoveryScreen} navigationOptions={{tabBarTestID:'Discovery tab button'}} />
<Tab.Screen name="Capture" component={View} navigationOptions={{tabBarTestID:'Capture tab button'}} />
<Tab.Screen name="Notifications" component={NotificationsScreen} navigationOptions={{tabBarTestID:'Notifications tab button'}} />
<Tab.Screen name="Menu" component={MoreScreenNew} navigationOptions={{tabBarTestID:'Menu tab button'}} />
})}>
<Tab.Screen
name="Newsfeed"
component={NewsfeedScreen}
options={{ tabBarTestID: 'Menu tab button', headerShown: false }}
/>
<Tab.Screen
name="Discovery"
component={DiscoveryScreen}
options={{ tabBarTestID: 'Discovery tab button' }}
/>
<Tab.Screen
name="Capture"
component={View}
options={{
tabBarTestID: 'Capture tab button',
tabBarButton: props => (
<TouchableOpacity {...props} onPress={navToCapture} />
),
}}
/>
<Tab.Screen
name="Notifications"
component={NotificationsScreen}
options={{ tabBarTestID: 'Notifications tab button' }}
/>
<Tab.Screen
name="Menu"
component={MoreScreenNew}
options={{ tabBarTestID: 'Menu tab button' }}
/>
</Tab.Navigator>
);
}
};
const styles = {
acitivity: {
......@@ -100,10 +138,8 @@ const styles = {
borderWidth: 2.5,
borderRadius: 35,
position: 'absolute',
borderColor: colors.primary
}
}
borderColor: colors.primary,
},
};
export default Tabs;
......@@ -66,22 +66,22 @@ export default class TopbarNew extends Component {
}
}
let topbarHeight = 100;
let topbarHeight = 50;
let topMargin = 0;
if (Platform.OS == 'ios') {
topbarHeight = 100;
topbarHeight = 80;
}
const styles = StyleSheet.create({
lineHeight0: {
lineHeight:0,
lineHeight: 28,
},
container: {
height: topbarHeight,
display: 'flex',
flexDirection: 'row',
paddingBottom: 5,
paddingBottom: 8,
},
topbar: {
flex: 1,
......
......@@ -69,7 +69,7 @@ export default class FabScreen extends Component {
}
async loadUserAndSetDefaults() {
const params = this.props.navigation.state.params;
const params = this.props.route.params;
// if there is no default data we reset the store
if (!params || !params.default) {
......
......@@ -1051,18 +1051,18 @@
resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-4.6.0.tgz#fc0b79a226da78371158f885a2f798ff07c926be"
integrity sha512-wz39BUpExDU1kTpLlBkDwwb0Efg+uuwixToosTSarZgpzG/CmcRvWdD786TMiE5tLDd+Mpi2xh3w4FrVM8zjoA==
"@react-navigation/bottom-tabs@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-5.0.1.tgz#7150cf52a63a561736fa36304385f9b858af0f41"
integrity sha512-CuTz4v0dYBKFZ1jZCXbtVlp+8sEWEIm/H+DzRlrcEqXJEBUKTPgatZrR7EGxLb+7+DqfkEATbpJntTTHs3ccMg==
"@react-navigation/bottom-tabs@^5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-5.0.3.tgz#0375683e261279912b095734f1eed10027b2ff37"
integrity sha512-/rGlulcpvoJeAE5j/9HWTQzmu2UxxT5eoTh+kGnTAYjPwPordLgwP36NeX2Jvhv5tIvTtlzhF7bCDquBBgiN6g==
dependencies:
color "^3.1.2"
react-native-iphone-x-helper "^1.2.1"
"@react-navigation/core@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-5.1.0.tgz#fba305f271aebcd4d7bdf8b094ede23204d76c45"
integrity sha512-H+wRpLanYimkw9PQkEwEL3/u8xJbbWqX35hUZ7KL2uOJyjo4oNM66uzNKPH9I4jErgH28oRdsAD0+ZnmdRbnRA==
"@react-navigation/core@^5.1.2":
version "5.1.2"
resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-5.1.2.tgz#1947449cb2f565cde8511ab953bb7170516c3e0f"
integrity sha512-0nQ46DTMzj5UjqUcc6g996qMQ2vSRSNgxd9LPlACVYHQRbwG2HHUq//NhfVf06dNpBHkQ0HYUITDHo+tifemjA==
dependencies:
"@react-navigation/routers" "^5.0.1"
escape-string-regexp "^2.0.0"
......@@ -1071,12 +1071,12 @@
shortid "^2.2.15"
use-subscription "^1.3.0"
"@react-navigation/native@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-5.0.1.tgz#980f9d4897e48d43678488707564269096339acd"
integrity sha512-2VX0RNlm3OzDxXDMhY61J0yUM7/w7DnAXe3o3M0EB2DHCshWANy+1X6VgyGHyp8O/3ZqCF+R1fyHbpw6RVdIpQ==
"@react-navigation/native@^5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-5.0.3.tgz#6a16f50c41f62e27dafcff20848222c018a6b033"
integrity sha512-caJVWRkpTY6zFkpZvQE1Dg0gTu+a09XnleKaDEV9TOK8AxkBhp+X4GvCP/7sV9Ke8Cw8mvcnh9R1UCKIO4fTCQ==
dependencies:
"@react-navigation/core" "^5.1.0"
"@react-navigation/core" "^5.1.2"
"@react-navigation/routers@^5.0.1":
version "5.0.1"
......@@ -1085,10 +1085,10 @@
dependencies:
shortid "^2.2.15"
"@react-navigation/stack@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-5.0.1.tgz#f06a927c145af8d2ea661fab6bfeab39b30b4f48"
integrity sha512-9PMY6/HXzpqq2PqCUOWsFMXeJ8T72pMl1mK4vosuaa/8amIfBMhF7Cq+6q95CFphMWlQI0RqDtd+WGEqFj/trA==
"@react-navigation/stack@^5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-5.0.3.tgz#e3d0f27b83a6da50da98846de4cd180647597546"
integrity sha512-CAFfURsfY6/zmIQsUunDoiuCrvZLNqUwN5D1usN8I8VFkJg1kKCZbIxaVftAbZiPcfwORarsuWMxqkCVmVvEuA==
dependencies:
color "^3.1.2"
react-native-iphone-x-helper "^1.2.1"
......
Please register or to comment