update to sdk 0.38.0. add tags / tag selection to publish.

This commit is contained in:
Akinwale Ariwodola 2019-07-03 11:01:13 +01:00
parent 44c58eb8c7
commit 2beb83d076
28 changed files with 546 additions and 113 deletions

View file

@ -25,7 +25,7 @@ build apk:
- cp -f $CI_PROJECT_DIR/scripts/mangled-glibc-syscalls.h ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-21/arch-arm/usr/include/crystax/bionic/libc/include/sys/mangled-glibc-syscalls.h - cp -f $CI_PROJECT_DIR/scripts/mangled-glibc-syscalls.h ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-21/arch-arm/usr/include/crystax/bionic/libc/include/sys/mangled-glibc-syscalls.h
- rm ~/.buildozer/android/crystax-ndk-10.3.2-linux-x86_64.tar.xz - rm ~/.buildozer/android/crystax-ndk-10.3.2-linux-x86_64.tar.xz
- git secret reveal - git secret reveal
- mv buildozer.spec.travis buildozer.spec - mv buildozer.spec.ci buildozer.spec
- "./release.sh | grep -Fv -e 'working:' -e 'copy' -e 'Compiling' --line-buffered" - "./release.sh | grep -Fv -e 'working:' -e 'copy' -e 'Compiling' --line-buffered"
- cp $CI_PROJECT_DIR/bin/browser-$BUILD_VERSION-release.apk /dev/null - cp $CI_PROJECT_DIR/bin/browser-$BUILD_VERSION-release.apk /dev/null

4
app/package-lock.json generated
View file

@ -5558,8 +5558,8 @@
} }
}, },
"lbry-redux": { "lbry-redux": {
"version": "github:lbryio/lbry-redux#b998577698d703714b0aa0c0e80c991902725a5c", "version": "github:lbryio/lbry-redux#9a676ee311d573b84d11f402d918aeee77be76e1",
"from": "github:lbryio/lbry-redux#publishing", "from": "github:lbryio/lbry-redux",
"requires": { "requires": {
"proxy-polyfill": "0.1.6", "proxy-polyfill": "0.1.6",
"reselect": "^3.0.0", "reselect": "^3.0.0",

View file

@ -11,7 +11,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#publishing", "lbry-redux": "lbryio/lbry-redux",
"lbryinc": "lbryio/lbryinc", "lbryinc": "lbryio/lbryinc",
"lodash": ">=4.17.11", "lodash": ">=4.17.11",
"merge": ">=1.2.1", "merge": ">=1.2.1",

View file

@ -74,13 +74,7 @@ export default class ChannelSelector extends React.PureComponent {
newChannelName = newChannelName.slice(1); newChannelName = newChannelName.slice(1);
} }
let newChannelNameError;
if (newChannelName.length > 1 && !isNameValid(newChannelName.substr(1), false)) {
notify({ message: 'LBRY channel names must contain only letters, numbers and dashes.' });
}
this.setState({ this.setState({
newChannelNameError,
newChannelName, newChannelName,
}); });
}; };
@ -88,8 +82,8 @@ export default class ChannelSelector extends React.PureComponent {
handleNewChannelBidChange = newChannelBid => { handleNewChannelBidChange = newChannelBid => {
const { balance, notify } = this.props; const { balance, notify } = this.props;
let newChannelBidError; let newChannelBidError;
if (newChannelBid === 0) { if (newChannelBid <= 0) {
newChannelBidError = __('Your deposit cannot be 0'); newChannelBidError = __('Please enter a deposit above 0');
} else if (newChannelBid === balance) { } else if (newChannelBid === balance) {
newChannelBidError = __('Please decrease your deposit to account for transaction fees'); newChannelBidError = __('Please decrease your deposit to account for transaction fees');
} else if (newChannelBid > balance) { } else if (newChannelBid > balance) {
@ -109,7 +103,7 @@ export default class ChannelSelector extends React.PureComponent {
const { newChannelBid, newChannelName } = this.state; const { newChannelBid, newChannelName } = this.state;
if (newChannelName.trim().length === 0 || !isNameValid(newChannelName.substr(1), false)) { if (newChannelName.trim().length === 0 || !isNameValid(newChannelName.substr(1), false)) {
notify({ message: 'LBRY channel names must contain only letters, numbers and dashes.' }); notify({ message: 'Your channel name contains invalid characters.' });
return; return;
} }
@ -198,6 +192,9 @@ export default class ChannelSelector extends React.PureComponent {
{this.state.showCreateChannel && ( {this.state.showCreateChannel && (
<View style={channelSelectorStyle.createChannelContainer}> <View style={channelSelectorStyle.createChannelContainer}>
<View style={channelSelectorStyle.channelInputContainer}>
<Text style={channelSelectorStyle.channelAt}>@</Text>
<TextInput <TextInput
style={channelSelectorStyle.channelNameInput} style={channelSelectorStyle.channelNameInput}
value={this.state.newChannelName} value={this.state.newChannelName}
@ -205,6 +202,7 @@ export default class ChannelSelector extends React.PureComponent {
placeholder={'Channel name'} placeholder={'Channel name'}
underlineColorAndroid={Colors.NextLbryGreen} underlineColorAndroid={Colors.NextLbryGreen}
/> />
</View>
<View style={channelSelectorStyle.bidRow}> <View style={channelSelectorStyle.bidRow}>
<Text style={channelSelectorStyle.label}>Deposit</Text> <Text style={channelSelectorStyle.label}>Deposit</Text>
<TextInput <TextInput

View file

@ -62,7 +62,8 @@ class FileItem extends React.PureComponent {
const uri = normalizeURI(this.props.uri); const uri = normalizeURI(this.props.uri);
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw; const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id); const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
const channelName = claim ? claim.channel_name : null; const signingChannel = claim ? claim.signing_channel : null;
const channelName = signingChannel ? signingChannel.name : null;
const channelClaimId = const channelClaimId =
claim && claim.value && claim.value.publisherSignature && claim.value.publisherSignature.certificateId; claim && claim.value && claim.value.publisherSignature && claim.value.publisherSignature.certificateId;
const fullChannelUri = channelClaimId ? `${channelName}#${channelClaimId}` : channelName; const fullChannelUri = channelClaimId ? `${channelName}#${channelClaimId}` : channelName;

View file

@ -63,7 +63,8 @@ class FileListItem extends React.PureComponent {
let name, channel, height, channelClaimId, fullChannelUri; let name, channel, height, channelClaimId, fullChannelUri;
if (claim) { if (claim) {
name = claim.name; name = claim.name;
channel = claim.channel_name; signingChannel = claim.signing_channel;
channel = signingChannel ? signingChannel.name : null;
height = claim.height; height = claim.height;
channelClaimId = claim.value && claim.value.publisherSignature && claim.value.publisherSignature.certificateId; channelClaimId = claim.value && claim.value.publisherSignature && claim.value.publisherSignature.certificateId;
fullChannelUri = channelClaimId ? `${channel}#${channelClaimId}` : channel; fullChannelUri = channelClaimId ? `${channel}#${channelClaimId}` : channel;

View file

@ -1,4 +0,0 @@
import { connect } from 'react-redux';
import Gallery from './view';
export default connect()(Gallery);

View file

@ -1,7 +0,0 @@
import React from 'react';
class Gallery extends React.PureComponent {
render() {}
}
export default Gallery;

View file

@ -0,0 +1,4 @@
import { connect } from 'react-redux';
import PublishRewardsDriver from './view';
export default connect()(PublishRewardsDriver);

View file

@ -0,0 +1,20 @@
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
import Colors from 'styles/colors';
import Icon from 'react-native-vector-icons/FontAwesome5';
import publishStyle from 'styles/publish';
class PublishRewadsDriver extends React.PureComponent<Props> {
render() {
const { navigation } = this.props;
return (
<TouchableOpacity style={publishStyle.rewardDriverCard} onPress={() => navigation.navigate('Rewards')}>
<Icon name="award" size={16} style={publishStyle.rewardIcon} />
<Text style={publishStyle.rewardDriverText}>Earn some credits to be able to publish your content.</Text>
</TouchableOpacity>
);
}
}
export default PublishRewadsDriver;

View file

@ -0,0 +1,4 @@
import { connect } from 'react-redux';
import Tag from './view';
export default connect()(Tag);

View file

@ -0,0 +1,55 @@
import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
import tagStyle from 'styles/tag';
import Colors from 'styles/colors';
import Icon from 'react-native-vector-icons/FontAwesome5';
export default class Tag extends React.PureComponent {
onPressDefault = () => {
const { name, navigation, type, onAddPress, onRemovePress } = this.props;
if ('add' === type) {
if (onAddPress) {
onAddPress(name);
}
return;
}
if ('remove' === type) {
if (onRemovePress) {
onRemovePress(name);
}
return;
}
if (navigation) {
// navigate to tag page
}
};
render() {
const { name, onPress, style, type } = this.props;
let styles = [];
if (style) {
if (style.length) {
styles = styles.concat(style);
} else {
styles.push(style);
}
}
styles.push({
backgroundColor: Colors.TagGreen,
borderRadius: 8,
marginBottom: 4,
});
return (
<TouchableOpacity style={styles} onPress={onPress || this.onPressDefault}>
<View style={tagStyle.content}>
<Text style={tagStyle.text}>{name}</Text>
{type && <Icon style={tagStyle.icon} name={type === 'add' ? 'plus' : 'times'} size={8} />}
</View>
</TouchableOpacity>
);
}
}

View file

@ -0,0 +1,12 @@
import { connect } from 'react-redux';
import { selectUnfollowedTags } from 'lbry-redux';
import TagSearch from './view';
const select = state => ({
unfollowedTags: selectUnfollowedTags(state),
});
export default connect(
select,
null
)(TagSearch);

View file

@ -0,0 +1,81 @@
import React from 'react';
import { Text, TextInput, TouchableOpacity, View } from 'react-native';
import Tag from 'component/tag';
import tagStyle from 'styles/tag';
import Colors from 'styles/colors';
import Icon from 'react-native-vector-icons/FontAwesome5';
export default class TagSearch extends React.PureComponent {
state = {
tag: null,
tagResults: [],
};
componentDidMount() {
const { selectedTags = [] } = this.props;
this.updateTagResults(this.state.tag, selectedTags);
}
componentWillReceiveProps(nextProps) {
const { selectedTags: prevSelectedTags = [] } = this.props;
const { selectedTags = [] } = nextProps;
if (selectedTags.length !== prevSelectedTags.length) {
this.updateTagResults(this.state.tag, selectedTags);
}
}
onAddTagPress = tag => {
const { handleAddTag } = this.props;
if (handleAddTag) {
handleAddTag(tag);
}
};
handleTagChange = tag => {
const { selectedTags = [] } = this.props;
this.setState({ tag });
this.updateTagResults(tag, selectedTags);
};
updateTagResults = (tag, selectedTags = []) => {
const { unfollowedTags } = this.props;
// the search term should always be the first result
let results = [];
const tagNotSelected = name => selectedTags.indexOf(name.toLowerCase()) === -1;
const suggestedTagsSet = new Set(unfollowedTags.map(tag => tag.name));
const suggestedTags = Array.from(suggestedTagsSet).filter(tagNotSelected);
if (tag && tag.trim().length > 0) {
results.push(tag.toLowerCase());
const doesTagMatch = name => name.toLowerCase().includes(tag.toLowerCase());
results = results.concat(suggestedTags.filter(doesTagMatch).slice(0, 5));
} else {
results = results.concat(suggestedTags.slice(0, 5));
}
this.setState({ tagResults: results });
};
render() {
const { name, style, type, selectedTags = [] } = this.props;
return (
<View>
<TextInput
style={tagStyle.searchInput}
placeholder={'Search for more tags'}
underlineColorAndroid={Colors.NextLbryGreen}
value={this.state.tag}
numberOfLines={1}
onChangeText={this.handleTagChange}
/>
<View style={tagStyle.tagResultsList}>
{this.state.tagResults.map(tag => (
<Tag key={tag} name={tag} style={tagStyle.tag} type="add" onAddPress={name => this.onAddTagPress(name)} />
))}
</View>
</View>
);
}
}

View file

@ -11,6 +11,7 @@ import {
notificationsReducer, notificationsReducer,
publishReducer, publishReducer,
searchReducer, searchReducer,
tagsReducer,
walletReducer, walletReducer,
} from 'lbry-redux'; } from 'lbry-redux';
import { import {
@ -41,7 +42,6 @@ import thunk from 'redux-thunk';
const globalExceptionHandler = (error, isFatal) => { const globalExceptionHandler = (error, isFatal) => {
if (error && NativeModules.Firebase) { if (error && NativeModules.Firebase) {
console.log(error);
NativeModules.Firebase.logException(isFatal, error.message ? error.message : 'No message', JSON.stringify(error)); NativeModules.Firebase.logException(isFatal, error.message ? error.message : 'No message', JSON.stringify(error));
} }
}; };
@ -97,6 +97,7 @@ const reducers = combineReducers({
search: searchReducer, search: searchReducer,
subscriptions: subscriptionsReducer, subscriptions: subscriptionsReducer,
sync: syncReducer, sync: syncReducer,
tags: tagsReducer,
user: userReducer, user: userReducer,
wallet: walletReducer, wallet: walletReducer,
}); });

View file

@ -22,6 +22,7 @@ import { navigateBack, navigateToUri } from 'utils/helper';
import Icon from 'react-native-vector-icons/FontAwesome5'; import Icon from 'react-native-vector-icons/FontAwesome5';
import ImageViewer from 'react-native-image-zoom-viewer'; import ImageViewer from 'react-native-image-zoom-viewer';
import Button from 'component/button'; import Button from 'component/button';
import Tag from 'component/tag';
import ChannelPage from 'page/channel'; import ChannelPage from 'page/channel';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; import Constants from 'constants';
@ -502,11 +503,7 @@ class FilePage extends React.PureComponent {
}; };
renderTags = tags => { renderTags = tags => {
return tags.map((tag, i) => ( return tags.map((tag, i) => <Tag style={filePageStyle.tagItem} key={`${tag}-${i}`} name={tag} />);
<Text style={filePageStyle.tagItem} key={`${tag}-${i}`}>
{tag}
</Text>
));
}; };
onFileDownloadButtonPlayed = () => { onFileDownloadButtonPlayed = () => {
@ -618,7 +615,8 @@ class FilePage extends React.PureComponent {
const description = metadata.description ? metadata.description : null; const description = metadata.description ? metadata.description : null;
const mediaType = Lbry.getMediaType(contentType); const mediaType = Lbry.getMediaType(contentType);
const isPlayable = mediaType === 'video' || mediaType === 'audio'; const isPlayable = mediaType === 'video' || mediaType === 'audio';
const { height, channel_name: channelName, value } = claim; const { height, signing_channel: signingChannel, value } = claim;
const channelName = signingChannel && signingChannel.name;
const showActions = const showActions =
fileInfo && fileInfo &&
fileInfo.download_path && fileInfo.download_path &&

View file

@ -1,10 +1,18 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doPublish, doResolveUri, doToast, doUploadThumbnail, selectPublishFormValues } from 'lbry-redux'; import {
doPublish,
doResolveUri,
doToast,
doUploadThumbnail,
selectBalance,
selectPublishFormValues,
} from 'lbry-redux';
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import Constants from 'constants'; import Constants from 'constants';
import PublishPage from './view'; import PublishPage from './view';
const select = state => ({ const select = state => ({
balance: selectBalance(state),
publishFormValues: selectPublishFormValues(state), publishFormValues: selectPublishFormValues(state),
}); });

View file

@ -28,14 +28,47 @@ import FloatingWalletBalance from 'component/floatingWalletBalance';
import Icon from 'react-native-vector-icons/FontAwesome5'; import Icon from 'react-native-vector-icons/FontAwesome5';
import Feather from 'react-native-vector-icons/Feather'; import Feather from 'react-native-vector-icons/Feather';
import Link from 'component/link'; import Link from 'component/link';
import PublishRewardsDriver from 'component/publishRewardsDriver';
import Tag from 'component/tag';
import TagSearch from 'component/tagSearch';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import publishStyle from 'styles/publish'; import publishStyle from 'styles/publish';
const languages = {
en: 'English',
zh: 'Chinese',
fr: 'French',
de: 'German',
jp: 'Japanese',
ru: 'Russian',
es: 'Spanish',
id: 'Indonesian',
it: 'Italian',
nl: 'Dutch',
tr: 'Turkish',
pl: 'Polish',
ms: 'Malay',
pt: 'Portuguese',
vi: 'Vietnamese',
th: 'Thai',
ar: 'Arabic',
cs: 'Czech',
hr: 'Croatian',
km: 'Cambodian',
ko: 'Korean',
no: 'Norwegian',
ro: 'Romanian',
hi: 'Hindi',
el: 'Greek',
};
class PublishPage extends React.PureComponent { class PublishPage extends React.PureComponent {
camera = null; camera = null;
state = { state = {
canUseCamera: false, canUseCamera: false,
titleFocused: false,
descriptionFocused: false,
// gallery videos // gallery videos
videos: null, videos: null,
@ -64,9 +97,13 @@ class PublishPage extends React.PureComponent {
bid: 0.1, bid: 0.1,
description: null, description: null,
title: null, title: null,
language: 'en',
license: LICENSES.NONE,
mature: false,
name: null, name: null,
price: 0, price: 0,
uri: null, uri: null,
tags: [],
uploadedThumbnailUri: null, uploadedThumbnailUri: null,
// other // other
@ -124,9 +161,13 @@ class PublishPage extends React.PureComponent {
channelName, channelName,
currentMedia, currentMedia,
description, description,
language,
license,
mature,
name, name,
price, price,
priceSet, priceSet,
tags,
title, title,
uploadedThumbnailUri: thumbnail, uploadedThumbnailUri: thumbnail,
uri, uri,
@ -142,22 +183,28 @@ class PublishPage extends React.PureComponent {
return; return;
} }
const publishTags = tags.slice();
if (mature) {
publishTags.push('nsfw');
}
const publishParams = { const publishParams = {
filePath: currentMedia.filePath, filePath: currentMedia.filePath,
bid: bid || 0.1, bid: bid || 0.1,
tags: publishTags,
title: title || '', title: title || '',
thumbnail: thumbnail, thumbnail: thumbnail,
description: description || '', description: description || '',
language: 'en', language,
nsfw: false, nsfw: mature,
license: '', license,
licenseUrl: '', licenseUrl: '',
otherLicenseDescription: '', otherLicenseDescription: '',
name: name || undefined, name: name || undefined,
contentIsFree: !priceSet, contentIsFree: !priceSet,
fee: { currency: 'LBC', price }, fee: { currency: 'LBC', price },
uri: uri || undefined, uri: uri || undefined,
channel: CLAIM_VALUES.CHANNEL_ANONYMOUS === channelName ? undefined : channelName, channel_name: CLAIM_VALUES.CHANNEL_ANONYMOUS === channelName ? undefined : channelName,
isStillEditing: false, isStillEditing: false,
claim: null, claim: null,
}; };
@ -189,7 +236,6 @@ class PublishPage extends React.PureComponent {
} }
if (publishFormValues) { if (publishFormValues) {
// TODO: Check thumbnail upload progress after thumbnail starts uploading
if (publishFormValues.thumbnail && !this.state.uploadedThumbnailUri) { if (publishFormValues.thumbnail && !this.state.uploadedThumbnailUri) {
this.setState({ uploadedThumbnailUri: publishFormValues.thumbnail }); this.setState({ uploadedThumbnailUri: publishFormValues.thumbnail });
} }
@ -242,9 +288,12 @@ class PublishPage extends React.PureComponent {
bid: 0.1, bid: 0.1,
description: null, description: null,
title: null, title: null,
language: 'en',
license: LICENSES.NONE,
name: null, name: null,
price: 0, price: 0,
uri: null, uri: null,
tags: [],
uploadedThumbnailUri: null, uploadedThumbnailUri: null,
}); });
} }
@ -334,11 +383,8 @@ class PublishPage extends React.PureComponent {
filetype: [DocumentPickerUtil.allFiles()], filetype: [DocumentPickerUtil.allFiles()],
}, },
(error, res) => { (error, res) => {
console.log(error);
console.log('***');
console.log(res);
if (!error) { if (!error) {
console.log(res); //console.log(res);
} }
} }
); );
@ -366,7 +412,7 @@ class PublishPage extends React.PureComponent {
const { notify } = this.props; const { notify } = this.props;
this.setState({ name }); this.setState({ name });
if (!isNameValid(name, false)) { if (!isNameValid(name, false)) {
notify({ message: 'LBRY names must contain only letters, numbers and dashes.' }); notify({ message: 'Your content address contains invalid characters' });
return; return;
} }
@ -380,6 +426,37 @@ class PublishPage extends React.PureComponent {
this.setState({ uri }); this.setState({ uri });
}; };
handleAddTag = tag => {
if (!tag) {
return;
}
const { notify } = this.props;
const { tags } = this.state;
const index = tags.indexOf(tag.toLowerCase());
if (index === -1) {
const newTags = tags.slice();
newTags.push(tag);
this.setState({ tags: newTags });
} else {
notify({ message: `You already added the "${tag}" tag.` });
}
};
handleRemoveTag = tag => {
if (!tag) {
return;
}
const newTags = this.state.tags.slice();
const index = newTags.indexOf(tag.toLowerCase());
if (index > -1) {
newTags.splice(index, 1);
this.setState({ tags: newTags });
}
};
updateThumbnailUriForMedia = media => { updateThumbnailUriForMedia = media => {
if (this.state.updatingThumbnailUri) { if (this.state.updatingThumbnailUri) {
return; return;
@ -400,7 +477,7 @@ class PublishPage extends React.PureComponent {
// upload the thumbnail // upload the thumbnail
if (!this.state.uploadedThumbnailUri) { if (!this.state.uploadedThumbnailUri) {
uploadThumbnail(this.getFilePathFromUri(uri), RNFS); //this.setState({ uploadThumbnailStarted: true }, () => uploadThumbnail(this.getFilePathFromUri(uri), RNFS));
} }
} else if ('image' === mediaType || 'video' === mediaType) { } else if ('image' === mediaType || 'video' === mediaType) {
const create = const create =
@ -411,7 +488,7 @@ class PublishPage extends React.PureComponent {
.then(path => { .then(path => {
this.setState({ currentThumbnailUri: `file://${path}`, updatingThumbnailUri: false }); this.setState({ currentThumbnailUri: `file://${path}`, updatingThumbnailUri: false });
if (!this.state.uploadedThumbnailUri) { if (!this.state.uploadedThumbnailUri) {
uploadThumbnail(path, RNFS); //this.setState({ uploadThumbnailStarted: true }, () => uploadThumbnail(path, RNFS));
} }
}) })
.catch(err => { .catch(err => {
@ -435,7 +512,7 @@ class PublishPage extends React.PureComponent {
}; };
render() { render() {
const { navigation, notify, publishFormValues } = this.props; const { balance, navigation, notify, publishFormValues } = this.props;
const { thumbnailPath } = this.state; const { thumbnailPath } = this.state;
let content; let content;
@ -443,11 +520,9 @@ class PublishPage extends React.PureComponent {
content = ( content = (
<View style={publishStyle.gallerySelector}> <View style={publishStyle.gallerySelector}>
<View style={publishStyle.actionsView}> <View style={publishStyle.actionsView}>
{this.state.canUseCamera && {this.state.canUseCamera && (
<RNCamera <RNCamera style={publishStyle.cameraPreview} type={RNCamera.Constants.Type.back} />
style={publishStyle.cameraPreview} )}
type={RNCamera.Constants.Type.back} />
}
<View style={publishStyle.actionsSubView}> <View style={publishStyle.actionsSubView}>
<TouchableOpacity style={publishStyle.record} onPress={this.handleRecordVideoPressed}> <TouchableOpacity style={publishStyle.record} onPress={this.handleRecordVideoPressed}>
<Icon name="video" size={48} color={Colors.White} /> <Icon name="video" size={48} color={Colors.White} />
@ -458,10 +533,12 @@ class PublishPage extends React.PureComponent {
<Icon name="camera" size={48} color={Colors.White} /> <Icon name="camera" size={48} color={Colors.White} />
<Text style={publishStyle.actionText}>Take a photo</Text> <Text style={publishStyle.actionText}>Take a photo</Text>
</TouchableOpacity> </TouchableOpacity>
{false && (
<TouchableOpacity style={publishStyle.upload} onPress={this.handleUploadPressed}> <TouchableOpacity style={publishStyle.upload} onPress={this.handleUploadPressed}>
<Icon name="file-upload" size={48} color={Colors.White} /> <Icon name="file-upload" size={48} color={Colors.White} />
<Text style={publishStyle.actionText}>Upload a file</Text> <Text style={publishStyle.actionText}>Upload a file</Text>
</TouchableOpacity> </TouchableOpacity>
)}
</View> </View>
</View> </View>
</View> </View>
@ -507,34 +584,68 @@ class PublishPage extends React.PureComponent {
/> />
</View> </View>
)} )}
{balance < 0.1 && <PublishRewardsDriver navigation={navigation} />}
{this.state.uploadThumbnailStarted && !this.state.uploadedThumbnailUri && (
<View style={publishStyle.thumbnailUploadContainer}>
<ActivityIndicator size={'small'} color={Colors.LbryGreen} />
<Text style={publishStyle.thumbnailUploadText}>Uploading thumbnail...</Text>
</View>
)}
<View style={publishStyle.card}> <View style={publishStyle.card}>
<Text style={publishStyle.cardTitle}>Title</Text> <View style={publishStyle.textInputLayout}>
{(this.state.titleFocused || (this.state.title != null && this.state.title.trim().length > 0)) && (
<Text style={publishStyle.textInputTitle}>Title</Text>
)}
<TextInput <TextInput
placeholder={'Title'} placeholder={this.state.titleFocused ? '' : 'Title'}
style={publishStyle.inputText} style={publishStyle.inputText}
value={this.state.title} value={this.state.title}
numberOfLines={1} numberOfLines={1}
underlineColorAndroid={Colors.NextLbryGreen} underlineColorAndroid={Colors.NextLbryGreen}
onChangeText={this.state.handleTitleChange} onChangeText={this.handleTitleChange}
onFocus={() => this.setState({ titleFocused: true })}
onBlur={() => this.setState({ titleFocused: false })}
/> />
</View> </View>
<View style={publishStyle.card}> <View style={publishStyle.textInputLayout}>
<Text style={publishStyle.cardTitle}>Description</Text> {(this.state.descriptionFocused ||
(this.state.description != null && this.state.description.trim().length > 0)) && (
<Text style={publishStyle.textInputTitle}>Description</Text>
)}
<TextInput <TextInput
placeholder={'Description'} placeholder={this.state.descriptionFocused ? '' : 'Description'}
style={publishStyle.inputText} style={publishStyle.inputText}
value={this.state.description} value={this.state.description}
underlineColorAndroid={Colors.NextLbryGreen} underlineColorAndroid={Colors.NextLbryGreen}
onChangeText={this.state.handleDescriptionChange} onChangeText={this.handleDescriptionChange}
onFocus={() => this.setState({ descriptionFocused: true })}
onBlur={() => this.setState({ descriptionFocused: false })}
/> />
</View> </View>
</View>
<View style={publishStyle.card}> <View style={publishStyle.card}>
<View style={publishStyle.titleRow}> <Text style={publishStyle.cardTitle}>Tags</Text>
<Text style={publishStyle.cardTitle}>Channel</Text> <View style={publishStyle.tagList}>
{this.state.tags &&
this.state.tags.map(tag => (
<Tag
key={tag}
name={tag}
type={'remove'}
style={publishStyle.tag}
onRemovePress={this.handleRemoveTag}
/>
))}
</View> </View>
<TagSearch handleAddTag={this.handleAddTag} selectedTags={this.state.tags} />
</View>
<View style={publishStyle.card}>
<Text style={publishStyle.cardTitle}>Channel</Text>
<ChannelSelector onChannelChange={this.handleChannelChange} /> <ChannelSelector onChannelChange={this.handleChannelChange} />
</View> </View>
@ -600,11 +711,54 @@ class PublishPage extends React.PureComponent {
</View> </View>
)} )}
{this.state.advancedMode && (
<View style={publishStyle.card}>
<Text style={publishStyle.cardTitle}>Additional Options</Text>
<View style={publishStyle.toggleField}>
<Switch value={this.state.mature} onValueChange={value => this.setState({ mature: value })} />
<Text style={publishStyle.toggleText}>Mature content</Text>
</View>
<View>
<Text style={publishStyle.cardText}>Language</Text>
<Picker
selectedValue={this.state.language}
style={publishStyle.picker}
itemStyle={publishStyle.pickerItem}
onValueChange={this.handleLanguageValueChange}
>
{Object.keys(languages).map(lang => (
<Picker.Item label={languages[lang]} value={lang} key={lang} />
))}
</Picker>
</View>
<View>
<Text style={publishStyle.cardText}>License</Text>
<Picker
selectedValue={this.state.license}
style={publishStyle.picker}
itemStyle={publishStyle.pickerItem}
onValueChange={this.handleLicenseValueChange}
>
<Picker.Item label={'None'} value={LICENSES.NONE} key={LICENSES.NONE} />
<Picker.Item label={'Public Domain'} value={LICENSES.PUBLIC_DOMAIN} key={LICENSES.PUBLIC_DOMAIN} />
{LICENSES.CC_LICENSES.map(({ value, url }) => (
<Picker.Item label={value} value={value} key={value} />
))}
<Picker.Item label={'Copyrighted...'} value={LICENSES.COPYRIGHT} key={LICENSES.COPYRIGHT} />
<Picker.Item label={'Other...'} value={LICENSES.OTHER} key={LICENSES.OTHER} />
</Picker>
</View>
</View>
)}
<View style={publishStyle.toggleContainer}> <View style={publishStyle.toggleContainer}>
<Link <Link
text={this.state.advancedMode ? 'Hide extra fields' : 'Show extra fields'} text={this.state.advancedMode ? 'Hide extra fields' : 'Show extra fields'}
onPress={this.handleModePressed} onPress={this.handleModePressed}
style={publishStyle.modeLink} /> style={publishStyle.modeLink}
/>
</View> </View>
<View style={publishStyle.actionButtons}> <View style={publishStyle.actionButtons}>
@ -626,7 +780,7 @@ class PublishPage extends React.PureComponent {
<View style={publishStyle.rightActionButtons}> <View style={publishStyle.rightActionButtons}>
<Button <Button
style={publishStyle.publishButton} style={publishStyle.publishButton}
disabled={!this.state.uploadedThumbnailUri} disabled={balance < 0.1 || !this.state.uploadedThumbnailUri}
text="Publish" text="Publish"
onPress={this.handlePublishPressed} onPress={this.handlePublishPressed}
/> />

View file

@ -23,6 +23,7 @@ const channelSelectorStyle = StyleSheet.create({
channelNameInput: { channelNameInput: {
fontFamily: 'Inter-UI-Regular', fontFamily: 'Inter-UI-Regular',
fontSize: 16, fontSize: 16,
paddingLeft: 20,
}, },
bidAmountInput: { bidAmountInput: {
fontFamily: 'Inter-UI-Regular', fontFamily: 'Inter-UI-Regular',
@ -40,6 +41,13 @@ const channelSelectorStyle = StyleSheet.create({
marginLeft: 8, marginLeft: 8,
marginRight: 8, marginRight: 8,
}, },
channelAt: {
position: 'absolute',
left: 4,
top: 13,
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
},
buttonContainer: { buttonContainer: {
flex: 1, flex: 1,
marginTop: 16, marginTop: 16,

View file

@ -9,6 +9,7 @@ const Colors = {
LbryGreen: '#2f9176', LbryGreen: '#2f9176',
BrighterLbryGreen: '#40b887', BrighterLbryGreen: '#40b887',
NextLbryGreen: '#38d9a9', NextLbryGreen: '#38d9a9',
TagGreen: '#e3f6f1',
LightGrey: '#cccccc', LightGrey: '#cccccc',
LighterGrey: '#e5e5e5', LighterGrey: '#e5e5e5',
Orange: '#ffbb00', Orange: '#ffbb00',

View file

@ -322,14 +322,16 @@ const filePageStyle = StyleSheet.create({
tagTitle: { tagTitle: {
fontFamily: 'Inter-UI-SemiBold', fontFamily: 'Inter-UI-SemiBold',
flex: 0.2, flex: 0.2,
marginTop: 4,
}, },
tagList: { tagList: {
fontFamily: 'Inter-UI-Regular', fontFamily: 'Inter-UI-Regular',
flex: 0.8, flex: 0.8,
flexDirection: 'row', flexDirection: 'row',
flexWrap: 'wrap',
}, },
tagItem: { tagItem: {
marginRight: 16, marginRight: 4,
}, },
rewardDriverCard: { rewardDriverCard: {
alignItems: 'center', alignItems: 'center',

View file

@ -45,8 +45,9 @@ const publishStyle = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
}, },
modeLink: { modeLink: {
backgroundColor: Colors.NextLbryGreen, color: Colors.LbryGreen,
alignSelf: 'flex-end' alignSelf: 'flex-end',
marginRight: 16,
}, },
publishButton: { publishButton: {
backgroundColor: Colors.LbryGreen, backgroundColor: Colors.LbryGreen,
@ -81,7 +82,7 @@ const publishStyle = StyleSheet.create({
}, },
photo: { photo: {
backgroundColor: 'transparent', backgroundColor: 'transparent',
height: 120, height: 240,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
}, },
@ -263,8 +264,68 @@ const publishStyle = StyleSheet.create({
toggleContainer: { toggleContainer: {
marginTop: 24, marginTop: 24,
alignItems: 'center', alignItems: 'center',
justifyContent: 'flex-end' justifyContent: 'flex-end',
} },
rewardDriverCard: {
alignItems: 'center',
backgroundColor: Colors.BrighterLbryGreen,
flexDirection: 'row',
paddingLeft: 16,
paddingRight: 16,
paddingTop: 12,
paddingBottom: 12,
},
rewardDriverText: {
fontFamily: 'Inter-UI-Regular',
color: Colors.White,
fontSize: 14,
},
rewardIcon: {
color: Colors.White,
marginRight: 8,
},
tag: {
marginRight: 4,
marginBottom: 4,
},
tagList: {
flexDirection: 'row',
flexWrap: 'wrap',
},
textInputLayout: {
marginBottom: 4,
},
textInputTitle: {
fontFamily: 'Inter-UI-Regular',
fontSize: 12,
marginBottom: -10,
marginLeft: 4,
},
thumbnailUploadContainer: {
marginTop: 16,
marginLeft: 16,
marginRight: 16,
paddingLeft: 2,
paddingRight: 2,
flexDirection: 'row',
alignItems: 'center',
},
thumbnailUploadText: {
fontFamily: 'Inter-UI-Regular',
fontSize: 14,
marginLeft: 8,
},
toggleField: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 4,
marginBottom: 16,
},
toggleText: {
fontFamily: 'Inter-UI-Regular',
fontSize: 14,
marginLeft: 8,
},
}); });
export default publishStyle; export default publishStyle;

35
app/src/styles/tag.js Normal file
View file

@ -0,0 +1,35 @@
import { StyleSheet } from 'react-native';
import Colors from './colors';
const tagStyle = StyleSheet.create({
tag: {
marginRight: 4,
marginBottom: 4,
},
tagSearchInput: {
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
},
content: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingLeft: 8,
paddingTop: 4,
paddingBottom: 4,
},
icon: {
marginRight: 8,
},
text: {
fontFamily: 'Inter-UI-Regular',
fontSize: 14,
marginRight: 8,
},
tagResultsList: {
flexDirection: 'row',
flexWrap: 'wrap',
},
});
export default tagStyle;

View file

@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
# (list) Application requirements # (list) Application requirements
# comma seperated e.g. requirements = sqlite3,kivy # comma seperated e.g. requirements = sqlite3,kivy
requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro==1.4.0, pyjnius, certifi==2018.11.29, appdirs==1.4.3, docopt==0.6.2, base58==1.0.0, colorama==0.3.7, ecdsa==0.13, jsonschema==2.6.0, pbkdf2==1.3, pyyaml, protobuf==3.6.1, keyring==10.4.0, git+https://github.com/lbryio/lbry.git@v0.37.4#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git@a404269d91cff5358bcffb8067b0fd1d9c6842d3#egg=aioupnp, asn1crypto, mock, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, yarl==1.3.0, chardet==3.0.4, async_timeout==3.0.1, git+https://github.com/lbryio/torba@v0.5.4a0#egg=torba, coincurve, msgpack==0.6.1, six, attrs==18.2.0, pylru, hachoir requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro==1.4.0, pyjnius, certifi==2018.11.29, appdirs==1.4.3, docopt==0.6.2, base58==1.0.0, colorama==0.3.7, ecdsa==0.13, jsonschema==2.6.0, pbkdf2==1.3, pyyaml, protobuf==3.6.1, keyring==10.4.0, git+https://github.com/lbryio/aioupnp.git@a404269d91cff5358bcffb8067b0fd1d9c6842d3#egg=aioupnp, asn1crypto, mock, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, yarl==1.3.0, chardet==3.0.4, async_timeout==3.0.1, coincurve, msgpack==0.6.1, six, attrs==18.2.0, pylru, hachoir, "git+https://github.com/lbryio/lbry-sdk@v0.38.0#egg=lbry&subdirectory=lbry", "git+https://github.com/lbryio/lbry-sdk@v0.38.0#egg=torba&subdirectory=torba"
# (str) Custom source folders for requirements # (str) Custom source folders for requirements
# Sets custom source for any requirements with recipes # Sets custom source for any requirements with recipes

View file

@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
# (list) Application requirements # (list) Application requirements
# comma seperated e.g. requirements = sqlite3,kivy # comma seperated e.g. requirements = sqlite3,kivy
requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro==1.4.0, pyjnius, certifi==2018.11.29, appdirs==1.4.3, docopt==0.6.2, base58==1.0.0, colorama==0.3.7, ecdsa==0.13, jsonschema==2.6.0, pbkdf2==1.3, pyyaml, protobuf==3.6.1, keyring==10.4.0, git+https://github.com/lbryio/lbry.git@v0.37.4#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git@a404269d91cff5358bcffb8067b0fd1d9c6842d3#egg=aioupnp, asn1crypto, mock, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, yarl==1.3.0, chardet==3.0.4, async_timeout==3.0.1, git+https://github.com/lbryio/torba@v0.5.4a0#egg=torba, coincurve, msgpack==0.6.1, six, attrs==18.2.0, pylru, hachoir requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro==1.4.0, pyjnius, certifi==2018.11.29, appdirs==1.4.3, docopt==0.6.2, base58==1.0.0, colorama==0.3.7, ecdsa==0.13, jsonschema==2.6.0, pbkdf2==1.3, pyyaml, protobuf==3.6.1, keyring==10.4.0, git+https://github.com/lbryio/aioupnp.git@a404269d91cff5358bcffb8067b0fd1d9c6842d3#egg=aioupnp, asn1crypto, mock, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, yarl==1.3.0, chardet==3.0.4, async_timeout==3.0.1, coincurve, msgpack==0.6.1, six, attrs==18.2.0, pylru, hachoir, "git+https://github.com/lbryio/lbry-sdk@v0.38.0#egg=lbry&subdirectory=lbry", "git+https://github.com/lbryio/lbry-sdk@v0.38.0#egg=torba&subdirectory=torba"
# (str) Custom source folders for requirements # (str) Custom source folders for requirements
# Sets custom source for any requirements with recipes # Sets custom source for any requirements with recipes

View file

@ -272,3 +272,4 @@ warn_on_root = 1
# Then, invoke the command line with the "demo" profile: # Then, invoke the command line with the "demo" profile:
# #
#buildozer --profile demo android debug #buildozer --profile demo android debug
requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro==1.4.0, pyjnius, certifi==2018.11.29, appdirs==1.4.3, docopt==0.6.2, base58==1.0.0, colorama==0.3.7, ecdsa==0.13, jsonschema==2.6.0, pbkdf2==1.3, pyyaml, protobuf==3.6.1, keyring==10.4.0, git+https://github.com/lbryio/aioupnp.git@a404269d91cff5358bcffb8067b0fd1d9c6842d3#egg=aioupnp, asn1crypto, mock, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, yarl==1.3.0, chardet==3.0.4, async_timeout==3.0.1, coincurve, msgpack==0.6.1, six, attrs==18.2.0, pylru, hachoir, "git+https://github.com/lbryio/lbry-sdk@v0.38.0#egg=lbry&subdirectory=lbry", "git+https://github.com/lbryio/lbry-sdk@v0.38.0#egg=torba&subdirectory=torba"

View file

@ -600,9 +600,9 @@ def run_pymodules_install(ctx, modules):
venv = sh.Command(ctx.virtualenv) venv = sh.Command(ctx.virtualenv)
with current_directory(join(ctx.build_dir)): with current_directory(join(ctx.build_dir)):
shprint(venv, shprint(venv,
'--python=python{}'.format( '--python=python{}.{}'.format(
ctx.python_recipe.major_minor_version_string. ctx.python_recipe.major_minor_version_string.partition(".")[0],
partition(".")[0] ctx.python_recipe.major_minor_version_string.partition(".")[2]
), ),
'venv' 'venv'
) )

View file

@ -6,13 +6,12 @@ import platform
import sys import sys
from jnius import autoclass from jnius import autoclass
from keyring.backend import KeyringBackend from keyring.backend import KeyringBackend
from lbrynet import __version__ as lbrynet_version, build_type from lbry import __version__ as lbrynet_version, build_type
from lbrynet.conf import Config from lbry.conf import Config
from lbrynet.extras.daemon.loggly_handler import get_loggly_handler from lbry.extras.daemon.loggly_handler import get_loggly_handler
from lbrynet.extras.daemon.Components import DHT_COMPONENT, HASH_ANNOUNCER_COMPONENT, PEER_PROTOCOL_SERVER_COMPONENT from lbry.extras.daemon.Components import DHT_COMPONENT, HASH_ANNOUNCER_COMPONENT, PEER_PROTOCOL_SERVER_COMPONENT
from lbrynet.extras.daemon.Daemon import Daemon from lbry.extras.daemon.Daemon import Daemon
from lbrynet.extras.daemon.loggly_handler import get_loggly_handler from lbry.extras.daemon.loggly_handler import get_loggly_handler
from lbrynet.utils import check_connection
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG) log.setLevel(logging.DEBUG)
@ -52,24 +51,24 @@ def configure_logging(conf):
conf.log_file_path, maxBytes=2097152, backupCount=5 conf.log_file_path, maxBytes=2097152, backupCount=5
) )
file_handler.setFormatter(default_formatter) file_handler.setFormatter(default_formatter)
logging.getLogger('lbrynet').addHandler(file_handler) logging.getLogger('lbry').addHandler(file_handler)
logging.getLogger('torba').addHandler(file_handler) logging.getLogger('torba').addHandler(file_handler)
handler = logging.StreamHandler() handler = logging.StreamHandler()
handler.setFormatter(default_formatter) handler.setFormatter(default_formatter)
log.addHandler(handler) log.addHandler(handler)
logging.getLogger('lbrynet').addHandler(handler) logging.getLogger('lbry').addHandler(handler)
logging.getLogger('torba').addHandler(handler) logging.getLogger('torba').addHandler(handler)
logging.getLogger('aioupnp').setLevel(logging.WARNING) logging.getLogger('aioupnp').setLevel(logging.WARNING)
logging.getLogger('aiohttp').setLevel(logging.CRITICAL) logging.getLogger('aiohttp').setLevel(logging.CRITICAL)
logging.getLogger('lbrynet').setLevel(logging.DEBUG if lbrynet_android_utils.isDebug() else logging.INFO) logging.getLogger('lbry').setLevel(logging.DEBUG if lbrynet_android_utils.isDebug() else logging.INFO)
logging.getLogger('torba').setLevel(logging.INFO) logging.getLogger('torba').setLevel(logging.INFO)
loggly_handler = get_loggly_handler() loggly_handler = get_loggly_handler()
loggly_handler.setLevel(logging.ERROR) loggly_handler.setLevel(logging.ERROR)
logging.getLogger('lbrynet').addHandler(loggly_handler) logging.getLogger('lbry').addHandler(loggly_handler)
def start(): def start():
keyring.set_keyring(LbryAndroidKeyring()) keyring.set_keyring(LbryAndroidKeyring())