Channel creation and editing #36

Merged
akinwale merged 11 commits from channel-creator into master 2019-09-13 09:16:06 +02:00
27 changed files with 1703 additions and 35 deletions

14
package-lock.json generated
View file

@ -5574,8 +5574,8 @@
}
},
"lbry-redux": {
"version": "github:lbryio/lbry-redux#362d764c4c0de23032b6871b4d54207dc548028c",
"from": "github:lbryio/lbry-redux#362d764c4c0de23032b6871b4d54207dc548028c",
"version": "github:lbryio/lbry-redux#3133ea60b0302c162f7b6f67cc858997f1d2ab52",
"from": "github:lbryio/lbry-redux#3133ea60b0302c162f7b6f67cc858997f1d2ab52",
"requires": {
"proxy-polyfill": "0.1.6",
"reselect": "^3.0.0",
@ -8039,11 +8039,6 @@
}
}
},
"react-native-document-picker": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-3.2.4.tgz",
"integrity": "sha512-5l0/fkgasUZdIk9jUUkReDtNCQn2yg1+BrMPHMt45c/NVmE15ThnhIuDj8/n8h1F1RlhUb3SzF86ANK4OdZAiQ=="
},
"react-native-exception-handler": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/react-native-exception-handler/-/react-native-exception-handler-2.9.0.tgz",
@ -9124,6 +9119,11 @@
"object-assign": "^4.1.1"
}
},
"seedrandom": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.3.tgz",
"integrity": "sha512-PJLhhxIMjlMJaiIRtqiVW061EZn3cS+waZkbFe7eCa2R3g88HbNdWmw4NTFG1w5unxd0GeNaUUxZJP7gPAzSDQ=="
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",

View file

@ -12,7 +12,7 @@
"base-64": "^0.1.0",
"@expo/vector-icons": "^8.1.0",
"gfycat-style-urls": "^1.0.3",
"lbry-redux": "lbryio/lbry-redux#362d764c4c0de23032b6871b4d54207dc548028c",
"lbry-redux": "lbryio/lbry-redux#3133ea60b0302c162f7b6f67cc858997f1d2ab52",
"lbryinc": "lbryio/lbryinc#b9f354ae50bd57691765a7d042c5054167878bf4",
"lodash": ">=4.17.11",
"merge": ">=1.2.1",
@ -22,7 +22,6 @@
"@react-native-community/async-storage": "^1.5.1",
"react-native-camera": "^2.11.1",
"react-native-country-picker-modal": "^0.6.2",
"react-native-document-picker": "^3.2.4",
"react-native-exception-handler": "2.9.0",
"react-native-fast-image": "^6.1.1",
"react-native-fs": "^2.13.3",
@ -44,7 +43,8 @@
"redux-persist-transform-compress": "^4.2.0",
"redux-persist-transform-filter": "0.0.18",
"redux-thunk": "^2.3.0",
"rn-fetch-blob": "0.10.15"
"rn-fetch-blob": "0.10.15",
"seedrandom": "3.0.3"
},
"devDependencies": {
"@babel/core": "^7.5.4",

View file

@ -1,5 +1,6 @@
import React from 'react';
import AboutPage from 'page/about';
import ChannelCreatorPage from 'page/channelCreator';
import DiscoverPage from 'page/discover';
import DownloadsPage from 'page/downloads';
import DrawerContent from 'component/drawerContent';
@ -159,6 +160,12 @@ const drawer = createDrawerNavigator(
drawerIcon: ({ tintColor }) => <Icon name="wallet" size={drawerIconSize} style={{ color: tintColor }} />,
},
},
ChannelCreator: {
screen: ChannelCreatorPage,
navigationOptions: {
drawerIcon: ({ tintColor }) => <Icon name="at" size={drawerIconSize} style={{ color: tintColor }} />,
},
},
Publish: {
screen: PublishPage,
navigationOptions: {

View file

@ -13,10 +13,15 @@ export default class ChannelIconItem extends React.PureComponent {
autothumbStyle.autothumbBlue,
autothumbStyle.autothumbLightBlue,
autothumbStyle.autothumbCyan,
autothumbStyle.autothumbTeal,
autothumbStyle.autothumbGreen,
autothumbStyle.autothumbYellow,
autothumbStyle.autothumbOrange,
autothumbStyle.autothumbDeepPurple,
autothumbStyle.autothumbAmber,
autothumbStyle.autothumbLime,
autothumbStyle.autothumbLightGreen,
autothumbStyle.autothumbDeepOrange,
autothumbStyle.autothumbBrown,
];
state = {

View file

@ -204,7 +204,11 @@ class ClaimList extends React.PureComponent {
const options = this.buildClaimSearchOptions();
const claimSearchKey = createNormalizedClaimSearchKey(options);
const uris = claimSearchByQuery[claimSearchKey];
let uris = claimSearchByQuery[claimSearchKey];
if (uris) {
uris = uris.filter(uri => uri && uri.length > 0);
}
kauffj commented 2019-09-04 00:11:57 +02:00 (Migrated from github.com)
Review

(update to 0.40?)

(update to 0.40?)
kauffj commented 2019-09-04 00:12:49 +02:00 (Migrated from github.com)
Review

Aside from updating to 0.40, I think it is a good idea to comment code like this so that others (including future you, which is a different person than now you):

  • understand why it is there
  • understand when it can be removed
Aside from updating to 0.40, I think it is a good idea to comment code like this so that others (including future you, which is a different person than now you): - understand why it is there - understand when it can be removed
if (Constants.ORIENTATION_VERTICAL === orientation) {
return (

View file

@ -12,6 +12,7 @@ const groupedMenuItems = {
{ icon: 'globe-americas', label: 'All content', route: Constants.DRAWER_ROUTE_TRENDING },
],
'Your content': [
{ icon: 'at', label: 'Channels', route: Constants.DRAWER_ROUTE_CHANNEL_CREATOR },
{ icon: 'download', label: 'Library', route: Constants.DRAWER_ROUTE_MY_LBRY },
{ icon: 'cloud-upload-alt', label: 'Publishes', route: Constants.DRAWER_ROUTE_PUBLISHES },
{ icon: 'upload', label: 'New publish', route: Constants.DRAWER_ROUTE_PUBLISH },

View file

@ -67,11 +67,12 @@ export default class TagSearch extends React.PureComponent {
};
render() {
const { name, style, type, selectedTags = [], showNsfwTags } = this.props;
const { editable, name, style, type, selectedTags = [], showNsfwTags } = this.props;
return (
<View>
<TextInput
editable={editable}
style={tagStyle.searchInput}
placeholder={'Search for more tags'}
underlineColorAndroid={Colors.NextLbryGreen}

View file

@ -25,6 +25,9 @@ const Constants = {
PHASE_DETAILS: 'details',
PHASE_PUBLISH: 'publish',
PHASE_LIST: 'list',
PHASE_NEW: 'create',
CONTENT_TAB: 'content',
ABOUT_TAB: 'about',
@ -56,6 +59,11 @@ const Constants = {
ACTION_SORT_BY_ITEM_CHANGED: 'SORT_BY_ITEM_CHANGED',
ACTION_TIME_ITEM_CHANGED: 'TIME_ITEM_CHANGED',
ACTION_UPDATE_PUBLISH_FORM_STATE: 'UPDATE_PUBLISH_FORM_STATE',
ACTION_UPDATE_CHANNEL_FORM_STATE: 'UPDATE_CHANNEL_FORM_STATE',
ACTION_CLEAR_PUBLISH_FORM_STATE: 'CLEAR_PUBLISH_FORM_STATE',
ACTION_CLEAR_CHANNEL_FORM_STATE: 'CLEAR_CHANNEL_FORM_STATE',
ORIENTATION_HORIZONTAL: 'horizontal',
ORIENTATION_VERTICAL: 'vertical',
@ -69,6 +77,7 @@ const Constants = {
DRAWER_ROUTE_SUBSCRIPTIONS: 'Subscriptions',
DRAWER_ROUTE_MY_LBRY: 'Downloads',
DRAWER_ROUTE_PUBLISH: 'Publish',
DRAWER_ROUTE_PUBLISH_FORM: 'PublishForm',
DRAWER_ROUTE_PUBLISHES: 'Publishes',
DRAWER_ROUTE_REWARDS: 'Rewards',
DRAWER_ROUTE_WALLET: 'Wallet',
@ -77,6 +86,8 @@ const Constants = {
DRAWER_ROUTE_SEARCH: 'Search',
DRAWER_ROUTE_TRANSACTION_HISTORY: 'TransactionHistory',
DRAWER_ROUTE_TAG: 'Tag',
DRAWER_ROUTE_CHANNEL_CREATOR: 'ChannelCreator',
DRAWER_ROUTE_CHANNEL_CREATOR_FORM: 'ChannnelCreatorForm',
FULL_ROUTE_NAME_DISCOVER: 'DiscoverStack',
FULL_ROUTE_NAME_WALLET: 'WalletStack',
@ -146,4 +157,8 @@ export const DrawerRoutes = [
Constants.DRAWER_ROUTE_ABOUT,
Constants.DRAWER_ROUTE_SEARCH,
Constants.DRAWER_ROUTE_TRANSACTION_HISTORY,
Constants.DRAWER_ROUTE_CHANNEL_CREATOR,
];
// sub-pages for main routes
export const InnerDrawerRoutes = [Constants.DRAWER_ROUTE_CHANNEL_CREATOR_FORM, Constants.DRAWER_ROUTE_PUBLISH_FORM];

View file

@ -37,6 +37,7 @@ import FilesystemStorage from 'redux-persist-filesystem-storage';
import createCompressor from 'redux-persist-transform-compress';
import createFilter from 'redux-persist-transform-filter';
import moment from 'moment';
import formReducer from 'redux/reducers/form';
import drawerReducer from 'redux/reducers/drawer';
import settingsReducer from 'redux/reducers/settings';
import thunk from 'redux-thunk';
@ -109,6 +110,7 @@ const reducers = persistCombineReducers(persistOptions, {
file: fileReducer,
fileInfo: fileInfoReducer,
filtered: filteredReducer,
form: formReducer,
homepage: homepageReducer,
nav: navigatorReducer,
notifications: notificationsReducer,

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import { doFetchClaimsByChannel, makeSelectClaimForUri } from 'lbry-redux';
import { makeSelectClaimForUri, selectMyChannelClaims } from 'lbry-redux';
import { doPopDrawerStack } from 'redux/actions/drawer';
import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
import { selectDrawerStack } from 'redux/selectors/drawer';
@ -7,6 +7,7 @@ import { selectSortByItem, selectTimeItem } from 'redux/selectors/settings';
import ChannelPage from './view';
const select = (state, props) => ({
channels: selectMyChannelClaims(state),
claim: makeSelectClaimForUri(props.uri)(state),
drawerStack: selectDrawerStack(state),
sortByItem: selectSortByItem(state),
@ -14,7 +15,6 @@ const select = (state, props) => ({
});
const perform = dispatch => ({
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
popDrawerStack: () => dispatch(doPopDrawerStack()),
setSortByItem: item => dispatch(doSetSortByItem(item)),
setTimeItem: item => dispatch(doSetTimeItem(item)),

View file

@ -2,6 +2,7 @@
import React from 'react';
import {
ActivityIndicator,
Alert,
Dimensions,
Image,
NativeModules,
@ -159,10 +160,46 @@ class ChannelPage extends React.PureComponent {
);
};
onEditPressed = () => {
const { claim, navigation } = this.props;
if (claim) {
const { permanent_url: permanentUrl } = claim;
navigation.navigate({
routeName: Constants.DRAWER_ROUTE_CHANNEL_CREATOR,
params: { editChannelUrl: permanentUrl },
});
}
};
onDeletePressed = () => {
const { abandonClaim, claim, navigation } = this.props;
if (claim) {
const { txid, nout } = claim;
// show confirm alert
Alert.alert(
__('Delete channel'),
__('Are you sure you want to delete this channel?'),
[
{ text: __('No') },
{
text: __('Yes'),
onPress: () => {
abandonClaim(txid, nout);
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_CHANNEL_CREATOR });
},
},
],
{ cancelable: true }
);
}
};
render() {
const { claim, navigation, uri, drawerStack, popDrawerStack, sortByItem, timeItem } = this.props;
const { channels, claim, navigation, uri, drawerStack, popDrawerStack, sortByItem, timeItem } = this.props;
const { name, permanent_url: permanentUrl } = claim;
const { autoStyle, showSortPicker, showTimePicker } = this.state;
const ownedChannel = channels ? channels.map(channel => channel.permanent_url).includes(permanentUrl) : false;
let thumbnailUrl,
coverUrl,
@ -218,12 +255,32 @@ class ChannelPage extends React.PureComponent {
</View>
<View style={channelPageStyle.subscribeButtonContainer}>
<SubscribeButton style={channelPageStyle.subscribeButton} uri={fullUri} name={name} />
<SubscribeNotificationButton
style={[channelPageStyle.subscribeButton, channelPageStyle.bellButton]}
uri={fullUri}
name={name}
/>
{ownedChannel && (
<Button
style={channelPageStyle.actionButton}
theme={'light'}
icon={'edit'}
text={'Edit'}
onPress={this.onEditPressed}
/>
)}
{ownedChannel && (
<Button
style={channelPageStyle.deleteButton}
theme={'light'}
icon={'trash-alt'}
text={'Delete'}
onPress={this.onDeletePressed}
/>
)}
{!ownedChannel && <SubscribeButton style={channelPageStyle.subscribeButton} uri={fullUri} name={name} />}
{!ownedChannel && (
<SubscribeNotificationButton
style={[channelPageStyle.subscribeButton, channelPageStyle.bellButton]}
uri={fullUri}
name={name}
/>
)}
</View>
</View>

View file

@ -0,0 +1,49 @@
import { connect } from 'react-redux';
import {
selectAbandoningIds,
selectBalance,
selectMyChannelClaims,
selectFetchingMyChannels,
selectUpdatingChannel,
selectUpdateChannelError,
doAbandonClaim,
doFetchChannelListMine,
doCreateChannel,
doUpdateChannel,
doToast,
} from 'lbry-redux';
import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doUpdateChannelFormState, doClearChannelFormState } from 'redux/actions/form';
import { selectDrawerStack } from 'redux/selectors/drawer';
import { selectChannelFormState } from 'redux/selectors/form';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import ChannelCreator from './view';
const select = state => ({
abandoningClaimIds: selectAbandoningIds(state),
channels: selectMyChannelClaims(state),
channelFormState: selectChannelFormState(state),
drawerStack: selectDrawerStack(state),
fetchingChannels: selectFetchingMyChannels(state),
balance: selectBalance(state),
updatingChannel: selectUpdatingChannel(state),
updateChannelError: selectUpdateChannelError(state),
});
const perform = dispatch => ({
abandonClaim: (txid, nout) => dispatch(doAbandonClaim(txid, nout)),
notify: data => dispatch(doToast(data)),
clearChannelFormState: () => dispatch(doClearChannelFormState()),
createChannel: (name, amount, optionalParams) => dispatch(doCreateChannel(name, amount, optionalParams)),
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
updateChannel: params => dispatch(doUpdateChannel(params)),
updateChannelFormState: data => dispatch(doUpdateChannelFormState(data)),
pushDrawerStack: (routeName, params) => dispatch(doPushDrawerStack(routeName, params)),
popDrawerStack: () => dispatch(doPopDrawerStack()),
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
});
export default connect(
select,
perform
)(ChannelCreator);

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
import { connect } from 'react-redux';
import {
doFetchFileInfo,
doFetchChannelListMine,
doFetchClaimListMine,
doFileGet,
doPurchaseUri,
@ -19,6 +20,7 @@ import {
makeSelectThumbnailForUri,
makeSelectTitleForUri,
selectBalance,
selectMyChannelClaims,
selectMyClaimUrisWithoutChannels,
selectPurchasedUris,
selectFailedPurchaseUris,
@ -46,6 +48,7 @@ const select = (state, props) => {
return {
balance: selectBalance(state),
blackListedOutpoints: selectBlackListedOutpoints(state),
channels: selectMyChannelClaims(state),
claim: makeSelectClaimForUri(selectProps.uri)(state),
drawerStack: selectDrawerStack(state),
isResolvingUri: makeSelectIsUriResolving(selectProps.uri)(state),
@ -75,6 +78,7 @@ const perform = dispatch => ({
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
fetchMyClaims: () => dispatch(doFetchClaimListMine()),
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
fileGet: (uri, saveFile) => dispatch(doFileGet(uri, saveFile)),
notify: data => dispatch(doToast(data)),
popDrawerStack: () => dispatch(doPopDrawerStack()),

View file

@ -1,5 +1,5 @@
import React from 'react';
import { Lbry, normalizeURI } from 'lbry-redux';
import { Lbry, normalizeURI, parseURI } from 'lbry-redux';
import { Lbryio } from 'lbryinc';
import {
ActivityIndicator,
@ -100,7 +100,7 @@ class FilePage extends React.PureComponent {
DeviceEventEmitter.addListener('onDownloadUpdated', this.handleDownloadUpdated);
DeviceEventEmitter.addListener('onDownloadCompleted', this.handleDownloadCompleted);
const { fileInfo, isResolvingUri, resolveUri, navigation } = this.props;
const { fetchChannelListMine, fileInfo, isResolvingUri, resolveUri, navigation } = this.props;
const { uri, uriVars } = navigation.state.params;
this.setState({ uri, uriVars });
@ -108,6 +108,7 @@ class FilePage extends React.PureComponent {
this.fetchFileInfo(this.props);
this.fetchCostInfo(this.props);
fetchChannelListMine();
if (NativeModules.Firebase) {
NativeModules.Firebase.track('open_file_page', { uri: uri });
@ -567,6 +568,7 @@ class FilePage extends React.PureComponent {
const {
balance,
claim,
channels,
channelUri,
costInfo,
fileInfo,
@ -585,6 +587,10 @@ class FilePage extends React.PureComponent {
} = this.props;
const { uri, autoplay } = navigation.state.params;
const myChannelUris = channels ? channels.map(channel => channel.permanent_url) : [];
const ownedClaim = myClaimUris.includes(uri) || myChannelUris.includes(uri);
const { isChannel } = parseURI(uri);
let innerContent = null;
if ((isResolvingUri && !claim) || !claim) {
return (
@ -597,7 +603,14 @@ class FilePage extends React.PureComponent {
)}
{claim === null && !isResolvingUri && (
<View style={filePageStyle.container}>
<Text style={filePageStyle.emptyClaimText}>There's nothing at this location.</Text>
{ownedClaim && (
<Text style={filePageStyle.emptyClaimText}>
{isChannel
? 'It looks like you just created this channel. It will appear in a few minutes.'
: 'It looks you just published this content. It will appear in a few minutes.'}
</Text>
)}
{!ownedClaim && <Text style={filePageStyle.emptyClaimText}>There's nothing at this location.</Text>}
</View>
)}
<UriBar value={uri} navigation={navigation} />
@ -606,7 +619,7 @@ class FilePage extends React.PureComponent {
}
if (claim) {
if (claim && claim.name.length && claim.name[0] === '@') {
if (isChannel) {
return <ChannelPage uri={uri} navigation={navigation} />;
}

View file

@ -523,6 +523,8 @@ class PublishPage extends React.PureComponent {
});
};
handleUploadPressed = () => {};
getRandomFileId = () => {
// generate a random id for a photo or recorded video between 1 and 20 (for creating thumbnails)
const id = Math.floor(Math.random() * (20 - 2)) + 1;
@ -824,6 +826,7 @@ class PublishPage extends React.PureComponent {
)}
<TextInput
editable={this.state.canPublish && !this.state.publishStarted}
multiline
placeholder={this.state.descriptionFocused ? '' : 'Description'}
style={publishStyle.inputText}
value={this.state.description}

View file

@ -33,6 +33,15 @@ class PublishesPage extends React.PureComponent {
this.onComponentFocused();
}
componentWillReceiveProps(nextProps) {
const { currentRoute } = nextProps;
const { currentRoute: prevRoute } = this.props;
if (Constants.DRAWER_ROUTE_PUBLISHES === currentRoute && currentRoute !== prevRoute) {
this.onComponentFocused();
}
}
onComponentFocused = () => {
const { checkPendingPublishes, fetchMyClaims, pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();

23
src/redux/actions/form.js Normal file
View file

@ -0,0 +1,23 @@
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
export const doUpdatePublishFormState = publishFormValue => dispatch =>
dispatch({
type: Constants.ACTION_UPDATE_PUBLISH_FORM_STATE,
data: { ...publishFormValue },
});
export const doUpdateChannelFormState = channelFormValue => dispatch =>
dispatch({
type: Constants.ACTION_UPDATE_CHANNEL_FORM_STATE,
data: { ...channelFormValue },
});
export const doClearPublishFormState = () => dispatch =>
dispatch({
type: Constants.ACTION_CLEAR_PUBLISH_FORM_STATE,
});
export const doClearChannelFormState = () => dispatch =>
dispatch({
type: Constants.ACTION_CLEAR_CHANNEL_FORM_STATE,
});

View file

@ -16,7 +16,19 @@ reducers[Constants.ACTION_PUSH_DRAWER_STACK] = (state, action) => {
const { routeName, params } = action.data;
const newStack = state.stack.slice();
if (routeName !== newStack[newStack.length - 1].route) {
const lastRoute = newStack[newStack.length - 1].route;
let canPushStack = routeName !== lastRoute;
if (
lastRoute === Constants.DRAWER_ROUTE_CHANNEL_CREATOR_FORM &&
routeName === Constants.DRAWER_ROUTE_CHANNEL_CREATOR
) {
canPushStack = false;
}
if (lastRoute === Constants.DRAWER_ROUTE_PUBLISH_FORM && routeName === Constants.DRAWER_ROUTE_PUBLISH) {
canPushStack = false;
}
kauffj commented 2019-09-12 00:22:38 +02:00 (Migrated from github.com)
Review

I'll be honest that I don't really understand what the above is doing, but it smells hackish 👃

I'll be honest that I don't really understand what the above is doing, but it smells hackish :nose:
akinwale commented 2019-09-12 08:38:39 +02:00 (Migrated from github.com)
Review

On the publish page, we have two screens: the gallery selector and the publish form.
On the channels page, we have the channel list and the channel editor form.
This is essentially checking if we're on one of the pages (or a related sub screen) before pushing into the stack.

On the publish page, we have two screens: the gallery selector and the publish form. On the channels page, we have the channel list and the channel editor form. This is essentially checking if we're on one of the pages (or a related sub screen) before pushing into the stack.
if (canPushStack) {
newStack.push({ route: routeName, params });
}

View file

@ -0,0 +1,49 @@
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
const reducers = {};
const defaultState = {
channelFormValues: {},
publishFormValues: {},
};
reducers[Constants.ACTION_UPDATE_PUBLISH_FORM_STATE] = (state, action) => {
const { data } = action;
const publishFormValues = Object.assign({}, state.publishFormValues);
const newPublishFormValues = Object.assign(publishFormValues, data);
return {
...state,
publishFormValues: newPublishFormValues,
};
};
reducers[Constants.ACTION_UPDATE_CHANNEL_FORM_STATE] = (state, action) => {
const { data } = action;
const channelFormValues = Object.assign({}, state.channelFormValues);
const newChannelFormValues = Object.assign(channelFormValues, data);
return {
...state,
channelFormValues: newChannelFormValues,
};
};
reducers[Constants.ACTION_CLEAR_PUBLISH_FORM_STATE] = state => {
return {
...state,
publishFormValues: {},
};
};
reducers[Constants.ACTION_CLEAR_CHANNEL_FORM_STATE] = state => {
return {
...state,
channelFormValues: {},
};
};
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
return state;
}

View file

@ -0,0 +1,13 @@
import { createSelector } from 'reselect';
export const selectState = state => state.form || {};
export const selectPublishFormState = createSelector(
selectState,
state => state.publishFormValues || {}
);
export const selectChannelFormState = createSelector(
selectState,
state => state.channelFormValues || {}
);

View file

@ -4,6 +4,9 @@ const autothumbStyle = StyleSheet.create({
autothumbPurple: {
backgroundColor: '#9c27b0',
},
autothumbDeepPurple: {
backgroundColor: '#5e35b1',
},
autothumbRed: {
backgroundColor: '#e53935',
},
@ -34,6 +37,21 @@ const autothumbStyle = StyleSheet.create({
autothumbOrange: {
backgroundColor: '#ffa726',
},
autothumbAmber: {
backgroundColor: '#ffb300',
},
autothumbLime: {
backgroundColor: '#c0ca33',
},
autothumbLightGreen: {
backgroundColor: '#7cb342',
},
autothumbDeepOrange: {
backgroundColor: '#f4511e',
},
autothumbBrown: {
backgroundColor: '#6d4c41',
},
});
export default autothumbStyle;

View file

@ -0,0 +1,261 @@
import { Dimensions, StyleSheet } from 'react-native';
import Colors from './colors';
const screenDimension = Dimensions.get('window');
const screenWidth = screenDimension.width;
const channelCreatorStyle = StyleSheet.create({
card: {
backgroundColor: Colors.White,
marginTop: 16,
marginLeft: 16,
marginRight: 16,
padding: 16,
},
container: {
flex: 1,
backgroundColor: Colors.PageBackground,
},
channelPicker: {
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
height: 52,
width: '100%',
},
bidRow: {
flexDirection: 'row',
alignItems: 'center',
},
label: {
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
},
channelNameInput: {
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
paddingLeft: 20,
},
bidAmountInput: {
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
marginLeft: 16,
textAlign: 'right',
width: 80,
},
helpText: {
fontFamily: 'Inter-UI-Regular',
fontSize: 12,
},
channelTitleInput: {
marginBottom: 4,
},
createChannelContainer: {
marginTop: 60,
flex: 1,
},
channelAt: {
position: 'absolute',
left: 4,
top: 13,
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
},
buttonContainer: {
marginTop: 24,
marginBottom: 16,
},
buttons: {
marginLeft: 16,
marginRight: 16,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
cancelLink: {
marginRight: 16,
},
createButton: {
backgroundColor: Colors.LbryGreen,
alignSelf: 'flex-end',
},
inlineError: {
fontFamily: 'Inter-UI-Regular',
fontSize: 12,
color: Colors.Red,
marginTop: 2,
},
imageSelectors: {
width: '100%',
height: 160,
},
coverImageTouchArea: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center',
},
coverImage: {
width: '100%',
height: '100%',
},
avatarImageContainer: {
position: 'absolute',
left: screenWidth / 2 - 80 / 2,
bottom: -16,
width: 80,
height: 80,
borderRadius: 160,
overflow: 'hidden',
},
avatarTouchArea: {
width: 80,
height: 80,
alignItems: 'center',
justifyContent: 'center',
},
avatarImage: {
width: '100%',
height: '100%',
},
listFooter: {
marginTop: 24,
},
createChannelButton: {
backgroundColor: Colors.LbryGreen,
alignSelf: 'flex-start',
},
scrollContainer: {
marginTop: 60,
marginLeft: 16,
marginRight: 16,
},
scrollPadding: {
paddingTop: 16,
},
channelListItem: {
flexDirection: 'row',
marginBottom: 16,
alignItems: 'center',
},
channelListTitle: {
fontFamily: 'Inter-UI-Regular',
fontSize: 18,
marginBottom: 4,
},
channelListName: {
fontFamily: 'Inter-UI-Regular',
fontSize: 14,
},
channelListAvatar: {
marginRight: 16,
width: 80,
height: 80,
borderRadius: 160,
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
},
selectedOverlay: {
position: 'absolute',
left: 0,
top: 0,
width: 80,
height: 80,
borderRadius: 160,
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#000000aa',
},
listHeader: {
alignItems: 'center',
marginTop: 16,
marginBottom: 16,
},
textInputLayout: {
marginBottom: 4,
},
textInputTitle: {
fontFamily: 'Inter-UI-Regular',
fontSize: 12,
marginBottom: -10,
marginLeft: 4,
},
inputText: {
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
},
toggleContainer: {
marginTop: 24,
alignItems: 'center',
justifyContent: 'flex-end',
},
modeLink: {
color: Colors.LbryGreen,
alignSelf: 'flex-end',
marginRight: 16,
},
cardTitle: {
fontFamily: 'Inter-UI-Regular',
fontSize: 20,
marginBottom: 8,
},
tag: {
marginRight: 4,
marginBottom: 4,
},
tagList: {
flexDirection: 'row',
flexWrap: 'wrap',
},
editOverlay: {
alignItems: 'center',
justifyContent: 'center',
borderRadius: 24,
position: 'absolute',
padding: 8,
left: 4,
bottom: 4,
backgroundColor: '#00000077',
},
thumbnailEditOverlay: {
alignItems: 'center',
justifyContent: 'center',
borderRadius: 24,
position: 'absolute',
padding: 8,
left: 80 / 2 - 32 / 2,
bottom: 4,
backgroundColor: '#00000077',
},
editIcon: {
color: Colors.White,
fontFamily: 'Inter-UI-SemiBold',
fontSize: 12,
},
uploadProgress: {
alignItems: 'center',
borderRadius: 16,
flexDirection: 'row',
position: 'absolute',
top: 8,
right: 8,
backgroundColor: '#00000077',
paddingLeft: 8,
paddingRight: 8,
paddingTop: 4,
paddingBottom: 4,
justifyContent: 'center',
},
uploadText: {
color: Colors.White,
fontFamily: 'Inter-UI-SemiBold',
fontSize: 12,
marginLeft: 4,
},
});
export default channelCreatorStyle;

View file

@ -31,6 +31,7 @@ const channelPageStyle = StyleSheet.create({
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 24,
},
infoText: {
fontFamily: 'Inter-UI-Regular',
@ -165,6 +166,9 @@ const channelPageStyle = StyleSheet.create({
claimListContent: {
paddingTop: 16,
},
actionButton: {
backgroundColor: Colors.White,
},
});
export default channelPageStyle;

View file

@ -41,8 +41,8 @@ const filePageStyle = StyleSheet.create({
fontFamily: 'Inter-UI-Regular',
textAlign: 'center',
fontSize: 20,
marginLeft: 16,
marginRight: 16,
marginLeft: 24,
marginRight: 24,
},
scrollContainer: {
flex: 1,

View file

@ -156,6 +156,10 @@ const publishStyle = StyleSheet.create({
fontSize: 16,
marginLeft: 8,
},
listEmptyText: {
fontFamily: 'Inter-UI-Regular',
fontSize: 14,
},
titleRow: {
flexDirection: 'row',
justifyContent: 'space-between',

View file

@ -1,7 +1,7 @@
import { NavigationActions, StackActions } from 'react-navigation';
import { buildURI, isURIValid, normalizeURI } from 'lbry-redux';
import { doPopDrawerStack, doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import Constants, { DrawerRoutes } from 'constants'; // eslint-disable-line node/no-deprecated-api
import Constants, { DrawerRoutes, InnerDrawerRoutes } from 'constants'; // eslint-disable-line node/no-deprecated-api
const tagNameLength = 10;
@ -156,10 +156,26 @@ export function navigateBack(navigation, drawerStack, popDrawerStack) {
const target = drawerStack[drawerStack.length > 1 ? drawerStack.length - 2 : 0];
const { route, params } = target;
navigation.goBack(navigation.state.key);
if (DrawerRoutes.indexOf(route) === -1 && isURIValid(route)) {
if (!DrawerRoutes.includes(route) && !InnerDrawerRoutes.includes(route) && isURIValid(route)) {
navigateToUri(navigation, route, null, true);
} else {
navigation.navigate({ routeName: route, params });
let targetRoute = route;
let targetParams = params;
if (InnerDrawerRoutes.includes(route)) {
if (Constants.DRAWER_ROUTE_CHANNEL_CREATOR_FORM === route) {
targetRoute = Constants.DRAWER_ROUTE_CHANNEL_CREATOR;
} else if (Constants.DRAWER_ROUTE_PUBLISH_FORM === route) {
targetRoute = Constants.DRAWER_ROUTE_PUBLISH_FORM;
}
if (targetParams) {
targetParams.displayForm = true;
} else {
targetParams = { displayForm: true };
}
}
navigation.navigate({ routeName: targetRoute, targetParams });
}
}
@ -169,14 +185,31 @@ export function dispatchNavigateBack(dispatch, nav, drawerStack) {
const target = drawerStack[drawerStack.length > 1 ? drawerStack.length - 2 : 0];
const { route } = target;
dispatch(NavigationActions.back());
if (DrawerRoutes.indexOf(route) === -1 && isURIValid(route)) {
if (!DrawerRoutes.includes(route) && !InnerDrawerRoutes.includes(route) && isURIValid(route)) {
dispatchNavigateToUri(dispatch, nav, route, true);
kauffj commented 2019-09-12 00:25:13 +02:00 (Migrated from github.com)
Review

This also smells. I'm guessing all of this is to properly retain navigation on channel creation and publishing, especially in the cases where you might be auto-navigated/directed to those areas?

I suspect there's a design that's a little cleaner here. Ping me tomorrow and we can get on a call to discuss.

This also smells. I'm guessing all of this is to properly retain navigation on channel creation and publishing, especially in the cases where you might be auto-navigated/directed to those areas? I suspect there's a design that's a little cleaner here. Ping me tomorrow and we can get on a call to discuss.
akinwale commented 2019-09-12 08:30:08 +02:00 (Migrated from github.com)
Review

The drawer stack is a LIFO structure used for maintaining the navigation state, since the drawer navigator doesn't properly support back navigation out of the box.

In retrospect, the ideal solution here would be to use a nested StackNavigator for these pages (list / selector and the form), but I had already started down this road and didn't feel like separating into multiple components).

The drawer stack is a LIFO structure used for maintaining the navigation state, since the drawer navigator doesn't properly support back navigation out of the box. In retrospect, the ideal solution here would be to use a nested StackNavigator for these pages (list / selector and the form), but I had already started down this road and didn't feel like separating into multiple components).
} else {
const newTarget = drawerStack[drawerStack.length > 1 ? drawerStack.length - 2 : 0];
let targetRoute = newTarget.route;
let targetParams = newTarget.params;
if (InnerDrawerRoutes.includes(targetRoute)) {
if (Constants.DRAWER_ROUTE_CHANNEL_CREATOR_FORM === route) {
targetRoute = Constants.DRAWER_ROUTE_CHANNEL_CREATOR;
} else if (Constants.DRAWER_ROUTE_PUBLISH_FORM === route) {
targetRoute = Constants.DRAWER_ROUTE_PUBLISH_FORM;
}
if (targetParams) {
targetParams.displayForm = true;
} else {
targetParams = { displayForm: true };
}
}
const navigateAction = NavigationActions.navigate({
routeName: newTarget.route,
params: newTarget.params,
routeName: targetRoute,
params: targetParams,
});
dispatch(navigateAction);
}
}
@ -256,3 +289,42 @@ export function transformUrl(url) {
export function __(str) {
return str;
}
export function uploadImageAsset(filePath, success, failure) {
const makeid = () => {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 24; i += 1) text += possible.charAt(Math.floor(Math.random() * 62));
return text;
};
const fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
let fileExt = fileName.indexOf('.') > -1 ? fileName.substring(fileName.lastIndexOf('.') + 1).trim() : 0;
if (!fileExt) {
fileExt = 'jpg'; // default to jpg
}
const fileType = `image/${fileExt}`;
const data = new FormData();
const name = makeid();
data.append('name', name);
data.append('file', { uri: 'file://' + filePath, type: fileType, name: fileName });
return fetch('https://spee.ch/api/claim/publish', {
method: 'POST',
body: data,
})
.then(response => response.json())
.then(json => {
if (json.success) {
if (success) {
success({ url: `${json.data.url}.${fileExt}` });
}
}
})
.catch(err => {
if (failure) {
failure(err.message ? err.message : 'The image failed to upload.');
kauffj commented 2019-09-04 00:17:19 +02:00 (Migrated from github.com)
Review

do we know anything about the user at the time this is happening? any ability to get a partial channel name or something else in here aside from pure random?

do we know anything about the user at the time this is happening? any ability to get a partial channel name or something else in here aside from pure random?
}
});
}