...
 
Commits (5)
......@@ -27,11 +27,13 @@ import groupsBar from './src/groups/GroupsBarStore';
import sessionService from './src/common/services/session.service';
import logService from './src/common/services/log.service';
import SubscriptionRequestStore from './src/channel/subscription/SubscriptionRequestStore';
/**
* App stores
*/
const stores = {
subscriptionRequest: new SubscriptionRequestStore(),
newsfeed: new newsfeed(),
notifications: new notifications(),
notificationsSettings: new notificationsSettings(),
......
......@@ -211,6 +211,7 @@
"video":"Video"
},
"channel":{
"isClosed":"This is a closed channel",
"channel":"channel",
"errorSaving":"Error saving channel",
"saveChanges":"Save your changes",
......@@ -220,6 +221,7 @@
"mature":"This channel contains mature content",
"confirmUnsubscribe":"Are you sure you want to unsubscribe from this channel?",
"subscribe":"Subscribe",
"requestSubscription":"Request Subscription",
"subscribed":"Subscribed",
"subscribeMessage":"Subscribe to this channel",
"unsubscribeMessage":"Unsubscribe to this channel",
......@@ -234,7 +236,9 @@
"blocked":"You have blocked @{{username}}",
"tapUnblock":"Tap to unblock",
"notFound":"Channel not found",
"viewScheduled":"Scheduled"
"viewScheduled":"Scheduled",
"requestAccepted":"Accepted",
"requestRejected":"Rejected"
},
"discovery":{
"search":"Search...",
......@@ -774,5 +778,6 @@
"cantReachServer":"Can't reach the server",
"showingStored":"Showing stored data",
"actions":"Actions",
"requests":"Requests",
"notAllowed":"You are not allowed"
}
......@@ -49,7 +49,7 @@ class ChannelActions extends Component {
getOptions() {
let options = [ i18n.t('cancel') ];
if (this.props.store.channel.subscribed){
if (this.props.store.channel.isSubscribed()){
options.push( i18n.t('channel.unsubscribe') );
}
......@@ -73,10 +73,10 @@ class ChannelActions extends Component {
this.props.store.channel.toggleSubscription();
break;
case i18n.t('channel.block'):
this.props.store.toggleBlock();
this.props.store.channel.toggleBlock();
break;
case i18n.t('channel.unblock'):
this.props.store.toggleBlock();
this.props.store.channel.toggleBlock();
break;
case i18n.t('channel.report'):
this.props.navigation.push('Report', { entity: this.props.store.channel });
......@@ -120,6 +120,8 @@ class ChannelActions extends Component {
return featuresService.has('post-scheduler') && !this.state.edit;
}
setSheetRef = o => this.ActionSheet = o;
/**
* Render Header
*/
......@@ -129,8 +131,8 @@ class ChannelActions extends Component {
const isOwner = channel.isOwner();
const showWire = !channel.blocked && !isOwner && featuresService.has('crypto') && channel.can(FLAG_WIRE);
const showScheduled = featuresService.has('post-scheduler') && !this.state.edit && isOwner;
const showSubscribe = !isOwner && !channel.subscribed && channel.can(FLAG_SUBSCRIBE);
const showMessage = !isOwner && channel.subscribed && channel.can(FLAG_MESSAGE);
const showSubscribe = !isOwner && !channel.isSubscribed() && channel.can(FLAG_SUBSCRIBE);
const showMessage = !isOwner && channel.isSubscribed() && channel.can(FLAG_MESSAGE);
const showEdit = isOwner && channel.can(FLAG_EDIT_CHANNEL);
if (this.props.store.isUploading) {
......@@ -198,7 +200,7 @@ class ChannelActions extends Component {
/>
}
<ActionSheet
ref={o => this.ActionSheet = o}
ref={this.setSheetRef}
title={i18n.t('actions')}
options={this.getOptions()}
onPress={this.handleSelection}
......
import {
observable,
action,
computed,
extendObservable
} from 'mobx'
import {
getFeedChannel,
toggleComments,
toggleExplicit,
setViewed
} from '../newsfeed/NewsfeedService';
import api from '../common/services/api.service';
import channelService from './ChannelService';
import OffsetFeedListStore from '../common/stores/OffsetFeedListStore';
import ActivityModel from '../newsfeed/ActivityModel';
import BlogModel from '../blogs/BlogModel';
import logService from '../common/services/log.service';
import featuresService from '../common/services/features.service';
import FeedStore from '../common/stores/FeedStore';
import { isNetworkFail } from '../common/helpers/abortableFetch';
/**
* Channel Feed store
......@@ -124,8 +108,8 @@ export default class ChannelFeedStore {
@action
async refresh() {
//ignore refresh on rewards view
if (this.filter == 'rewards') {
// ignore refresh on rewards or requests view
if (this.filter == 'rewards' || this.filter == 'request') {
return;
}
......@@ -142,6 +126,8 @@ export default class ChannelFeedStore {
setFilter(filter) {
this.filter = filter;
if (filter == 'requests' || filter == 'rewards') return;
this.feedStore.setEndpoint(`api/v2/${this.endpoint}/${this.guid}/${this.esFeedfilter}`)
.setIsTiled(filter === 'images' || filter === 'videos')
.clear()
......
......@@ -4,7 +4,6 @@ import React, {
import {
StyleSheet,
FlatList,
Text,
View,
Alert,
......@@ -18,7 +17,6 @@ import {
import { Icon } from 'react-native-elements'
import RewardsCarousel from './carousel/RewardsCarousel';
import ChannelHeader from './header/ChannelHeader';
import Toolbar from './toolbar/Toolbar';
import CenteredLoading from '../common/components/CenteredLoading';
......@@ -35,12 +33,14 @@ import { GOOGLE_PLAY_STORE } from '../config/Config';
import i18n from '../common/services/i18n.service';
import FeedList from '../common/components/FeedList';
import { FLAG_VIEW } from '../common/Permissions';
import SubscriptionButton from './subscription/SubscriptionButton';
import SubscriptionRequestList from './subscription/SubscriptionRequestList';
/**
* Channel Screen
*/
export default
@inject('channel')
@inject('channel', 'subscriptionRequest')
@observer
class ChannelScreen extends Component {
......@@ -135,7 +135,7 @@ class ChannelScreen extends Component {
*/
checkCanView(channel) {
// if the channel obj doesn't have the permissions loaded return true
if (!channel.permissions.permissions) {
if (channel.isClosed() || !channel.permissions.permissions) {
return true
}
......@@ -185,6 +185,62 @@ class ChannelScreen extends Component {
this.props.navigation.navigate('Capture');
}
getHeader(store) {
const feed = store.feedStore;
const channel = store.channel;
const rewards = store.rewards;
const showClosed = channel.isClosed() && !channel.subscribed;
return (
<View>
<ChannelHeader
styles={styles}
store={store}
navigation={this.props.navigation}
/>
{!channel.blocked && !showClosed &&
<Toolbar
feed={feed}
subscriptionRequest={this.props.subscriptionRequest}
channel={channel}
hasRewards={rewards.merged && rewards.merged.length}
/>
}
{!!channel.blocked &&
<View style={styles.blockView}>
<Text style={styles.blockText}>{i18n.t('channel.blocked',{username: channel.username})}</Text>
<Touchable onPress={this.toggleBlock}>
<Text style={styles.blockTextLink}>{i18n.t('channel.tapUnblock')}</Text>
</Touchable>
</View>
}
{!!showClosed && !channel.blocked &&
<View style={styles.blockView}>
<Text style={styles.blockText}>{i18n.t('channel.isClosed')}</Text>
<SubscriptionButton channel={channel} />
</View>
}
</View>
);
}
/**
* Toggle block channel
*/
toggleBlock = () => {
this.props.channel.store(this.guid).channel.toggleBlock();
}
/**
* Nav to prev screen
*/
goBack = () => {
this.props.navigation.goBack();
}
/**
* Render
*/
......@@ -210,18 +266,9 @@ class ChannelScreen extends Component {
const rewards = store.rewards;
const guid = this.guid;
const isOwner = guid == session.guid;
const isClosed = channel.isClosed() && !channel.subscribed;
let emptyMessage = null;
let carousel = null;
// carousel only visible if we have data
/*if (rewards.merged && rewards.merged.length && channelfeed.showrewards) {
carousel = (
<View style={styles.carouselcontainer}>
<RewardsCarousel rewards={rewards.merged} />
</View>
);
}*/
if (channel.is_mature && !channel.mature_visibility) {
return (
......@@ -245,31 +292,9 @@ class ChannelScreen extends Component {
}
// channel header
const header = (
<View>
<ChannelHeader
styles={styles}
store={store}
navigation={this.props.navigation}
/>
const header = this.getHeader(store);
{!channel.blocked && <Toolbar feed={feed} hasRewards={rewards.merged && rewards.merged.length}/>}
{carousel}
<SafeAreaView style={styles.gobackicon}>
<Icon raised color={colors.primary} size={22} name='arrow-back' onPress={() => this.props.navigation.goBack()}/>
</SafeAreaView>
{!!channel.blocked && <View style={styles.blockView}>
<Text style={styles.blockText}>{i18n.t('channel.blocked',{username: channel.username})}</Text>
<Touchable onPress={() => this.props.channel.store(this.guid).toggleBlock()}>
<Text style={styles.blockTextLink}>{i18n.t('channel.tapUnblock')}</Text>
</Touchable>
</View>}
</View>
);
let renderActivity = null
let renderActivity = null, body = null;
// is a blog? use blog card to render
if(feed.filter == 'blogs') {
......@@ -290,29 +315,25 @@ class ChannelScreen extends Component {
);
}
const emptyRender = () => <View />;
body = feed.filter != 'requests' ?
<FeedList
feedStore={feed.feedStore}
renderActivity={renderActivity}
header={header}
navigation={this.props.navigation}
emptyMessage={emptyMessage}
/> :
<SubscriptionRequestList
ListHeaderComponent={header}
style={[CommonStyle.flexContainer]}
/>
return (
<View style={CommonStyle.flexContainer}>
{!channel.blocked &&
<FeedList
feedStore={feed.feedStore}
renderActivity={renderActivity}
header={header}
navigation={this.props.navigation}
emptyMessage={emptyMessage}
/>}
{/* Not using FlatList breaks header layout */}
{channel.blocked && <FlatList
style={{ flex: 1, backgroundColor: '#fff' }}
ListHeaderComponent={header}
data={[]}
renderItem={emptyRender}
/>}
{ (!channel.blocked && !isClosed) ? body : header }
<SafeAreaView style={styles.gobackicon}>
<Icon raised color={colors.primary} size={22} name='arrow-back' onPress={this.goBack}/>
</SafeAreaView>
<CaptureFab navigation={this.props.navigation} />
</View>
);
......
......@@ -72,19 +72,6 @@ export default class ChannelStore {
return false;
}
@action
toggleBlock() {
let value = !this.channel.blocked;
return channelService.toggleBlock(this.channel.guid, value)
.then(response => {
this.channel.blocked = value;
})
.catch(err => {
this.channel.blocked = !value;
logService.exception('[ChannelStore] toggleBlock', err);
});
}
@action
loadrewards(guid) {
wireService.rewards(guid)
......
......@@ -4,7 +4,12 @@ import api from '../common/services/api.service';
import BaseModel from '../common/BaseModel';
import ChannelService from './ChannelService';
import sessionService from '../common/services/session.service';
import apiService from '../common/services/api.service';
import logService from '../common/services/log.service';
export const USER_MODE_OPEN = 0;
export const USER_MODE_MODERATED = 1;
export const USER_MODE_CLOSED = 2;
/**
* User model
......@@ -12,29 +17,35 @@ import sessionService from '../common/services/session.service';
export default class UserModel extends BaseModel {
/**
* @var boolean
* @var {boolean}
*/
@observable blocked;
/**
* @var integer
* @var {number}
*/
@observable subscribers_count;
/**
* @var integer
* @var {number}
*/
@observable impressions;
/**
* @var boolean
* @var {boolean}
*/
@observable subscribed;
/**
* @var boolean
* @var {boolean}
*/
@observable mature_visibility = false;
/**
* @var {boolean}
*/
@observable pending_subscribe = false;
getOwnerIcontime() {
if (sessionService.getUser().guid === this.guid) {
return sessionService.getUser().icontime;
......@@ -64,6 +75,19 @@ export default class UserModel extends BaseModel {
}
}
@action
async toggleBlock(value = null) {
value = (value === null) ? !this.blocked : value;
try {
await ChannelService.toggleBlock(this.guid, value);
this.blocked = value;
} catch (err) {
this.blocked = !value;
logService.exception('[ChannelStore] toggleBlock', err);
}
}
/**
* Is admin
*/
......@@ -83,7 +107,6 @@ export default class UserModel extends BaseModel {
* @param {string} size
*/
getBannerSource(size='medium') {
if (this.carousels) {
return {
uri: this.carousels[0].src
......@@ -102,8 +125,69 @@ export default class UserModel extends BaseModel {
/**
* Has banner
* @returns {boolean}
*/
hasBanner() {
return !!this.carousels;
}
/**
* Is closed
* @returns {boolean}
*/
isClosed() {
return this.mode === USER_MODE_CLOSED;
}
/**
* Is open
* @returns {boolean}
*/
isOpen() {
return this.mode === USER_MODE_OPEN;
}
/**
* Is moderated
* @returns {boolean}
*/
isModerated() {
return this.mode === USER_MODE_MODERATED;
}
/**
* Is subscribed
* @returns {boolean}
*/
isSubscribed() {
return !!this.subscribed;
}
/**
* Request subscribe
*/
async subscribeRequest() {
if (this.pending_subscribe || this.mode !== USER_MODE_CLOSED) return;
try {
this.pending_subscribe = true;
await apiService.put(`api/v2/subscriptions/outgoing/${this.guid}`);
} catch (err) {
this.pending_subscribe = false;
logService.exception(err);
}
}
/**
* Cancel subscribe request
*/
async cancelSubscribeRequest() {
if (!this.pending_subscribe || this.mode !== USER_MODE_CLOSED) return;
try {
this.pending_subscribe = false;
await apiService.delete(`api/v2/subscriptions/outgoing/${this.guid}`);
} catch (err) {
this.pending_subscribe = true;
logService.exception(err);
}
}
}
\ No newline at end of file
// @flow
import React, {
Component
} from 'react';
import { Alert } from 'react-native';
import type { Node } from 'react';
import { observer } from 'mobx-react';
import type UserModel from '../UserModel';
import Button from '../../common/components/Button';
import i18n from '../../common/services/i18n.service';
import Icon from 'react-native-vector-icons/Ionicons';
import { CommonStyle } from '../../styles/Common';
type PropsType = {
channel: UserModel
};
/**
* Subscription request
*/
export default
@observer
class SubscriptionButton extends Component<PropsType> {
/**
* On press
*/
onPress = () => {
const { channel } = this.props;
if (channel.isOpen() || channel.subscribed) {
if (channel.subscribed) {
Alert.alert(
i18n.t('attention'),
i18n.t('channel.confirmUnsubscribe'),
[{ text: i18n.t('yesImSure'), onPress: () => channel.toggleSubscription() }, { text: i18n.t('no')}]
);
} else {
channel.toggleSubscription();
}
} else if (channel.pending_subscribe) {
channel.cancelSubscribeRequest();
} else {
channel.subscribeRequest();
}
}
/**
* Render
*/
render(): Node {
const {
channel,
...otherProps
} = this.props;
let text, icon = null;
if (channel.isOpen()) {
text = channel.subscribed ? i18n.t('channel.unsubscribe') : i18n.t('channel.subscribe');
} else {
text = channel.subscribed ? i18n.t('channel.unsubscribe') : (!channel.pending_subscribe ? i18n.t('channel.requestSubscription') : i18n.t('pending'));
if (channel.pending_subscribe) {
icon = <Icon name="ios-close" style={[CommonStyle.colorPrimary, CommonStyle.paddingLeft]} size={23}/>
}
}
return (
<Button
text={text}
onPress={this.onPress}
{...otherProps}
>{icon}</Button>
)
}
}
// @flow
import React from 'react';
import { View, Text } from 'react-native';
import type { Node } from 'react';
import type UserModel from '../UserModel';
import type SubscriptionRequestStore from './SubscriptionRequestStore';
import DiscoveryUser from '../../discovery/DiscoveryUser';
import Button from '../../common/components/Button';
import i18nService from '../../common/services/i18n.service';
import { CommonStyle as CS } from '../../styles/Common';
import { observer } from 'mobx-react';
type PropsType = {
row: any,
subscriptionRequest: SubscriptionRequestStore
};
/**
* Subscription Request
*/
export default
@observer
class SubscriptionRequest extends DiscoveryUser<PropsType> {
/**
* Get the channel from the props
*/
getChannel(): UserModel {
return this.props.row.item.subscriber;
}
/**
* Accept the request
*/
onAccept = () => {
this.props.subscriptionRequest.accept(this.props.row.item)
}
/**
* reject the request
*/
onReject = () => {
this.props.subscriptionRequest.decline(this.props.row.item)
}
/**
* Render Right buttons
*/
renderRightButton(): Node {
if (this.props.row.item.status) {
return (
<View style={[CS.rowJustifyEnd]}>
<Text style={[this.props.row.item.status != 'requestAccepted' ? CS.colorDanger: CS.colorPrimary]}>{i18nService.t(`channel.${this.props.row.item.status}`)}</Text>
</View>
)
}
return (
<View style={[CS.rowJustifyEnd]}>
<Button
text={i18nService.t('accept')}
onPress={this.onAccept}
loading={this.props.row.item.inProgress}
inverted
/>
<Button
text={i18nService.t('reject')}
loading={this.props.row.item.inProgress}
onPress={this.onReject}
/>
</View>
)
}
}
// @flow
import React, {
Component
} from 'react';
import {
FlatList,
ActivityIndicator,
Text,
View
} from 'react-native';
import type { Node, Element } from 'react';
import type SubscriptionRequestStore from './SubscriptionRequestStore';
import { inject, observer } from 'mobx-react/native';
import SubscriptionRequest from './SubscriptionRequest';
import i18n from '../../common/services/i18n.service';
import { CommonStyle as CS } from '../../styles/Common';
import ErrorLoading from '../../common/components/ErrorLoading';
type PropsType = {
subscriptionRequest: SubscriptionRequestStore
};
/**
* Subscription list
*/
export default
@inject('subscriptionRequest')
@observer
class SubscriptionRequestList extends Component<PropsType> {
/**
* Render item
*/
renderItem = (row: any): Element<any> => {
return (
<SubscriptionRequest
row={row}
onAccept={this.onAccept}
onReject={this.onReject}
subscriptionRequest={this.props.subscriptionRequest}
/>
)
}
onAccept = (row: any) => {
}
onReject = (row: any) => {
}
reload = () => {
this.props.subscriptionRequest.load();
}
/**
* Render
*/
render(): Node {
const {
subscriptionRequest,
...otherProps
} = this.props;
let footerCmp = null, emptyCmp = null;
if (subscriptionRequest.errorLoading) {
footerCmp = <ErrorLoading message={i18n.t('cantLoad')} tryAgain={this.reload}/>
} else {
const message = subscriptionRequest.loading ?
<ActivityIndicator size="large"/> :
<Text style={[CS.fontM, CS.fontHairline]}>{i18n.t('discovery.nothingToShow')}</Text>
emptyCmp = <View style={[CS.flexColumnCentered, CS.marginTop4x, CS.paddingTop2x]}>{message}</View>
}
return (
<FlatList
data={subscriptionRequest.requests}
renderItem={this.renderItem}
ListEmptyComponent={emptyCmp}
ListFooterComponent={footerCmp}
{...otherProps}
/>
)
}
}
\ No newline at end of file
//@flow
import { observable, action, extendObservable } from "mobx";
import api from "../../common/services/api.service";
import logService from "../../common/services/log.service";
import UserModel from "../UserModel";
/**
* Subscription request store
*/
export default class SubscriptionRequestStore {
@observable requests = [];
@observable loading = false;
@observable errorLoading = false;
/**
* Load the subscriptions requests
*/
@action
async load(): Promise<any> {
this.setLoading(true);
this.setErrorLoading(false);
try {
let { requests } = await api.get(`api/v2/subscriptions/incoming`);
this.setRequest(requests);
} catch (err) {
this.setErrorLoading(true);
logService.exception(err);
} finally {
this.setLoading(false);
}
}
/**
* Accept a request
* @param {any} request
*/
async accept(request: any): Promise<void> {
try {
this.setInProgress(request, true);
await api.put(
`api/v2/subscriptions/incoming/${request.subscriber.guid}/accept`
);
this.setStatus(request, 'requestAccepted');
} catch (err) {
logService.exception(err);
} finally {
this.setInProgress(request, false);
}
}
/**
* Decline a request
* @param {any} request
*/
async decline(request: any): Promise<void> {
try {
this.setInProgress(request, true);
await api.put(
`api/v2/subscriptions/incoming/${request.subscriber.guid}/decline`
);
this.setStatus(request, 'requestRejected');
} catch (err) {
logService.exception(err);
} finally {
this.setInProgress(request, false);
}
}
/**
* Set action
*/
@action
setLoading(value: boolean) {
this.loading = value;
}
/**
* Set requests list
* @param {Array<any>}
*/
@action
setRequest(requests: Array<any>) {
this.requests = requests;
this.requests.forEach((r: any): any => {
extendObservable(r, {inProgress: false, status: '' });
r.subscriber = UserModel.create(r.subscriber);
});
}
/**
* Set the error loading flag
* @param {boolean} value
*/
@action
setErrorLoading(value: boolean) {
this.errorLoading = value;
}
/**
* Set the in progress flag
* @param {any} request
* @param {boolean} value
*/
@action
setInProgress(request: any, value: boolean) {
request.inProgress = value;
}
/**
* Set the status flag
* @param {any} request
* @param {string} value
*/
@action
setStatus(request: any, value: string) {
request.status = value;
console.log('SETTNIG REQUEST', request)
}
/**
* Clear the store to the default values
*/
@action
clear() {
this.requests = [];
this.errorLoading = false;
}
/**
* Reset the store
*/
reset() {
this.clear();
}
}
\ No newline at end of file
......@@ -8,19 +8,21 @@ import {
import {
observer,
inject
} from 'mobx-react/native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import IonIcon from 'react-native-vector-icons/Ionicons';
import FAIcon from 'react-native-vector-icons/FontAwesome5';
import i18n from '../../common/services/i18n.service';
import colors from '../../styles/Colors';
import featuresService from '../../common/services/features.service';
const ICON_SIZE = 22;
export default
@observer
export default class Toolbar extends Component {
class Toolbar extends Component {
filterRewards = () => {
this.props.feed.setFilter('rewards');
......@@ -42,18 +44,32 @@ export default class Toolbar extends Component {
this.props.feed.setFilter('blogs');
}
filterRequests = () => {
this.props.feed.setFilter('requests');
this.props.subscriptionRequest.load();
}
render() {
const filter = this.props.feed.filter;
const pstyles = this.props.styles;
let rewards = null;
let rewards = null, subscriptionRequests = null;
if (this.props.hasRewards) {
rewards = (
<TouchableOpacity style={styles.button} onPress={this.filterRewards}>
<IonIcon name="ios-flash" size={ICON_SIZE} color={filter == 'rewards' ? colors.primary : color} />
<Text style={styles.buttontext}>{i18n.t('rewards').toUpperCase()}</Text>
<IonIcon name="ios-flash" size={ICON_SIZE} color={filter == 'rewards' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'rewards' ? styles.buttontextSelected : null]}>{i18n.t('rewards').toUpperCase()}</Text>
</TouchableOpacity>
)
}
if (this.props.channel.isOwner() && featuresService.has('permissions')) {
subscriptionRequests = (
<TouchableOpacity style={styles.button} onPress={this.filterRequests}>
<FAIcon name="user-check" size={ICON_SIZE} color={filter == 'requests' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'requests' ? styles.buttontextSelected : null]}>{i18n.t('requests').toUpperCase()}</Text>
</TouchableOpacity>
)
}
......@@ -62,22 +78,23 @@ export default class Toolbar extends Component {
<View style={styles.container}>
<View style={styles.topbar}>
<TouchableOpacity style={styles.button} onPress={this.filterFeed}>
<Icon name="list" size={ICON_SIZE} color={filter == 'feed' ? colors.primary : color} />
<Text style={styles.buttontext}>{i18n.t('feed').toUpperCase()}</Text>
<Icon name="list" size={ICON_SIZE} color={filter == 'feed' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'feed' ? styles.buttontextSelected : null]}>{i18n.t('feed').toUpperCase()}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={this.filterImages} >
<IonIcon name="md-image" size={ICON_SIZE} color={filter == 'images' ? colors.primary : color} />
<Text style={styles.buttontext}>{i18n.t('images').toUpperCase()}</Text>
<IonIcon name="md-image" size={ICON_SIZE} color={filter == 'images' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'images' ? styles.buttontextSelected : null]}>{i18n.t('images').toUpperCase()}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={this.filterVideos} >
<IonIcon name="md-videocam" size={ICON_SIZE} color={filter == 'videos' ? colors.primary : color} />
<Text style={styles.buttontext}>{i18n.t('videos').toUpperCase()}</Text>
<IonIcon name="md-videocam" size={ICON_SIZE} color={filter == 'videos' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'videos' ? styles.buttontextSelected : null]}>{i18n.t('videos').toUpperCase()}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={this.filterBlogs}>
<Icon name="subject" size={ICON_SIZE} color={filter == 'blogs' ? colors.primary : color} />
<Text style={styles.buttontext}>{i18n.t('blogs.blogs').toUpperCase()}</Text>
<Icon name="subject" size={ICON_SIZE} color={filter == 'blogs' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'blogs' ? styles.buttontextSelected : null]}>{i18n.t('blogs.blogs').toUpperCase()}</Text>
</TouchableOpacity>
{rewards}
{subscriptionRequests}
</View>
</View>
);
......@@ -88,17 +105,21 @@ const color = '#444'
const styles = StyleSheet.create({
container: {
height: 65,
height: 55,
display: 'flex',
flexDirection: 'row',
paddingTop: 12,
paddingTop: 5,
paddingLeft: 0,
paddingRight: 0,
paddingBottom: 5,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#EEE',
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: '#EEE',
},
icon: {
height: 25,
},
topbar: {
flex: 1,
justifyContent: 'space-between',
......@@ -109,8 +130,12 @@ const styles = StyleSheet.create({
fontSize: 10,
color: '#444',
},
buttontextSelected: {
color: colors.primary
},
button: {
flex: 1,
justifyContent: 'space-between',
flexDirection: 'column',
alignItems: 'center',
padding: 3,
......
......@@ -227,7 +227,7 @@ export default class BaseModel {
*/
blockOwner() {
if (!this.ownerObj) throw new Error('This entity has no owner');
return channelService.toggleBlock(this.ownerObj.guid, true);
return this.ownerObj.toggleBlock(true);
}
/**
......@@ -235,7 +235,7 @@ export default class BaseModel {
*/
unblockOwner() {
if (!this.ownerObj) throw new Error('This entity has no owner');
return channelService.toggleBlock(this.ownerObj.guid, false);
return this.ownerObj.toggleBlock(false);
}
@action
......
......@@ -10,7 +10,6 @@ import {
TouchableOpacity,
} from 'react-native';
import { CommonStyle } from '../../styles/Common';
import { ComponentsStyle } from '../../styles/Components';
import colors from '../../styles/Colors';
......@@ -62,7 +61,7 @@ export default class Button extends Component {
const body = this.props.loading ?
<ActivityIndicator color={mainColor}/> :
<Text style={[{ color: textColor || mainColor }, textStyle]} > {this.props.text} </Text>;
<Text style={[{ color: textColor || mainColor }, textStyle]}> {this.props.text} </Text>;
const onButtonPress = this.props.loading ? null : onPress;
......
......@@ -76,20 +76,21 @@ class LogService {
prepend = null;
}
// do not log request or api errors < 500
if (!isNetworkFail(error) && (!this.isApiError(error) || this.isUnexpectedError(error))) {
// report the issue to sentry
Sentry.captureException(error);
}
let stack = null;
if (__DEV__) {
stack = parseErrorStack(error);
}
if (stack) {
deviceLog.rnerror(false, (prepend ? `${prepend} ` : '') + error.message, stack);
if (__DEV__) console.log(error);
} else {
deviceLog.error((prepend ? `${prepend} ` : '') + String(error));
let stack = null;
if (__DEV__) {
stack = parseErrorStack(error);
}
if (stack) {
deviceLog.rnerror(false, (prepend ? `${prepend} ` : '') + error.message, stack);
if (__DEV__) console.log(error);
} else {
deviceLog.error((prepend ? `${prepend} ` : '') + String(error));
}
}
}
}
......
......@@ -547,7 +547,7 @@ export default class DiscoveryScreen extends Component {
return (
<ErrorBoundary containerStyle={CS.hairLineBottom}>
<DiscoveryUser entity={row} navigation={this.props.navigation} hideButtons={this.props.discovery.filters.type == 'lastchannels'} />
<DiscoveryUser row={row} navigation={this.props.navigation} hideButtons={this.props.discovery.filters.type == 'lastchannels'} />
</ErrorBoundary>
);
}
......
......@@ -6,33 +6,26 @@ import {
TouchableOpacity,
Image,
StyleSheet,
Alert,
Keyboard,
TouchableHighlight,
Text,
View
} from 'react-native';
import {
observer,
inject
} from 'mobx-react/native'
import {
MINDS_CDN_URI
} from '../config/Config';
import abbrev from '../common/helpers/abbrev'
import colors from '../styles/Colors'
import { ComponentsStyle } from '../styles/Components';
import { CommonStyle } from '../styles/Common';
import i18n from '../common/services/i18n.service';
import { FLAG_SUBSCRIBE, FLAG_VIEW } from '../common/Permissions';
import SubscriptionButton from '../channel/subscription/SubscriptionButton';
@inject('user')
export default
@observer
export default class DiscoveryUser extends Component {
class DiscoveryUser extends Component {
/**
* Navigate To channel
......@@ -40,61 +33,35 @@ export default class DiscoveryUser extends Component {
_navToChannel = () => {
Keyboard.dismiss();
if (this.props.navigation) {
if (!this.props.entity.item.can(FLAG_VIEW, true)) {
if (this.props.row.item.isOpen() && !this.props.row.item.can(FLAG_VIEW, true)) {
return;
}
this.props.navigation.push('Channel', { entity: this.props.entity.item });
}
}
toggleSubscribe = () => {
if (this.props.entity.item.subscribed) {
Alert.alert(
i18n.t('attention'),
i18n.t('channel.confirmUnsubscribe'),
[{ text: i18n.t('yesImSure'), onPress: () => this._toggleSusbcribed() }, { text: i18n.t('no')}]
);
} else {
this._toggleSusbcribed();
this.props.navigation.push('Channel', { entity: this.props.row.item });
}
}
_toggleSusbcribed() {
const item = this.props.entity.item;
this.props.entity.item.toggleSubscription();
}
renderRightButton() {
const item = this.props.entity.item;
if (this.props.user.me.guid === item.guid || this.props.hideButtons || !item.can(FLAG_SUBSCRIBE)) {
const channel = this.props.row.item;
if (channel.isOwner() || this.props.hideButtons || (channel.isOpen() && !channel.can(FLAG_SUBSCRIBE) )) {
return;
}
if (item.subscribed) {
return <TouchableHighlight
onPress={ this.toggleSubscribe }
underlayColor='transparent'
style={[ComponentsStyle.button ]}
accessibilityLabel={i18n.t('channel.subscribeMessage')}
>
<Text style={{ color: '#888' }} > {i18n.t('channel.subscribed')} </Text>
</TouchableHighlight>;
} else {
return <TouchableHighlight
onPress={ this.toggleSubscribe }
underlayColor='transparent'
style={[ComponentsStyle.button, ComponentsStyle.buttonAction]}
accessibilityLabel={i18n.t('channel.unsubscribeMessage')}
>
<Text style={{ color: colors.primary }} > {i18n.t('channel.subscribe')} </Text>
</TouchableHighlight>;
}
return (
<SubscriptionButton
channel={channel}
/>
)
}
getChannel() {
return this.props.row.item;
}
/**
* Render
*/
render() {
const item = this.props.entity.item;
const item = this.getChannel();
const avatarImg = { uri: MINDS_CDN_URI + 'icon/' + item.guid + '/medium' };
return (
<TouchableOpacity style={styles.row} onPress={this._navToChannel}>
......
import React, {
Component
} from 'react';
import React from 'react';
import {
StyleSheet
} from 'react-native';
import {
observer,
inject
} from 'mobx-react/native'
import Icon from 'react-native-vector-icons/MaterialIcons';
import DiscoveryUser from '../discovery/DiscoveryUser';
......@@ -18,24 +12,24 @@ import DiscoveryUser from '../discovery/DiscoveryUser';
/**
* Group user component
*/
@inject('user')
export default class GroupUser extends DiscoveryUser.wrappedComponent {
export default
class GroupUser extends DiscoveryUser {
/**
* Handle right button press
*/
handlePress = () => {
this.props.onRightIconPress(this.props.entity.item)
this.props.onRightIconPress(this.props.row.item)
}
/**
* Render right button
*/
renderRightButton() {
const item = this.props.entity.item;
const item = this.props.row.item;
if (!(this.props.isOwner || this.props.isModerator) ||
this.props.user.me.guid === item.guid ||
this.props.isModerator && this.props.entity.item['is:owner']) {
item.isOwner() ||
this.props.isModerator && this.props.row.item['is:owner']) {
return;
}
......
......@@ -9,8 +9,6 @@ import {
StyleSheet,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { MINDS_CDN_URI } from '../../config/Config';
import abbrev from '../../common/helpers/abbrev';
import FastImage from 'react-native-fast-image';
......
......@@ -67,11 +67,13 @@ export const ComponentsStyle = StyleSheet.create({
//button
commonButton: {
margin: 4,
padding: 4,
alignItems: 'center',
borderRadius: 20,
borderWidth: 1,
flexDirection: 'row',
justifyContent: 'center',
margin: 4,
padding: 4,
},
bluebutton: {
margin:4,
......