Channel creation and editing #36
14
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
if (Constants.ORIENTATION_VERTICAL === orientation) {
|
||||
return (
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
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 {
|
||||
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()),
|
||||
|
|
|
@ -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} />;
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
@ -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 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 });
|
||||
}
|
||||
|
||||
|
|
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: {
|
||||
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;
|
||||
|
|
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,
|
||||
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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
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 {
|
||||
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.');
|
||||
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):