Channel creation and editing #36
14
package-lock.json
generated
|
@ -5574,8 +5574,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lbry-redux": {
|
"lbry-redux": {
|
||||||
"version": "github:lbryio/lbry-redux#362d764c4c0de23032b6871b4d54207dc548028c",
|
"version": "github:lbryio/lbry-redux#3133ea60b0302c162f7b6f67cc858997f1d2ab52",
|
||||||
"from": "github:lbryio/lbry-redux#362d764c4c0de23032b6871b4d54207dc548028c",
|
"from": "github:lbryio/lbry-redux#3133ea60b0302c162f7b6f67cc858997f1d2ab52",
|
||||||
"requires": {
|
"requires": {
|
||||||
"proxy-polyfill": "0.1.6",
|
"proxy-polyfill": "0.1.6",
|
||||||
"reselect": "^3.0.0",
|
"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": {
|
"react-native-exception-handler": {
|
||||||
"version": "2.9.0",
|
"version": "2.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-exception-handler/-/react-native-exception-handler-2.9.0.tgz",
|
"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"
|
"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": {
|
"semver": {
|
||||||
"version": "5.7.1",
|
"version": "5.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"base-64": "^0.1.0",
|
"base-64": "^0.1.0",
|
||||||
"@expo/vector-icons": "^8.1.0",
|
"@expo/vector-icons": "^8.1.0",
|
||||||
"gfycat-style-urls": "^1.0.3",
|
"gfycat-style-urls": "^1.0.3",
|
||||||
"lbry-redux": "lbryio/lbry-redux#362d764c4c0de23032b6871b4d54207dc548028c",
|
"lbry-redux": "lbryio/lbry-redux#3133ea60b0302c162f7b6f67cc858997f1d2ab52",
|
||||||
"lbryinc": "lbryio/lbryinc#b9f354ae50bd57691765a7d042c5054167878bf4",
|
"lbryinc": "lbryio/lbryinc#b9f354ae50bd57691765a7d042c5054167878bf4",
|
||||||
"lodash": ">=4.17.11",
|
"lodash": ">=4.17.11",
|
||||||
"merge": ">=1.2.1",
|
"merge": ">=1.2.1",
|
||||||
|
@ -22,7 +22,6 @@
|
||||||
"@react-native-community/async-storage": "^1.5.1",
|
"@react-native-community/async-storage": "^1.5.1",
|
||||||
"react-native-camera": "^2.11.1",
|
"react-native-camera": "^2.11.1",
|
||||||
"react-native-country-picker-modal": "^0.6.2",
|
"react-native-country-picker-modal": "^0.6.2",
|
||||||
"react-native-document-picker": "^3.2.4",
|
|
||||||
"react-native-exception-handler": "2.9.0",
|
"react-native-exception-handler": "2.9.0",
|
||||||
"react-native-fast-image": "^6.1.1",
|
"react-native-fast-image": "^6.1.1",
|
||||||
"react-native-fs": "^2.13.3",
|
"react-native-fs": "^2.13.3",
|
||||||
|
@ -44,7 +43,8 @@
|
||||||
"redux-persist-transform-compress": "^4.2.0",
|
"redux-persist-transform-compress": "^4.2.0",
|
||||||
"redux-persist-transform-filter": "0.0.18",
|
"redux-persist-transform-filter": "0.0.18",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"rn-fetch-blob": "0.10.15"
|
"rn-fetch-blob": "0.10.15",
|
||||||
|
"seedrandom": "3.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.5.4",
|
"@babel/core": "^7.5.4",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import AboutPage from 'page/about';
|
import AboutPage from 'page/about';
|
||||||
|
import ChannelCreatorPage from 'page/channelCreator';
|
||||||
import DiscoverPage from 'page/discover';
|
import DiscoverPage from 'page/discover';
|
||||||
import DownloadsPage from 'page/downloads';
|
import DownloadsPage from 'page/downloads';
|
||||||
import DrawerContent from 'component/drawerContent';
|
import DrawerContent from 'component/drawerContent';
|
||||||
|
@ -159,6 +160,12 @@ const drawer = createDrawerNavigator(
|
||||||
drawerIcon: ({ tintColor }) => <Icon name="wallet" size={drawerIconSize} style={{ color: tintColor }} />,
|
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: {
|
Publish: {
|
||||||
screen: PublishPage,
|
screen: PublishPage,
|
||||||
navigationOptions: {
|
navigationOptions: {
|
||||||
|
|
|
@ -13,10 +13,15 @@ export default class ChannelIconItem extends React.PureComponent {
|
||||||
autothumbStyle.autothumbBlue,
|
autothumbStyle.autothumbBlue,
|
||||||
autothumbStyle.autothumbLightBlue,
|
autothumbStyle.autothumbLightBlue,
|
||||||
autothumbStyle.autothumbCyan,
|
autothumbStyle.autothumbCyan,
|
||||||
autothumbStyle.autothumbTeal,
|
|
||||||
autothumbStyle.autothumbGreen,
|
autothumbStyle.autothumbGreen,
|
||||||
autothumbStyle.autothumbYellow,
|
autothumbStyle.autothumbYellow,
|
||||||
autothumbStyle.autothumbOrange,
|
autothumbStyle.autothumbOrange,
|
||||||
|
autothumbStyle.autothumbDeepPurple,
|
||||||
|
autothumbStyle.autothumbAmber,
|
||||||
|
autothumbStyle.autothumbLime,
|
||||||
|
autothumbStyle.autothumbLightGreen,
|
||||||
|
autothumbStyle.autothumbDeepOrange,
|
||||||
|
autothumbStyle.autothumbBrown,
|
||||||
];
|
];
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
|
|
@ -204,7 +204,11 @@ class ClaimList extends React.PureComponent {
|
||||||
|
|
||||||
const options = this.buildClaimSearchOptions();
|
const options = this.buildClaimSearchOptions();
|
||||||
const claimSearchKey = createNormalizedClaimSearchKey(options);
|
const claimSearchKey = createNormalizedClaimSearchKey(options);
|
||||||
const uris = claimSearchByQuery[claimSearchKey];
|
let uris = claimSearchByQuery[claimSearchKey];
|
||||||
|
|
||||||
|
if (uris) {
|
||||||
|
uris = uris.filter(uri => uri && uri.length > 0);
|
||||||
|
}
|
||||||
|
|||||||
|
|
||||||
if (Constants.ORIENTATION_VERTICAL === orientation) {
|
if (Constants.ORIENTATION_VERTICAL === orientation) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -12,6 +12,7 @@ const groupedMenuItems = {
|
||||||
{ icon: 'globe-americas', label: 'All content', route: Constants.DRAWER_ROUTE_TRENDING },
|
{ icon: 'globe-americas', label: 'All content', route: Constants.DRAWER_ROUTE_TRENDING },
|
||||||
],
|
],
|
||||||
'Your content': [
|
'Your content': [
|
||||||
|
{ icon: 'at', label: 'Channels', route: Constants.DRAWER_ROUTE_CHANNEL_CREATOR },
|
||||||
{ icon: 'download', label: 'Library', route: Constants.DRAWER_ROUTE_MY_LBRY },
|
{ icon: 'download', label: 'Library', route: Constants.DRAWER_ROUTE_MY_LBRY },
|
||||||
{ icon: 'cloud-upload-alt', label: 'Publishes', route: Constants.DRAWER_ROUTE_PUBLISHES },
|
{ icon: 'cloud-upload-alt', label: 'Publishes', route: Constants.DRAWER_ROUTE_PUBLISHES },
|
||||||
{ icon: 'upload', label: 'New publish', route: Constants.DRAWER_ROUTE_PUBLISH },
|
{ icon: 'upload', label: 'New publish', route: Constants.DRAWER_ROUTE_PUBLISH },
|
||||||
|
|
|
@ -67,11 +67,12 @@ export default class TagSearch extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { name, style, type, selectedTags = [], showNsfwTags } = this.props;
|
const { editable, name, style, type, selectedTags = [], showNsfwTags } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
editable={editable}
|
||||||
style={tagStyle.searchInput}
|
style={tagStyle.searchInput}
|
||||||
placeholder={'Search for more tags'}
|
placeholder={'Search for more tags'}
|
||||||
underlineColorAndroid={Colors.NextLbryGreen}
|
underlineColorAndroid={Colors.NextLbryGreen}
|
||||||
|
|
|
@ -25,6 +25,9 @@ const Constants = {
|
||||||
PHASE_DETAILS: 'details',
|
PHASE_DETAILS: 'details',
|
||||||
PHASE_PUBLISH: 'publish',
|
PHASE_PUBLISH: 'publish',
|
||||||
|
|
||||||
|
PHASE_LIST: 'list',
|
||||||
|
PHASE_NEW: 'create',
|
||||||
|
|
||||||
CONTENT_TAB: 'content',
|
CONTENT_TAB: 'content',
|
||||||
ABOUT_TAB: 'about',
|
ABOUT_TAB: 'about',
|
||||||
|
|
||||||
|
@ -56,6 +59,11 @@ const Constants = {
|
||||||
ACTION_SORT_BY_ITEM_CHANGED: 'SORT_BY_ITEM_CHANGED',
|
ACTION_SORT_BY_ITEM_CHANGED: 'SORT_BY_ITEM_CHANGED',
|
||||||
ACTION_TIME_ITEM_CHANGED: 'TIME_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_HORIZONTAL: 'horizontal',
|
||||||
ORIENTATION_VERTICAL: 'vertical',
|
ORIENTATION_VERTICAL: 'vertical',
|
||||||
|
|
||||||
|
@ -69,6 +77,7 @@ const Constants = {
|
||||||
DRAWER_ROUTE_SUBSCRIPTIONS: 'Subscriptions',
|
DRAWER_ROUTE_SUBSCRIPTIONS: 'Subscriptions',
|
||||||
DRAWER_ROUTE_MY_LBRY: 'Downloads',
|
DRAWER_ROUTE_MY_LBRY: 'Downloads',
|
||||||
DRAWER_ROUTE_PUBLISH: 'Publish',
|
DRAWER_ROUTE_PUBLISH: 'Publish',
|
||||||
|
DRAWER_ROUTE_PUBLISH_FORM: 'PublishForm',
|
||||||
DRAWER_ROUTE_PUBLISHES: 'Publishes',
|
DRAWER_ROUTE_PUBLISHES: 'Publishes',
|
||||||
DRAWER_ROUTE_REWARDS: 'Rewards',
|
DRAWER_ROUTE_REWARDS: 'Rewards',
|
||||||
DRAWER_ROUTE_WALLET: 'Wallet',
|
DRAWER_ROUTE_WALLET: 'Wallet',
|
||||||
|
@ -77,6 +86,8 @@ const Constants = {
|
||||||
DRAWER_ROUTE_SEARCH: 'Search',
|
DRAWER_ROUTE_SEARCH: 'Search',
|
||||||
DRAWER_ROUTE_TRANSACTION_HISTORY: 'TransactionHistory',
|
DRAWER_ROUTE_TRANSACTION_HISTORY: 'TransactionHistory',
|
||||||
DRAWER_ROUTE_TAG: 'Tag',
|
DRAWER_ROUTE_TAG: 'Tag',
|
||||||
|
DRAWER_ROUTE_CHANNEL_CREATOR: 'ChannelCreator',
|
||||||
|
DRAWER_ROUTE_CHANNEL_CREATOR_FORM: 'ChannnelCreatorForm',
|
||||||
|
|
||||||
FULL_ROUTE_NAME_DISCOVER: 'DiscoverStack',
|
FULL_ROUTE_NAME_DISCOVER: 'DiscoverStack',
|
||||||
FULL_ROUTE_NAME_WALLET: 'WalletStack',
|
FULL_ROUTE_NAME_WALLET: 'WalletStack',
|
||||||
|
@ -146,4 +157,8 @@ export const DrawerRoutes = [
|
||||||
Constants.DRAWER_ROUTE_ABOUT,
|
Constants.DRAWER_ROUTE_ABOUT,
|
||||||
Constants.DRAWER_ROUTE_SEARCH,
|
Constants.DRAWER_ROUTE_SEARCH,
|
||||||
Constants.DRAWER_ROUTE_TRANSACTION_HISTORY,
|
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];
|
||||||
|
|
|
@ -37,6 +37,7 @@ import FilesystemStorage from 'redux-persist-filesystem-storage';
|
||||||
import createCompressor from 'redux-persist-transform-compress';
|
import createCompressor from 'redux-persist-transform-compress';
|
||||||
import createFilter from 'redux-persist-transform-filter';
|
import createFilter from 'redux-persist-transform-filter';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import formReducer from 'redux/reducers/form';
|
||||||
import drawerReducer from 'redux/reducers/drawer';
|
import drawerReducer from 'redux/reducers/drawer';
|
||||||
import settingsReducer from 'redux/reducers/settings';
|
import settingsReducer from 'redux/reducers/settings';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
|
@ -109,6 +110,7 @@ const reducers = persistCombineReducers(persistOptions, {
|
||||||
file: fileReducer,
|
file: fileReducer,
|
||||||
fileInfo: fileInfoReducer,
|
fileInfo: fileInfoReducer,
|
||||||
filtered: filteredReducer,
|
filtered: filteredReducer,
|
||||||
|
form: formReducer,
|
||||||
homepage: homepageReducer,
|
homepage: homepageReducer,
|
||||||
nav: navigatorReducer,
|
nav: navigatorReducer,
|
||||||
notifications: notificationsReducer,
|
notifications: notificationsReducer,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doFetchClaimsByChannel, makeSelectClaimForUri } from 'lbry-redux';
|
import { makeSelectClaimForUri, selectMyChannelClaims } from 'lbry-redux';
|
||||||
import { doPopDrawerStack } from 'redux/actions/drawer';
|
import { doPopDrawerStack } from 'redux/actions/drawer';
|
||||||
import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
|
import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
|
||||||
import { selectDrawerStack } from 'redux/selectors/drawer';
|
import { selectDrawerStack } from 'redux/selectors/drawer';
|
||||||
|
@ -7,6 +7,7 @@ import { selectSortByItem, selectTimeItem } from 'redux/selectors/settings';
|
||||||
import ChannelPage from './view';
|
import ChannelPage from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
channels: selectMyChannelClaims(state),
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
drawerStack: selectDrawerStack(state),
|
drawerStack: selectDrawerStack(state),
|
||||||
sortByItem: selectSortByItem(state),
|
sortByItem: selectSortByItem(state),
|
||||||
|
@ -14,7 +15,6 @@ const select = (state, props) => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
|
|
||||||
popDrawerStack: () => dispatch(doPopDrawerStack()),
|
popDrawerStack: () => dispatch(doPopDrawerStack()),
|
||||||
setSortByItem: item => dispatch(doSetSortByItem(item)),
|
setSortByItem: item => dispatch(doSetSortByItem(item)),
|
||||||
setTimeItem: item => dispatch(doSetTimeItem(item)),
|
setTimeItem: item => dispatch(doSetTimeItem(item)),
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
Alert,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
Image,
|
Image,
|
||||||
NativeModules,
|
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() {
|
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 { name, permanent_url: permanentUrl } = claim;
|
||||||
const { autoStyle, showSortPicker, showTimePicker } = this.state;
|
const { autoStyle, showSortPicker, showTimePicker } = this.state;
|
||||||
|
const ownedChannel = channels ? channels.map(channel => channel.permanent_url).includes(permanentUrl) : false;
|
||||||
|
|
||||||
let thumbnailUrl,
|
let thumbnailUrl,
|
||||||
coverUrl,
|
coverUrl,
|
||||||
|
@ -218,12 +255,32 @@ class ChannelPage extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={channelPageStyle.subscribeButtonContainer}>
|
<View style={channelPageStyle.subscribeButtonContainer}>
|
||||||
<SubscribeButton style={channelPageStyle.subscribeButton} uri={fullUri} name={name} />
|
{ownedChannel && (
|
||||||
<SubscribeNotificationButton
|
<Button
|
||||||
style={[channelPageStyle.subscribeButton, channelPageStyle.bellButton]}
|
style={channelPageStyle.actionButton}
|
||||||
uri={fullUri}
|
theme={'light'}
|
||||||
name={name}
|
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>
|
</View>
|
||||||
|
|
||||||
|
|
49
src/page/channelCreator/index.js
Normal 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);
|
1042
src/page/channelCreator/view.js
Normal file
|
@ -1,6 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
doFetchFileInfo,
|
doFetchFileInfo,
|
||||||
|
doFetchChannelListMine,
|
||||||
doFetchClaimListMine,
|
doFetchClaimListMine,
|
||||||
doFileGet,
|
doFileGet,
|
||||||
doPurchaseUri,
|
doPurchaseUri,
|
||||||
|
@ -19,6 +20,7 @@ import {
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
makeSelectTitleForUri,
|
makeSelectTitleForUri,
|
||||||
selectBalance,
|
selectBalance,
|
||||||
|
selectMyChannelClaims,
|
||||||
selectMyClaimUrisWithoutChannels,
|
selectMyClaimUrisWithoutChannels,
|
||||||
selectPurchasedUris,
|
selectPurchasedUris,
|
||||||
selectFailedPurchaseUris,
|
selectFailedPurchaseUris,
|
||||||
|
@ -46,6 +48,7 @@ const select = (state, props) => {
|
||||||
return {
|
return {
|
||||||
balance: selectBalance(state),
|
balance: selectBalance(state),
|
||||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||||
|
channels: selectMyChannelClaims(state),
|
||||||
claim: makeSelectClaimForUri(selectProps.uri)(state),
|
claim: makeSelectClaimForUri(selectProps.uri)(state),
|
||||||
drawerStack: selectDrawerStack(state),
|
drawerStack: selectDrawerStack(state),
|
||||||
isResolvingUri: makeSelectIsUriResolving(selectProps.uri)(state),
|
isResolvingUri: makeSelectIsUriResolving(selectProps.uri)(state),
|
||||||
|
@ -75,6 +78,7 @@ const perform = dispatch => ({
|
||||||
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
|
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
|
||||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
||||||
fetchMyClaims: () => dispatch(doFetchClaimListMine()),
|
fetchMyClaims: () => dispatch(doFetchClaimListMine()),
|
||||||
|
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
|
||||||
fileGet: (uri, saveFile) => dispatch(doFileGet(uri, saveFile)),
|
fileGet: (uri, saveFile) => dispatch(doFileGet(uri, saveFile)),
|
||||||
notify: data => dispatch(doToast(data)),
|
notify: data => dispatch(doToast(data)),
|
||||||
popDrawerStack: () => dispatch(doPopDrawerStack()),
|
popDrawerStack: () => dispatch(doPopDrawerStack()),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry, normalizeURI } from 'lbry-redux';
|
import { Lbry, normalizeURI, parseURI } from 'lbry-redux';
|
||||||
import { Lbryio } from 'lbryinc';
|
import { Lbryio } from 'lbryinc';
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
@ -100,7 +100,7 @@ class FilePage extends React.PureComponent {
|
||||||
DeviceEventEmitter.addListener('onDownloadUpdated', this.handleDownloadUpdated);
|
DeviceEventEmitter.addListener('onDownloadUpdated', this.handleDownloadUpdated);
|
||||||
DeviceEventEmitter.addListener('onDownloadCompleted', this.handleDownloadCompleted);
|
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;
|
const { uri, uriVars } = navigation.state.params;
|
||||||
this.setState({ uri, uriVars });
|
this.setState({ uri, uriVars });
|
||||||
|
|
||||||
|
@ -108,6 +108,7 @@ class FilePage extends React.PureComponent {
|
||||||
|
|
||||||
this.fetchFileInfo(this.props);
|
this.fetchFileInfo(this.props);
|
||||||
this.fetchCostInfo(this.props);
|
this.fetchCostInfo(this.props);
|
||||||
|
fetchChannelListMine();
|
||||||
|
|
||||||
if (NativeModules.Firebase) {
|
if (NativeModules.Firebase) {
|
||||||
NativeModules.Firebase.track('open_file_page', { uri: uri });
|
NativeModules.Firebase.track('open_file_page', { uri: uri });
|
||||||
|
@ -567,6 +568,7 @@ class FilePage extends React.PureComponent {
|
||||||
const {
|
const {
|
||||||
balance,
|
balance,
|
||||||
claim,
|
claim,
|
||||||
|
channels,
|
||||||
channelUri,
|
channelUri,
|
||||||
costInfo,
|
costInfo,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
|
@ -585,6 +587,10 @@ class FilePage extends React.PureComponent {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { uri, autoplay } = navigation.state.params;
|
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;
|
let innerContent = null;
|
||||||
if ((isResolvingUri && !claim) || !claim) {
|
if ((isResolvingUri && !claim) || !claim) {
|
||||||
return (
|
return (
|
||||||
|
@ -597,7 +603,14 @@ class FilePage extends React.PureComponent {
|
||||||
)}
|
)}
|
||||||
{claim === null && !isResolvingUri && (
|
{claim === null && !isResolvingUri && (
|
||||||
<View style={filePageStyle.container}>
|
<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>
|
</View>
|
||||||
)}
|
)}
|
||||||
<UriBar value={uri} navigation={navigation} />
|
<UriBar value={uri} navigation={navigation} />
|
||||||
|
@ -606,7 +619,7 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (claim) {
|
if (claim) {
|
||||||
if (claim && claim.name.length && claim.name[0] === '@') {
|
if (isChannel) {
|
||||||
return <ChannelPage uri={uri} navigation={navigation} />;
|
return <ChannelPage uri={uri} navigation={navigation} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -523,6 +523,8 @@ class PublishPage extends React.PureComponent {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleUploadPressed = () => {};
|
||||||
|
|
||||||
getRandomFileId = () => {
|
getRandomFileId = () => {
|
||||||
// generate a random id for a photo or recorded video between 1 and 20 (for creating thumbnails)
|
// 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;
|
const id = Math.floor(Math.random() * (20 - 2)) + 1;
|
||||||
|
@ -824,6 +826,7 @@ class PublishPage extends React.PureComponent {
|
||||||
)}
|
)}
|
||||||
<TextInput
|
<TextInput
|
||||||
editable={this.state.canPublish && !this.state.publishStarted}
|
editable={this.state.canPublish && !this.state.publishStarted}
|
||||||
|
multiline
|
||||||
placeholder={this.state.descriptionFocused ? '' : 'Description'}
|
placeholder={this.state.descriptionFocused ? '' : 'Description'}
|
||||||
style={publishStyle.inputText}
|
style={publishStyle.inputText}
|
||||||
value={this.state.description}
|
value={this.state.description}
|
||||||
|
|
|
@ -33,6 +33,15 @@ class PublishesPage extends React.PureComponent {
|
||||||
this.onComponentFocused();
|
this.onComponentFocused();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
const { currentRoute } = nextProps;
|
||||||
|
const { currentRoute: prevRoute } = this.props;
|
||||||
|
|
||||||
|
if (Constants.DRAWER_ROUTE_PUBLISHES === currentRoute && currentRoute !== prevRoute) {
|
||||||
|
this.onComponentFocused();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onComponentFocused = () => {
|
onComponentFocused = () => {
|
||||||
const { checkPendingPublishes, fetchMyClaims, pushDrawerStack, setPlayerVisible } = this.props;
|
const { checkPendingPublishes, fetchMyClaims, pushDrawerStack, setPlayerVisible } = this.props;
|
||||||
pushDrawerStack();
|
pushDrawerStack();
|
||||||
|
|
23
src/redux/actions/form.js
Normal 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,
|
||||||
|
});
|
|
@ -16,7 +16,19 @@ reducers[Constants.ACTION_PUSH_DRAWER_STACK] = (state, action) => {
|
||||||
const { routeName, params } = action.data;
|
const { routeName, params } = action.data;
|
||||||
const newStack = state.stack.slice();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
![]() 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:
![]() On the publish page, we have two screens: the gallery selector and the publish form. 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 });
|
newStack.push({ route: routeName, params });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
49
src/redux/reducers/form.js
Normal 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;
|
||||||
|
}
|
13
src/redux/selectors/form.js
Normal 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 || {}
|
||||||
|
);
|
|
@ -4,6 +4,9 @@ const autothumbStyle = StyleSheet.create({
|
||||||
autothumbPurple: {
|
autothumbPurple: {
|
||||||
backgroundColor: '#9c27b0',
|
backgroundColor: '#9c27b0',
|
||||||
},
|
},
|
||||||
|
autothumbDeepPurple: {
|
||||||
|
backgroundColor: '#5e35b1',
|
||||||
|
},
|
||||||
autothumbRed: {
|
autothumbRed: {
|
||||||
backgroundColor: '#e53935',
|
backgroundColor: '#e53935',
|
||||||
},
|
},
|
||||||
|
@ -34,6 +37,21 @@ const autothumbStyle = StyleSheet.create({
|
||||||
autothumbOrange: {
|
autothumbOrange: {
|
||||||
backgroundColor: '#ffa726',
|
backgroundColor: '#ffa726',
|
||||||
},
|
},
|
||||||
|
autothumbAmber: {
|
||||||
|
backgroundColor: '#ffb300',
|
||||||
|
},
|
||||||
|
autothumbLime: {
|
||||||
|
backgroundColor: '#c0ca33',
|
||||||
|
},
|
||||||
|
autothumbLightGreen: {
|
||||||
|
backgroundColor: '#7cb342',
|
||||||
|
},
|
||||||
|
autothumbDeepOrange: {
|
||||||
|
backgroundColor: '#f4511e',
|
||||||
|
},
|
||||||
|
autothumbBrown: {
|
||||||
|
backgroundColor: '#6d4c41',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default autothumbStyle;
|
export default autothumbStyle;
|
||||||
|
|
261
src/styles/channelCreator.js
Normal 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;
|
|
@ -31,6 +31,7 @@ const channelPageStyle = StyleSheet.create({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
padding: 24,
|
||||||
},
|
},
|
||||||
infoText: {
|
infoText: {
|
||||||
fontFamily: 'Inter-UI-Regular',
|
fontFamily: 'Inter-UI-Regular',
|
||||||
|
@ -165,6 +166,9 @@ const channelPageStyle = StyleSheet.create({
|
||||||
claimListContent: {
|
claimListContent: {
|
||||||
paddingTop: 16,
|
paddingTop: 16,
|
||||||
},
|
},
|
||||||
|
actionButton: {
|
||||||
|
backgroundColor: Colors.White,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default channelPageStyle;
|
export default channelPageStyle;
|
||||||
|
|
|
@ -41,8 +41,8 @@ const filePageStyle = StyleSheet.create({
|
||||||
fontFamily: 'Inter-UI-Regular',
|
fontFamily: 'Inter-UI-Regular',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
marginLeft: 16,
|
marginLeft: 24,
|
||||||
marginRight: 16,
|
marginRight: 24,
|
||||||
},
|
},
|
||||||
scrollContainer: {
|
scrollContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|
|
@ -156,6 +156,10 @@ const publishStyle = StyleSheet.create({
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
marginLeft: 8,
|
marginLeft: 8,
|
||||||
},
|
},
|
||||||
|
listEmptyText: {
|
||||||
|
fontFamily: 'Inter-UI-Regular',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
titleRow: {
|
titleRow: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { NavigationActions, StackActions } from 'react-navigation';
|
import { NavigationActions, StackActions } from 'react-navigation';
|
||||||
import { buildURI, isURIValid, normalizeURI } from 'lbry-redux';
|
import { buildURI, isURIValid, normalizeURI } from 'lbry-redux';
|
||||||
import { doPopDrawerStack, doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
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;
|
const tagNameLength = 10;
|
||||||
|
|
||||||
|
@ -156,10 +156,26 @@ export function navigateBack(navigation, drawerStack, popDrawerStack) {
|
||||||
const target = drawerStack[drawerStack.length > 1 ? drawerStack.length - 2 : 0];
|
const target = drawerStack[drawerStack.length > 1 ? drawerStack.length - 2 : 0];
|
||||||
const { route, params } = target;
|
const { route, params } = target;
|
||||||
navigation.goBack(navigation.state.key);
|
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);
|
navigateToUri(navigation, route, null, true);
|
||||||
} else {
|
} 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 target = drawerStack[drawerStack.length > 1 ? drawerStack.length - 2 : 0];
|
||||||
const { route } = target;
|
const { route } = target;
|
||||||
dispatch(NavigationActions.back());
|
dispatch(NavigationActions.back());
|
||||||
if (DrawerRoutes.indexOf(route) === -1 && isURIValid(route)) {
|
if (!DrawerRoutes.includes(route) && !InnerDrawerRoutes.includes(route) && isURIValid(route)) {
|
||||||
dispatchNavigateToUri(dispatch, nav, route, true);
|
dispatchNavigateToUri(dispatch, nav, route, true);
|
||||||
![]() 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.
![]() 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 {
|
} else {
|
||||||
const newTarget = drawerStack[drawerStack.length > 1 ? drawerStack.length - 2 : 0];
|
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({
|
const navigateAction = NavigationActions.navigate({
|
||||||
routeName: newTarget.route,
|
routeName: targetRoute,
|
||||||
params: newTarget.params,
|
params: targetParams,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(navigateAction);
|
dispatch(navigateAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,3 +289,42 @@ export function transformUrl(url) {
|
||||||
export function __(str) {
|
export function __(str) {
|
||||||
return 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.');
|
||||||
![]() 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?
|
|||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
(update to 0.40?)
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):