Merge branch 'master' into video-overlay-new

This commit is contained in:
Baltazar Gomez 2018-06-18 13:51:16 -06:00 committed by GitHub
commit 4b7187a707
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
139 changed files with 2449 additions and 2318 deletions

View file

@ -36,6 +36,8 @@
"func-names": ["warn", "as-needed"],
"jsx-a11y/label-has-for": 0,
"import/prefer-default-export": 0,
"no-return-assign": 0
"no-return-assign": 0,
"react/require-default-props": 0,
"react/jsx-closing-tag-location": 0
}
}

View file

@ -20,5 +20,6 @@ module.name_mapper='^lbry\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/lbry\1'
module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/rewards\1'
module.name_mapper='^modal\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/modal\1'
module.name_mapper='^app\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/app\1'
module.name_mapper='^native\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/native\1'
[strict]

2
.gitignore vendored
View file

@ -3,3 +3,5 @@
/static/daemon/lbrynet*
/static/locales
yarn-error.log
package-lock.json
.idea/

View file

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>

12
.idea/lbry-app.iml generated
View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/misc.xml generated
View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="FLOW" />
</component>
</project>

8
.idea/modules.xml generated
View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/lbry-app.iml" filepath="$PROJECT_DIR$/.idea/lbry-app.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

547
.idea/workspace.xml generated
View file

@ -1,547 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="061e089e-71dc-4d6b-b27c-9c614b097257" name="Default" comment="">
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/renderer/types/claim.js" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/renderer/component/fileDetails/view.jsx" afterPath="$PROJECT_DIR$/src/renderer/component/fileDetails/view.jsx" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/renderer/component/uriIndicator/view.jsx" afterPath="$PROJECT_DIR$/src/renderer/component/uriIndicator/view.jsx" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/renderer/component/video/view.jsx" afterPath="$PROJECT_DIR$/src/renderer/component/video/view.jsx" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/renderer/component/walletSendTip/view.jsx" afterPath="$PROJECT_DIR$/src/renderer/component/walletSendTip/view.jsx" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/renderer/page/channel/view.jsx" afterPath="$PROJECT_DIR$/src/renderer/page/channel/view.jsx" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/renderer/page/file/view.jsx" afterPath="$PROJECT_DIR$/src/renderer/page/file/view.jsx" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/renderer/page/show/view.jsx" afterPath="$PROJECT_DIR$/src/renderer/page/show/view.jsx" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/renderer/page/subscriptions/view.jsx" afterPath="$PROJECT_DIR$/src/renderer/page/subscriptions/view.jsx" />
</list>
<ignored path="$PROJECT_DIR$/.tmp/" />
<ignored path="$PROJECT_DIR$/temp/" />
<ignored path="$PROJECT_DIR$/tmp/" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file leaf-file-name="view.jsx" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#0#26#0" expanded="true" />
<marker date="1526068149928" expanded="true" signature="5366:5471" ph="{...}" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="index.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="216">
<caret line="12" column="0" lean-forward="false" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="0" />
<folding>
<marker date="1525123536618" expanded="true" signature="1172:1177" ph="{...}" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="package.json" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1332">
<caret line="74" column="0" lean-forward="false" selection-start-line="74" selection-start-column="0" selection-end-line="74" selection-end-column="0" />
<folding>
<marker date="1526397284183" expanded="true" signature="2668:3140" ph="{&quot;axios&quot;: &quot;^0.18.0&quot;...}" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="join.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/node_modules/bluebird/js/release/join.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="144">
<caret line="8" column="11" lean-forward="false" selection-start-line="8" selection-start-column="11" selection-end-line="8" selection-end-column="11" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="view.jsx" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/renderer/page/file/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="287">
<caret line="18" column="15" lean-forward="false" selection-start-line="18" selection-start-column="14" selection-end-line="18" selection-end-column="15" />
<folding />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>TYPE_FEATURED_DOWNLOAD</find>
<find>TYPE_FEATURED_DOW</find>
<find>doOpen</find>
<find>doNotify</find>
<find>settings</find>
</findStrings>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/src/renderer/modal/modalRouter/index.js" />
<option value="$PROJECT_DIR$/package.json" />
<option value="$PROJECT_DIR$/README.md" />
<option value="$PROJECT_DIR$/src/renderer/page/file/view.jsx" />
</list>
</option>
</component>
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER">
<package-json value="$PROJECT_DIR$/package.json" />
</component>
<component name="JsFlowSettings">
<service-enabled>true</service-enabled>
<exe-path />
<annotation-enable>false</annotation-enable>
<other-services-enabled>true</other-services-enabled>
<auto-save>true</auto-save>
</component>
<component name="JsGulpfileManager">
<detection-done>true</detection-done>
<sorting>DEFINITION_ORDER</sorting>
</component>
<component name="NodeModulesDirectoryManager">
<handled-path value="$PROJECT_DIR$/dist/linux-unpacked/resources/app.asar.unpacked/node_modules" />
<handled-path value="$PROJECT_DIR$/node_modules" />
</component>
<component name="PhpWorkspaceProjectConfiguration" backward_compatibility_performed="true" />
<component name="ProjectFrameBounds" extendedState="6">
<option name="y" value="24" />
<option name="width" value="1920" />
<option name="height" value="1056" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
<manualOrder />
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="lbry-app" type="b2602c69:ProjectViewProjectNode" />
<item name="lbry-app" type="2a2b976b:PhpTreeStructureProvider$1" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scratches" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="nodejs_interpreter_path" value="/usr/local/bin/node" />
<property name="HbShouldOpenHtmlAsHb" value="" />
<property name="node.js.path.for.package.eslint" value="project" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.selected.package.eslint" value="$PROJECT_DIR$/node_modules/eslint" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="ShelveChangesManager" show_recycled="false">
<option name="remove_strategy" value="false" />
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="061e089e-71dc-4d6b-b27c-9c614b097257" name="Default" comment="" />
<created>1522354295512</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1522354295512</updated>
<workItem from="1522354299246" duration="1866000" />
<workItem from="1522643117648" duration="2000" />
<workItem from="1524064618567" duration="2674000" />
<workItem from="1525381677126" duration="197000" />
<workItem from="1525382363305" duration="597000" />
<workItem from="1525461458836" duration="70000" />
<workItem from="1526067492327" duration="897000" />
<workItem from="1526397339713" duration="27000" />
</task>
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="6330000" />
</component>
<component name="ToolWindowManager">
<frame x="0" y="24" width="1920" height="1055" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="npm" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager />
<watches-manager />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#0#26#0" expanded="true" />
<marker date="1526068149928" expanded="true" signature="5366:5471" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="216">
<caret line="12" column="0" lean-forward="false" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="0" />
<folding>
<marker date="1525123536618" expanded="true" signature="1172:1177" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1332">
<caret line="74" column="0" lean-forward="false" selection-start-line="74" selection-start-column="0" selection-end-line="74" selection-end-column="0" />
<folding>
<marker date="1526397284183" expanded="true" signature="2668:3140" ph="{&quot;axios&quot;: &quot;^0.18.0&quot;...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/node_modules/bluebird/js/release/join.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="144">
<caret line="8" column="11" lean-forward="false" selection-start-line="8" selection-start-column="11" selection-end-line="8" selection-end-column="11" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#0#26#0" expanded="true" />
<marker date="1526068149928" expanded="true" signature="5366:5471" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1332">
<caret line="74" column="0" lean-forward="false" selection-start-line="74" selection-start-column="0" selection-end-line="74" selection-end-column="0" />
<folding>
<marker date="1526397284183" expanded="true" signature="2668:3140" ph="{&quot;axios&quot;: &quot;^0.18.0&quot;...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="216">
<caret line="12" column="0" lean-forward="false" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="0" />
<folding>
<marker date="1525123536618" expanded="true" signature="1172:1177" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/node_modules/bluebird/js/release/join.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#0#26#0" expanded="true" />
<marker date="1526068149928" expanded="true" signature="5366:5471" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="216">
<caret line="12" column="0" lean-forward="false" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="0" />
<folding>
<marker date="1525123536618" expanded="true" signature="1172:1177" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2322">
<caret line="129" column="38" lean-forward="false" selection-start-line="129" selection-start-column="38" selection-end-line="129" selection-end-column="38" />
<folding>
<marker date="1526397284183" expanded="true" signature="2668:3140" ph="{&quot;axios&quot;: &quot;^0.18.0&quot;...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#0#26#0" expanded="true" />
<marker date="1526068149928" expanded="true" signature="5366:5471" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="144">
<caret line="19" column="68" lean-forward="true" selection-start-line="19" selection-start-column="68" selection-end-line="19" selection-end-column="68" />
<folding>
<marker date="1525123536618" expanded="true" signature="1172:1177" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/page/rewards/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-122">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/redux/reducers/rewards.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-28">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/redux/actions/rewards.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-832">
<caret line="13" column="61" lean-forward="false" selection-start-line="13" selection-start-column="36" selection-end-line="13" selection-end-column="61" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/rewards.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="324">
<caret line="18" column="0" lean-forward="false" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#0#26#0" expanded="true" />
<marker date="1526068149928" expanded="true" signature="5366:5471" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="216">
<caret line="12" column="0" lean-forward="false" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="0" />
<folding>
<marker date="1525123536618" expanded="true" signature="1172:1177" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="693">
<caret line="129" column="38" lean-forward="false" selection-start-line="129" selection-start-column="38" selection-end-line="129" selection-end-column="38" />
<folding>
<marker date="1526397284183" expanded="true" signature="2668:3140" ph="{&quot;axios&quot;: &quot;^0.18.0&quot;...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#0#26#0" expanded="true" />
<marker date="1526068149928" expanded="true" signature="5366:5471" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="144">
<caret line="19" column="68" lean-forward="true" selection-start-line="19" selection-start-column="68" selection-end-line="19" selection-end-column="68" />
<folding>
<marker date="1525123536618" expanded="true" signature="1172:1177" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/page/rewards/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-122">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/redux/reducers/rewards.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-28">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/redux/actions/rewards.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-832">
<caret line="13" column="61" lean-forward="false" selection-start-line="13" selection-start-column="36" selection-end-line="13" selection-end-column="61" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/rewards.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="324">
<caret line="18" column="0" lean-forward="false" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#0#26#0" expanded="true" />
<marker date="1526068149928" expanded="true" signature="5366:5471" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/modal/modalRouter/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="216">
<caret line="12" column="0" lean-forward="false" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="0" />
<folding>
<marker date="1525123536618" expanded="true" signature="1172:1177" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1332">
<caret line="74" column="0" lean-forward="false" selection-start-line="74" selection-start-column="0" selection-end-line="74" selection-end-column="0" />
<folding>
<marker date="1526397284183" expanded="true" signature="2668:3140" ph="{&quot;axios&quot;: &quot;^0.18.0&quot;...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CONTRIBUTING.md">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="540">
<caret line="168" column="0" lean-forward="true" selection-start-line="168" selection-start-column="0" selection-end-line="168" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="846">
<caret line="47" column="81" lean-forward="true" selection-start-line="47" selection-start-column="81" selection-end-line="47" selection-end-column="81" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/node_modules/bluebird/js/release/join.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="144">
<caret line="8" column="11" lean-forward="false" selection-start-line="8" selection-start-column="11" selection-end-line="8" selection-end-column="11" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/renderer/page/file/view.jsx">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="287">
<caret line="18" column="15" lean-forward="false" selection-start-line="18" selection-start-column="14" selection-end-line="18" selection-end-column="15" />
<folding />
</state>
</provider>
</entry>
</component>
</project>

View file

@ -1,16 +1,19 @@
matrix:
include:
- os: osx
env: TARGET=mac
osx_image: xcode9.2
language: node_js
node_js: "9"
env:
- ELECTRON_CACHE=$HOME/.cache/electron
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
- os: linux
env: TARGET=windows
services: docker
language: node_js
node_js: "9"
- os: linux
env: TARGET=linux
language: node_js
node_js: "9"
cache: false
before_install:
- |
@ -20,6 +23,7 @@ before_install:
else
sudo apt-get -qq update
sudo apt-get install -y libsecret-1-dev
sudo apt-get install --no-install-recommends -y gcc-multilib g++-multilib
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.6.0
export PATH="$HOME/.yarn/bin:$PATH"
fi
@ -27,17 +31,15 @@ before_script:
- git lfs pull
script:
- |
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
if [ "$TARGET" == "windows" ]; then
docker run --rm \
--env-file <(env | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|GH_|GITHUB_|BT_|AWS_|STRIP|BUILD_') \
-v ${PWD}:/project \
-v ~/.cache/electron:/root/.cache/electron \
-v ~/.cache/electron-builder:/root/.cache/electron-builder \
electronuserland/builder:wine \
/bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn build --linux --win --publish onTag"
else
yarn build --publish onTag
/bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn build --win --publish onTag";
fi
- if [ "$TARGET" == "mac" ]; then yarn build --publish onTag; fi
- if [ "$TARGET" == "linux" ]; then yarn --link-duplicates --pure-lockfile && yarn build --linux --publish onTag; fi
branches:
except:
- "/^v\\d+\\.\\d+\\.\\d+$/"

View file

@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
## [Unreleased]
### Added
* Add ability to open log file and log directory in the help page ([#1556](https://github.com/lbryio/lbry-app/issues/1556))
* Add ability to resend verification email ([#1492](https://github.com/lbryio/lbry-app/issues/1492))
* Add Narrative about Feature Request on Help Page and Report Page ([#1551](https://github.com/lbryio/lbry-app/pull/1551))
* Add keyboard shortcut to quit the app on Windows ([#1202](https://github.com/lbryio/lbry-app/pull/1202))
* Build for both architectures (x86 and x64) for Windows ([#1262](https://github.com/lbryio/lbry-app/pull/1262))
* Add referral FAQ to Invites screen([#1314](https://github.com/lbryio/lbry-app/pull/1314))
@ -14,13 +18,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
* Pre-fill publish URL after clicking "Put something here" link ([#1303](https://github.com/lbryio/lbry-app/pull/1303))
* Add Danger JS to automate code reviews ([#1289](https://github.com/lbryio/lbry-app/pull/1289))
* Autoplay downloaded and free media ([#584](https://github.com/lbryio/lbry-app/pull/1453))
* Add 'Go to page' input on channel pagination ([#1166](https://github.com/lbryio/lbry-app/pull/1166))
* Add "View on web" button on file/channel pages with spee.ch link ([#1222](https://github.com/lbryio/lbry-app/pull/1222))
### Changed
* Add flair to snackbar ([#1313](https://github.com/lbryio/lbry-app/pull/1313))
* Made font in price badge larger ([#1420](https://github.com/lbryio/lbry-app/pull/1420))
* Store subscriptions in internal database ([#1424](https://github.com/lbryio/lbry-app/pull/1424))
* Move rewards logic to interal api ([#1509](https://github.com/lbryio/lbry-app/pull/1509))
### Fixed
* Fixing content address extending outside of visible area. ([#741](https://github.com/lbryio/lbry-app/issues/741))
* Fix content-type not shown correctly in file description ([#863](https://github.com/lbryio/lbry-app/pull/863))
* Fix [Flow](https://flow.org/) ([#1197](https://github.com/lbryio/lbry-app/pull/1197))
* Fix black screen on macOS after maximizing LBRY and then closing ([#1235](https://github.com/lbryio/lbry-app/pull/1235))
@ -30,11 +38,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
* Fix don't allow dark mode with automatic night mode enabled ([#1005](https://github.com/lbryio/lbry-app/issues/1005))
* Fix description box on Publish (dark theme) ([#1356](https://github.com/lbryio/lbry-app/issues/#1356))
* Fix price wrapping in price badge ([#1420](https://github.com/lbryio/lbry-app/pull/1420))
* Fix spacing in search suggestions ([#1422])(https://github.com/lbryio/lbry-app/pull/1422))
* Fix text/HTML files don't display correctly in-app anymore ([#1379])(https://github.com/lbryio/lbry-app/issues/1379)
* Fix notification modals when reward is claimed ([#1436])(https://github.com/lbryio/lbry-app/issues/1436) and ([#1407])(https://github.com/lbryio/lbry-app/issues/1407)
* Fix disabled cards(grayed out) ([#1466])(https://github.com/lbryio/lbry-app/issues/1466)
* Fix spacing in search suggestions ([#1422](https://github.com/lbryio/lbry-app/pull/1422))
* Fix text/HTML files don't display correctly in-app anymore ([#1379](https://github.com/lbryio/lbry-app/issues/1379))
* Fix notification modals when reward is claimed ([#1436](https://github.com/lbryio/lbry-app/issues/1436)) and ([#1407](https://github.com/lbryio/lbry-app/issues/1407))
* Fix disabled cards(grayed out) ([#1466](https://github.com/lbryio/lbry-app/issues/1466))
* Fix markdown render ([#1179](https://github.com/lbryio/lbry-app/issues/1179))
* Fix new lines not showing correctly after markdown changes ([#1504](https://github.com/lbryio/lbry-app/issues/1504))
* Fix claim ID being null when reporting a claim that was not previously download ([issue#1512](https://github.com/lbryio/lbry-app/issues/1512)) ([PR#1530](https://github.com/lbryio/lbry-app/pull/1530))
* Fix URI and outpoint not being passed properly to API ([#1494](https://github.com/lbryio/lbry-app/issues/1494))
* Fix incorrect markdown preview on url with parentheses ([#1570](https://github.com/lbryio/lbry-app/issues/1570))
## [0.21.3] - 2018-04-23
@ -57,7 +69,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
* App category for Linux ([#877](https://github.com/lbryio/lbry-app/pull/877))
* Add YouTube Sync reward ([#1147](https://github.com/lbryio/lbry-app/pull/1147))
* Retain previous screen sizing on startup ([#338](https://github.com/lbryio/lbry-app/issues/338))
* Add 'Go to page' input on channel pagination ([#1166](https://github.com/lbryio/lbry-app/pull/1166))
### Changed
@ -372,7 +383,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
* Removed placeholder values from price selection form fields, which was causing confusion that these were real values (#426)
* Fixed showing "other currency" help tip in publish form, which was caused due to not "setting" state for price
* Publish page now properly checks for all required fields are filled
* Fixed pagination styling for pages > 5 (#416)
* Fixed sizing on squat videos (#419)
* Support claims no longer show up on Published page (#384)
* Fixed rendering of small prices (#461)
@ -410,7 +420,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
### Added
* Replaced horizontal scrollbars with scroll arrows
* Featured weekly reward content shows with an orange star
* Added pagination to channel pages
### Fixed

View file

@ -146,6 +146,8 @@ There are a few tools integrated to the project that will ease the process of de
manner and, therefore, not begin working on anything reserved (or updated) within the last 3 days.
If someone has been officially assigned an issue via Github's assignment system, it is also not
available. Contributors are encouraged to ask if they have any questions about issue availability.
* The [changelog](https://github.com/lbryio/lbry-app/blob/master/CHANGELOG.md) should be updated to
include a reference to the fix/change/addition. See previous entries for format.
* Once the pull request is visible in the LBRY repo, a LBRY team member will review it and make sure
it is up to our standards. At this point, the contributor may have to change his or her code based
on our suggestions and comments.

View file

@ -50,7 +50,7 @@ distributable packages.
#### Resetting your Packages
If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del node_modules && yarn` on Windows.
If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del /s /q node_modules && yarn` on Windows.
## Contributing
@ -66,4 +66,4 @@ We take security seriously. Please contact security@lbry.io regarding any securi
## Contact
The primary contact for this project is [@seanyesmunt](https://github.com/@seanyesmunt)
The primary contact for this project is [@seanyesmunt](https://github.com/seanyesmunt)

View file

@ -1,6 +1,6 @@
{
"name": "LBRY",
"version": "0.21.3",
"version": "0.22.0-rc.12",
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
"keywords": [
"lbry"
@ -48,7 +48,7 @@
"formik": "^0.10.4",
"hast-util-sanitize": "^1.1.2",
"keytar": "^4.2.1",
"lbry-redux": "lbryio/lbry-redux#30c18725d8c6c141c30c57f0a324d0abb8963b99",
"lbry-redux": "lbryio/lbry-redux#be7a07302aab0c5fc40f8c76cff524c7bce327a2",
"localforage": "^1.7.1",
"mixpanel-browser": "^2.17.1",
"moment": "^2.22.0",
@ -57,11 +57,11 @@
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-feather": "^1.0.8",
"react-markdown": "^2.5.0",
"react-modal": "^3.1.7",
"react-paginate": "^5.2.1",
"react-redux": "^5.0.3",
"react-simplemde-editor": "3.6.11",
"react-simplemde-editor": "^3.6.15",
"react-toggle": "^4.0.2",
"react-transition-group": "1.x",
"redux": "^3.6.0",
"redux-logger": "^3.0.1",
@ -127,7 +127,7 @@
"yarn": "^1.3"
},
"lbrySettings": {
"lbrynetDaemonVersion": "0.19.3",
"lbrynetDaemonVersion": "0.20.0",
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-daemon-vDAEMONVER-OSNAME.zip",
"lbrynetDaemonDir": "static/daemon",
"lbrynetDaemonFileName": "lbrynet-daemon"

View file

@ -3,7 +3,6 @@ import isDev from 'electron-is-dev';
import windowStateKeeper from 'electron-window-state';
import setupBarMenu from './menu/setupBarMenu';
import setupContextMenu from './menu/setupContextMenu';
export default appState => {
// Get primary display dimensions from Electron.
@ -65,15 +64,18 @@ export default appState => {
// path, so we just strip it off.
// - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as
// an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well.
// - ? also interpreted as an anchor, remove slash also.
if (process.platform === 'win32') {
deepLinkingURI = deepLinkingURI.replace(/\/$/, '').replace('/#', '#');
deepLinkingURI = deepLinkingURI
.replace(/\/$/, '')
.replace('/#', '#')
.replace('/?', '?');
}
} else {
deepLinkingURI = appState.macDeepLinkingURI;
}
setupBarMenu();
setupContextMenu(window);
window.on('close', event => {
if (!appState.isQuitting && !appState.autoUpdateAccepted) {

View file

@ -75,6 +75,9 @@ app.on('ready', async () => {
await installExtensions();
}
rendererWindow = createWindow(appState);
rendererWindow.webContents.on('devtools-opened', () => {
rendererWindow.webContents.send('devtools-is-opened');
});
tray = createTray(rendererWindow);
});
@ -176,11 +179,11 @@ ipcMain.on('version-info-requested', () => {
},
};
let result = '';
const req = https.get(Object.assign(opts, url.parse(latestReleaseAPIURL)), res => {
const onSuccess = res => {
res.on('data', data => {
result += data;
});
res.on('end', () => {
const tagName = JSON.parse(result).tag_name;
const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
@ -199,14 +202,27 @@ ipcMain.on('version-info-requested', () => {
}
}
});
});
};
req.on('error', err => {
console.log('Failed to get current version from GitHub. Error:', err);
if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', null);
}
});
const requestLatestRelease = (apiUrl, alreadyRedirected = false) => {
const req = https.get(Object.assign(opts, url.parse(apiUrl)), res => {
if (res.statusCode === 301 || res.statusCode === 302) {
requestLatestRelease(res.headers.location, true);
} else {
onSuccess(res);
}
});
if (alreadyRedirected) return;
req.on('error', err => {
console.log('Failed to get current version from GitHub. Error:', err);
if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', null);
}
});
};
requestLatestRelease(latestReleaseAPIURL);
});
ipcMain.on('get-auth-token', event => {
@ -242,8 +258,11 @@ const isSecondInstance = app.makeSingleInstance(argv => {
// path, so we just strip it off.
// - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as
// an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well.
// - ? also interpreted as an anchor, remove slash also.
if (process.platform === 'win32') {
URI = URI.replace(/\/$/, '').replace('/#', '#');
URI = URI.replace(/\/$/, '')
.replace('/#', '#')
.replace('/?', '?');
}
rendererWindow.webContents.send('open-uri-requested', URI);

View file

@ -1,27 +0,0 @@
// @flow
import { Menu, BrowserWindow } from 'electron';
import isDev from 'electron-is-dev';
export default (rendererWindow: BrowserWindow) => {
rendererWindow.webContents.on('context-menu', (e, params) => {
const { x, y } = params;
const template = [{ role: 'cut' }, { role: 'copy' }, { role: 'paste' }];
const developmentTemplateAddition = [
{ type: 'separator' },
{
label: 'Inspect element',
click: () => {
rendererWindow.inspectElement(x, y);
},
},
];
if (isDev) {
template.push(...developmentTemplateAddition);
}
Menu.buildFromTemplate(template).popup();
});
};

View file

@ -7,6 +7,7 @@ import ReactModal from 'react-modal';
import throttle from 'util/throttle';
import SideBar from 'component/sideBar';
import Header from 'component/header';
import { openContextMenu } from '../../util/contextMenu';
type Props = {
alertError: (string | {}) => void,
@ -79,12 +80,12 @@ class App extends React.PureComponent<Props> {
render() {
return (
<div id="window">
<div id="window" onContextMenu={e => openContextMenu(e)}>
<Theme />
<main className="page">
<SideBar />
<Header />
<div className="content" id="content">
<Header />
<Router />
<ModalRouter />
</div>

View file

@ -22,6 +22,7 @@ type Props = {
button: ?string, // primary, secondary, alt, link
noPadding: ?boolean, // to remove padding and allow circular buttons
uppercase: ?boolean,
iconColor: ?string,
};
class Button extends React.PureComponent<Props> {
@ -48,6 +49,7 @@ class Button extends React.PureComponent<Props> {
type,
noPadding,
uppercase,
iconColor,
...otherProps
} = this.props;
@ -82,10 +84,10 @@ class Button extends React.PureComponent<Props> {
const content = (
<span className="btn__content">
{icon && <Icon icon={icon} />}
{icon && <Icon icon={icon} iconColor={iconColor} />}
{label && <span className="btn__label">{label}</span>}
{children && children}
{iconRight && <Icon icon={iconRight} />}
{iconRight && <Icon icon={iconRight} iconColor={iconColor} />}
</span>
);

View file

@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import { doFetchClaimsByChannel } from 'redux/actions/content';
import {
makeSelectClaimsInChannelForCurrentPage,
makeSelectFetchingChannelClaims,
} from 'lbry-redux';
import CategoryList from './view';
const select = (state, props) => ({
channelClaims: makeSelectClaimsInChannelForCurrentPage(props.categoryLink)(state),
fetching: makeSelectFetchingChannelClaims(props.categoryLink)(state),
});
const perform = dispatch => ({
fetchChannel: channel => dispatch(doFetchClaimsByChannel(channel)),
});
export default connect(select, perform)(CategoryList);

View file

@ -5,11 +5,15 @@ import ToolTip from 'component/common/tooltip';
import FileCard from 'component/fileCard';
import Button from 'component/button';
import * as icons from 'constants/icons';
import Claim from 'types/claim';
type Props = {
category: string,
names: Array<string>,
categoryLink?: string,
categoryLink: ?string,
fetching: boolean,
channelClaims: Array<Claim>,
fetchChannel: string => void,
};
type State = {
@ -18,6 +22,11 @@ type State = {
};
class CategoryList extends React.PureComponent<Props, State> {
static defaultProps = {
names: [],
categoryLink: '',
};
constructor() {
super();
@ -32,6 +41,11 @@ class CategoryList extends React.PureComponent<Props, State> {
}
componentDidMount() {
const { fetching, categoryLink, fetchChannel } = this.props;
if (!fetching && categoryLink) {
fetchChannel(categoryLink);
}
const cardRow = this.rowItems;
if (cardRow) {
const cards = cardRow.getElementsByTagName('section');
@ -109,6 +123,9 @@ class CategoryList extends React.PureComponent<Props, State> {
// check if a card is fully visible horizontally
isCardVisible = (section: HTMLElement) => {
if (!section) {
return false;
}
const rect = section.getBoundingClientRect();
const isVisible = rect.left >= 0 && rect.right <= window.innerWidth;
return isVisible;
@ -189,7 +206,7 @@ class CategoryList extends React.PureComponent<Props, State> {
}
render() {
const { category, names, categoryLink } = this.props;
const { category, categoryLink, names, channelClaims } = this.props;
const { canScrollNext, canScrollPrevious } = this.state;
// The lint was throwing an error saying we should use <button> instead of <a>
@ -235,6 +252,12 @@ class CategoryList extends React.PureComponent<Props, State> {
className="card-row__scrollhouse"
>
{names && names.map(name => <FileCard key={name} uri={normalizeURI(name)} />)}
{channelClaims && channelClaims.length
? channelClaims.map(claim => (
<FileCard key={claim.claim_id} uri={`lbry://${claim.name}#${claim.claim_id}`} />
))
: null}
</div>
</div>
);

View file

@ -9,6 +9,8 @@ type Props = {
type: string,
currentPath: ?string,
onFileChosen: (string, string) => void,
fileLabel: ?string,
directoryLabel?: string,
};
class FileSelector extends React.PureComponent<Props> {
@ -47,15 +49,14 @@ class FileSelector extends React.PureComponent<Props> {
input: ?HTMLInputElement;
render() {
const { type, currentPath } = this.props;
const { type, currentPath, fileLabel, directoryLabel } = this.props;
const label =
type === 'file' ? fileLabel || __('Choose File') : directoryLabel || __('Choose Directory');
return (
<FormRow verticallyCentered padded>
<Button
button="primary"
onClick={() => this.handleButtonClick()}
label={type === 'file' ? __('Choose File') : __('Choose Directory')}
/>
<Button button="primary" onClick={() => this.handleButtonClick()} label={label} />
<input
webkitdirectory="true"
className="input-copyable"

View file

@ -62,7 +62,6 @@ export class FormFieldPrice extends React.PureComponent<Props> {
name={`${name}_currency`}
type="select"
id={`${name}_currency`}
className="form-field"
disabled={disabled}
onChange={this.handleCurrencyChange}
value={price.currency}

View file

@ -1,8 +1,11 @@
// @flow
import * as React from 'react';
import ReactDOMServer from 'react-dom/server';
import classnames from 'classnames';
import MarkdownPreview from 'component/common/markdown-preview';
import SimpleMDE from 'react-simplemde-editor';
import style from 'react-simplemde-editor/dist/simplemde.min.css'; // eslint-disable-line no-unused-vars
import 'simplemde/dist/simplemde.min.css';
import Toggle from 'react-toggle';
type Props = {
name: string,
@ -18,6 +21,8 @@ type Props = {
placeholder?: string | number,
children?: React.Node,
stretch?: boolean,
affixClass?: string, // class applied to prefix/postfix label
useToggle?: boolean,
};
export class FormField extends React.PureComponent<Props> {
@ -33,6 +38,8 @@ export class FormField extends React.PureComponent<Props> {
type,
children,
stretch,
affixClass,
useToggle,
...inputProps
} = this.props;
@ -42,7 +49,7 @@ export class FormField extends React.PureComponent<Props> {
if (type) {
if (type === 'select') {
input = (
<select id={name} {...inputProps}>
<select className="form-field__select" id={name} {...inputProps}>
{children}
</select>
);
@ -52,12 +59,20 @@ export class FormField extends React.PureComponent<Props> {
<SimpleMDE
{...inputProps}
type="textarea"
options={{ hideIcons: ['heading', 'image', 'fullscreen', 'side-by-side'] }}
options={{
hideIcons: ['heading', 'image', 'fullscreen', 'side-by-side'],
previewRender(plainText) {
const preview = <MarkdownPreview content={plainText} />;
return ReactDOMServer.renderToString(preview);
},
}}
/>
</div>
);
} else if (type === 'textarea') {
input = <textarea type={type} id={name} {...inputProps} />;
} else if (type === 'checkbox' && useToggle) {
input = <Toggle id={name} {...inputProps} />;
} else {
input = <input type={type} id={name} {...inputProps} />;
}
@ -84,22 +99,18 @@ export class FormField extends React.PureComponent<Props> {
})}
>
{prefix && (
<label htmlFor={name} className="form-field__prefix">
<label htmlFor={name} className={classnames('form-field__prefix', affixClass)}>
{prefix}
</label>
)}
{input}
{postfix && (
<label htmlFor={name} className="form-field__postfix">
<label htmlFor={name} className={classnames('form-field__postfix', affixClass)}>
{postfix}
</label>
)}
</div>
{helper && (
<label htmlFor={name} className="form-field__help">
{helper}
</label>
)}
{helper && <div className="form-field__help">{helper}</div>}
</div>
);
}

View file

@ -4,7 +4,6 @@ import * as React from 'react';
import classnames from 'classnames';
type Props = {
centered?: boolean,
children: React.Node,
padded?: boolean,
verticallyCentered?: boolean,
@ -22,7 +21,6 @@ export class FormRow extends React.PureComponent<Props> {
return (
<div
className={classnames('form-row', {
'form-row--centered': centered,
'form-row--padded': padded,
'form-row--vertically-centered': verticallyCentered,
'form-row--stretch': stretch,

View file

@ -1,26 +1,52 @@
// @flow
import React from 'react';
// import * as icons from 'constants/icons';
import * as FeatherIcons from 'react-feather';
import * as icons from 'constants/icons';
import Tooltip from 'component/common/tooltip';
const RED_COLOR = '#e2495e';
const PURPLE_COLOR = '#8165b0';
type Props = {
icon: string,
tooltip?: string, // tooltip direction
iconColor?: string,
};
class IconComponent extends React.PureComponent<Props> {
// TODO: Move all icons to constants and add titles for all
// Add some some sort of hover flyout with the title?
getTooltip = (icon: string) => {
switch (icon) {
case icons.FEATURED:
return __('Featured content. Earn rewards for watching.');
case icons.LOCAL:
return __('This file is downloaded.');
default:
return null;
}
};
getIconColor = (color: string) => {
switch (color) {
case 'red':
return RED_COLOR;
case 'purple':
return PURPLE_COLOR;
default:
return null;
}
};
render() {
const { icon } = this.props;
const { icon, tooltip, iconColor } = this.props;
const Icon = FeatherIcons[icon];
if (!Icon) {
return null;
}
let color;
if (icon === icons.HEART || icon === icons.FEATURED) {
color = RED_COLOR;
if (iconColor) {
color = this.getIconColor(iconColor);
}
let size = 14;
@ -28,7 +54,19 @@ class IconComponent extends React.PureComponent<Props> {
size = 20;
}
return Icon ? <Icon size={size} className="icon" color={color} /> : null;
let tooltipText;
if (tooltip) {
tooltipText = this.getTooltip(icon);
}
const inner = <Icon size={size} className="icon" color={color} />;
return tooltip ? (
<Tooltip icon body={tooltipText} direction={tooltip}>
{inner}
</Tooltip>
) : (
inner
);
}
}

View file

@ -11,14 +11,19 @@ const schema = { ...defaultSchema };
// Extend sanitation schema to support lbry protocol
schema.protocols.href[3] = 'lbry';
type MarkdownProps = { content: string };
type MarkdownProps = {
content: string,
promptLinks?: boolean,
};
const SimpleLink = ({ href, title, children }) => (<a href={href} title={title}>{children}</a>);
const MarkdownPreview = (props: MarkdownProps) => {
const { content } = props;
const { content, externalLinks, promptLinks } = props;
const remarkOptions = {
sanitize: schema,
remarkReactComponents: {
a: ExternalLink,
a: promptLinks ? ExternalLink : SimpleLink,
},
};
return (

View file

@ -6,19 +6,22 @@ import classnames from 'classnames';
type Props = {
value: string,
paddingRight?: boolean,
paddingTop?: boolean,
};
class QRCode extends React.Component<Props> {
static defaultProps = {
paddingRight: false,
paddingTop: false,
};
render() {
const { value, paddingRight } = this.props;
const { value, paddingRight, paddingTop } = this.props;
return (
<div
className={classnames('qr-code', {
'qr-code--right-padding': paddingRight,
'qr-code--top-padding': paddingTop,
})}
>
<QRCodeElement value={value} />

View file

@ -1,55 +1,40 @@
// @flow
import React from 'react';
import * as React from 'react';
import classnames from 'classnames';
import Icon from 'component/common/icon';
import Button from 'component/button';
import * as icons from 'constants/icons';
type Props = {
body: string,
label: string,
label?: string,
children: ?React.Node,
icon: ?boolean,
direction: string,
onFormField?: boolean,
};
type State = {
showTooltip: boolean,
};
class ToolTip extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
showTooltip: false,
};
(this: any).handleClick = this.handleClick.bind(this);
}
handleClick() {
const { showTooltip } = this.state;
if (!showTooltip) {
document.addEventListener('click', this.handleClick);
} else {
document.removeEventListener('click', this.handleClick);
}
this.setState({
showTooltip: !showTooltip,
});
}
class ToolTip extends React.PureComponent<Props> {
static defaultProps = {
direction: 'bottom',
};
render() {
const { label, body } = this.props;
const { showTooltip } = this.state;
const { children, label, body, icon, direction, onFormField } = this.props;
const tooltipContent = children || label;
return (
<span className="tooltip">
<Button button="link" className="help tooltip__link" onClick={this.handleClick}>
{label}
{showTooltip && <Icon icon={icons.CLOSE} />}
</Button>
<div className={classnames('tooltip__body', { hidden: !showTooltip })}>{body}</div>
<span
className={classnames('tooltip', {
'tooltip--label': label && !icon,
'tooltip--icon': icon,
'tooltip--top': direction === 'top',
'tooltip--right': direction === 'right',
'tooltip--bottom': direction === 'bottom',
'tooltip--left': direction === 'left',
'tooltip--on-formfield': onFormField,
})}
>
{tooltipContent}
<span className="tooltip__body">{body}</span>
</span>
);
}

View file

@ -19,7 +19,6 @@ class ExternalLink extends React.PureComponent<Props> {
createLink() {
const { href, title, children, openModal, navigate } = this.props;
console.info(href);
// Regex for url protocol
const protocolRegex = new RegExp('^(https?|lbry)+:', 'i');

View file

@ -1,7 +1,6 @@
// @flow
import React from 'react';
import Button from 'component/button';
import FileDownloadLink from 'component/fileDownloadLink';
import { MODALS } from 'lbry-redux';
import classnames from 'classnames';
import * as icons from 'constants/icons';
@ -12,6 +11,7 @@ type FileInfo = {
type Props = {
uri: string,
claimId: string,
openModal: ({ id: string }, { uri: string }) => void,
claimIsMine: boolean,
fileInfo: FileInfo,
@ -20,28 +20,26 @@ type Props = {
class FileActions extends React.PureComponent<Props> {
render() {
const { fileInfo, uri, openModal, claimIsMine, vertical } = this.props;
const claimId = fileInfo ? fileInfo.claim_id : '';
const { fileInfo, uri, openModal, claimIsMine, vertical, claimId } = this.props;
const showDelete = fileInfo && Object.keys(fileInfo).length > 0;
return (
<section className={classnames('card__actions', { 'card__actions--vertical': vertical })}>
<FileDownloadLink uri={uri} />
{showDelete && (
<Button
className="btn--file-actions"
button="alt"
icon={icons.TRASH}
description={__('Delete')}
iconColor="red"
label={__('Delete')}
onClick={() => openModal({ id: MODALS.CONFIRM_FILE_REMOVE }, { uri })}
/>
)}
{!claimIsMine && (
<Button
className="btn--file-actions"
button="alt"
icon={icons.REPORT}
href={`https://lbry.io/dmca?claim_id=${claimId}`}
description={__('Report content')}
label={__('Report content')}
/>
)}
</section>

View file

@ -5,6 +5,7 @@ import {
makeSelectMetadataForUri,
makeSelectFileInfoForUri,
makeSelectIsUriResolving,
makeSelectClaimIsMine,
} from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import { selectRewardContentClaimIds } from 'redux/selectors/content';
@ -27,6 +28,7 @@ const select = (state, props) => {
return {
obscureNsfw: !selectShowNsfw(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
...fileCardInfo,
pending: !!pendingPublish,
@ -38,4 +40,7 @@ const perform = dispatch => ({
resolveUri: uri => dispatch(doResolveUri(uri)),
});
export default connect(select, perform)(FileCard);
export default connect(
select,
perform
)(FileCard);

View file

@ -1,6 +1,6 @@
// @flow
import * as React from 'react';
import { normalizeURI } from 'lbry-redux';
import { normalizeURI, convertToShareLink } from 'lbry-redux';
import Button from 'component/button';
import CardMedia from 'component/cardMedia';
import TruncatedText from 'component/common/truncated-text';
@ -9,18 +9,24 @@ import FilePrice from 'component/filePrice';
import UriIndicator from 'component/uriIndicator';
import * as icons from 'constants/icons';
import classnames from 'classnames';
import { openCopyLinkMenu } from '../../util/contextMenu';
// TODO: iron these out
type Props = {
uri: string,
claim: ?{ claim_id: string },
fileInfo: ?{},
metadata: ?{ nsfw: boolean, thumbnail: ?string },
metadata: ?{ nsfw: boolean, title: string, thumbnail: ?string },
navigate: (string, ?{}) => void,
rewardedContentClaimIds: Array<string>,
obscureNsfw: boolean,
claimIsMine: boolean,
showPrice: boolean,
pending?: boolean,
/* eslint-disable react/no-unused-prop-types */
resolveUri: string => void,
isResolvingUri: boolean,
/* eslint-enable react/no-unused-prop-types */
};
class FileCard extends React.PureComponent<Props> {
@ -52,14 +58,20 @@ class FileCard extends React.PureComponent<Props> {
navigate,
rewardedContentClaimIds,
obscureNsfw,
claimIsMine,
showPrice,
pending,
} = this.props;
const uri = !pending ? normalizeURI(this.props.uri) : this.props.uri;
const title = metadata && metadata.title ? metadata.title : uri;
const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null;
const shouldObscureNsfw = obscureNsfw && metadata && metadata.nsfw;
const shouldObscureNsfw = obscureNsfw && metadata && metadata.nsfw && !claimIsMine;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
const handleContextMenu = event => {
event.preventDefault();
event.stopPropagation();
openCopyLinkMenu(convertToShareLink(uri), event);
};
// We should be able to tab through cards
/* eslint-disable jsx-a11y/click-events-have-key-events */
@ -72,6 +84,7 @@ class FileCard extends React.PureComponent<Props> {
'card--link': !pending,
'card--pending': pending,
})}
onContextMenu={handleContextMenu}
>
<CardMedia nsfw={shouldObscureNsfw} thumbnail={thumbnail} />
<div className="card-media__internal-links">{showPrice && <FilePrice uri={uri} />}</div>
@ -98,14 +111,16 @@ class FileCard extends React.PureComponent<Props> {
<div className="card__title--small">
<TruncatedText lines={3}>{title}</TruncatedText>
</div>
<div className="card__subtitle card__subtitle--file-info">
<div className="card__subtitle">
{pending ? (
<div>Pending...</div>
) : (
<React.Fragment>
<UriIndicator uri={uri} link />
{isRewardContent && <Icon icon={icons.FEATURED} />}
{fileInfo && <Icon icon={icons.LOCAL} />}
<div>
{isRewardContent && <Icon iconColor="red" icon={icons.FEATURED} />}
{fileInfo && <Icon icon={icons.LOCAL} />}
</div>
</React.Fragment>
)}
</div>

View file

@ -1,6 +1,6 @@
// @flow
import * as React from 'react';
import MarkdownPreview from 'component/markdownPreview';
import MarkdownPreview from 'component/common/markdown-preview';
import Button from 'component/button';
import path from 'path';
import type { Claim } from 'types/claim';
@ -41,7 +41,7 @@ const FileDetails = (props: Props) => {
<React.Fragment>
<div className="card__subtext-title">About</div>
<div className="card__subtext">
<MarkdownPreview content={description} />
<MarkdownPreview content={description} promptLinks={true} />
</div>
</React.Fragment>
)}

View file

@ -5,7 +5,6 @@ import {
makeSelectLoadingForUri,
makeSelectCostInfoForUri,
} from 'lbry-redux';
import { doFetchAvailability } from 'redux/actions/availability';
import { doOpenFileInShell } from 'redux/actions/file';
import { doPurchaseUri, doStartDownload } from 'redux/actions/content';
import { doPause } from 'redux/actions/media';
@ -20,7 +19,6 @@ const select = (state, props) => ({
});
const perform = dispatch => ({
checkAvailability: uri => dispatch(doFetchAvailability(uri)),
openInShell: path => dispatch(doOpenFileInShell(path)),
purchaseUri: uri => dispatch(doPurchaseUri(uri)),
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),

View file

@ -1,21 +1,29 @@
// @flow
import React from 'react';
import Button from 'component/button';
import classnames from 'classnames';
import * as icons from 'constants/icons';
class FileDownloadLink extends React.PureComponent {
componentWillMount() {
this.checkAvailability(this.props.uri);
}
componentWillReceiveProps(nextProps) {
this.checkAvailability(nextProps.uri);
this.restartDownload(nextProps);
}
restartDownload(props) {
const { downloading, fileInfo, uri, restartDownload } = props;
type Props = {
uri: string,
downloading: boolean,
fileInfo: ?{
written_bytes: number,
total_bytes: number,
outpoint: number,
download_path: string,
completed: boolean,
},
loading: boolean,
costInfo: ?{},
restartDownload: (string, number) => void,
openInShell: string => void,
purchaseUri: string => void,
doPause: () => void,
};
class FileDownloadLink extends React.PureComponent<Props> {
componentWillUpdate() {
const { downloading, fileInfo, uri, restartDownload } = this.props;
if (
!downloading &&
fileInfo &&
@ -27,12 +35,7 @@ class FileDownloadLink extends React.PureComponent {
}
}
checkAvailability(uri) {
if (!this._uri || uri !== this._uri) {
this._uri = uri;
this.props.checkAvailability(uri);
}
}
uri: ?string;
render() {
const {
@ -47,8 +50,10 @@ class FileDownloadLink extends React.PureComponent {
} = this.props;
const openFile = () => {
openInShell(fileInfo.download_path);
doPause();
if (fileInfo) {
openInShell(fileInfo.download_path);
doPause();
}
};
if (loading || downloading) {
@ -56,21 +61,11 @@ class FileDownloadLink extends React.PureComponent {
fileInfo && fileInfo.written_bytes
? fileInfo.written_bytes / fileInfo.total_bytes * 100
: 0;
const label = fileInfo ? progress.toFixed(0) + __('% complete') : __('Connecting...');
const label = fileInfo
? __('Downloading: ') + progress.toFixed(0) + __('% complete')
: __('Connecting...');
return (
<div className="file-download btn__content">
<div
className={classnames('file-download__overlay', {
btn__content: !!progress,
})}
style={{ width: `${progress}%` }}
>
{label}
</div>
{label}
</div>
);
return <span className="file-download">{label}</span>;
} else if (fileInfo === null && !downloading) {
if (!costInfo) {
return null;
@ -78,9 +73,10 @@ class FileDownloadLink extends React.PureComponent {
return (
<Button
className="btn--file-actions"
description={__('Download')}
button="alt"
label={__('Download')}
icon={icons.DOWNLOAD}
iconColor="purple"
onClick={() => {
purchaseUri(uri);
}}
@ -89,8 +85,9 @@ class FileDownloadLink extends React.PureComponent {
} else if (fileInfo && fileInfo.download_path) {
return (
<Button
className="btn--file-actions"
description={__('Open')}
button="alt"
iconColor="purple"
label={__('Open File')}
icon={icons.OPEN}
onClick={() => openFile()}
/>

View file

@ -8,6 +8,7 @@ type FileInfo = {
name: string,
channelName: ?string,
pending?: boolean,
channel_claim_id: string,
value?: {
publisherSignature: {
certificateId: string,
@ -139,6 +140,8 @@ class FileList extends React.PureComponent<Props, State> {
});
}
sortFunctions: {};
render() {
const { fileInfos, hideFilter, checkPending } = this.props;
const { sortBy } = this.state;
@ -149,27 +152,14 @@ class FileList extends React.PureComponent<Props, State> {
}
this.sortFunctions[sortBy](fileInfos).forEach(fileInfo => {
const {
channel_name: channelName,
name: claimName,
claim_name: claimNameDownloaded,
claim_id: claimId,
} = fileInfo;
const { name: claimName, claim_name: claimNameDownloaded, claim_id: claimId } = fileInfo;
const uriParams = {};
// This is unfortunate
// https://github.com/lbryio/lbry/issues/1159
const name = claimName || claimNameDownloaded;
if (channelName) {
uriParams.channelName = channelName;
uriParams.contentName = name;
uriParams.claimId = this.getChannelSignature(fileInfo);
} else {
uriParams.claimId = claimId;
uriParams.claimName = name;
}
uriParams.contentName = name;
uriParams.claimId = claimId;
const uri = buildURI(uriParams);
content.push(<FileCard key={uri} uri={uri} checkPending={checkPending} />);
@ -181,6 +171,7 @@ class FileList extends React.PureComponent<Props, State> {
{!hideFilter && (
<FormField
prefix={__('Sort by')}
affixClass="form-field--align-center"
type="select"
value={sortBy}
onChange={this.handleSortChanged}

View file

@ -58,7 +58,7 @@ class FileListSearch extends React.PureComponent<Props> {
return (
query && (
<div className="search__results">
<div className="search-result__column">
<div className="search-result__row">
<div className="file-list__header">{__('Content')}</div>
{!isSearching &&
(fileResults.length ? (
@ -68,7 +68,7 @@ class FileListSearch extends React.PureComponent<Props> {
))}
</div>
<div className="search-result__column">
<div className="search-result__row">
<div className="file-list__header">{__('Channels')}</div>
{!isSearching &&
(channelResults.length ? (
@ -78,7 +78,7 @@ class FileListSearch extends React.PureComponent<Props> {
))}
</div>
<div className="search-result__column">
<div className="search-result__row">
<div className="file-list__header">{__('Your downloads')}</div>
{downloadUris && downloadUris.length ? (
downloadUris.map(uri => <FileTile hideNoResult key={uri} uri={uri} />)

View file

@ -35,20 +35,14 @@ class FilePrice extends React.PureComponent<Props> {
render() {
const { costInfo, showFullPrice } = this.props;
const isEstimate = costInfo ? !costInfo.includesData : false;
if (!costInfo) {
return <span className="credit-amount">PRICE</span>;
}
return (
return costInfo ? (
<CreditAmount
amount={costInfo.cost}
isEstimate={isEstimate}
isEstimate={!costInfo.includesData}
showFree
showFullPrice={showFullPrice}
/>
);
) : null;
}
}

View file

@ -5,7 +5,9 @@ import {
makeSelectMetadataForUri,
makeSelectFileInfoForUri,
makeSelectIsUriResolving,
makeSelectClaimIsMine,
} from 'lbry-redux';
import { selectShowNsfw } from 'redux/selectors/settings';
import { doNavigate } from 'redux/actions/navigation';
import { doClearPublish, doUpdatePublishForm } from 'redux/actions/publish';
import { selectRewardContentClaimIds } from 'redux/selectors/content';
@ -17,6 +19,8 @@ const select = (state, props) => ({
metadata: makeSelectMetadataForUri(props.uri)(state),
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
obscureNsfw: !selectShowNsfw(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
});
const perform = dispatch => ({

View file

@ -13,6 +13,8 @@ type Props = {
fullWidth: boolean, // removes the max-width css
showUri: boolean,
showLocal: boolean,
obscureNsfw: boolean,
claimIsMine: boolean,
isDownloaded: boolean,
uri: string,
isResolvingUri: boolean,
@ -58,6 +60,8 @@ class FileTile extends React.PureComponent<Props> {
navigate,
rewardedContentClaimIds,
showUri,
obscureNsfw,
claimIsMine,
fullWidth,
showLocal,
isDownloaded,
@ -68,10 +72,12 @@ class FileTile extends React.PureComponent<Props> {
const uri = normalizeURI(this.props.uri);
const isClaimed = !!claim;
const description = isClaimed && metadata && metadata.description ? metadata.description : '';
const title =
isClaimed && metadata && metadata.title ? metadata.title : parseURI(uri).contentName;
const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
const shouldObscureNsfw = obscureNsfw && metadata && metadata.nsfw && !claimIsMine;
const onClick = () => navigate('/show', { uri });
@ -92,7 +98,7 @@ class FileTile extends React.PureComponent<Props> {
role="button"
tabIndex="0"
>
<CardMedia title={title || name} thumbnail={thumbnail} />
<CardMedia title={title || name} thumbnail={thumbnail} nsfw={shouldObscureNsfw} />
<div className="file-tile__info">
{isResolvingUri && <div className="card__title--small">{__('Loading...')}</div>}
{!isResolvingUri && (
@ -104,9 +110,12 @@ class FileTile extends React.PureComponent<Props> {
{showUri ? uri : channel || __('Anonymous')}
{isRewardContent && <Icon icon={icons.FEATURED} />}
{showLocal && isDownloaded && <Icon icon={icons.LOCAL} />}
<div className="card__subtitle-price">
<FilePrice uri={uri} />
</div>
</div>
<div className="card__subtext card__subtext--small">
<TruncatedText lines={3}>{description}</TruncatedText>
</div>
<div className="card__subtitle-price">
<FilePrice uri={uri} />
</div>
{!name && (
<React.Fragment>

View file

@ -1,26 +1,17 @@
import React from 'react';
import { connect } from 'react-redux';
import InviteNew from './view';
import {
selectUserInvitesRemaining,
selectUserInviteNewIsPending,
selectUserInviteNewErrorMessage,
} from 'redux/selectors/user';
import rewards from 'rewards';
import { makeSelectRewardAmountByType } from 'redux/selectors/rewards';
import { doUserInviteNew } from 'redux/actions/user';
import InviteNew from './view';
const select = state => {
const selectReward = makeSelectRewardAmountByType();
return {
errorMessage: selectUserInviteNewErrorMessage(state),
invitesRemaining: selectUserInvitesRemaining(state),
isPending: selectUserInviteNewIsPending(state),
rewardAmount: selectReward(state, { reward_type: rewards.TYPE_REFERRAL }),
};
};
const select = state => ({
errorMessage: selectUserInviteNewErrorMessage(state),
invitesRemaining: selectUserInvitesRemaining(state),
isPending: selectUserInviteNewIsPending(state),
});
const perform = dispatch => ({
inviteNew: email => dispatch(doUserInviteNew(email)),

View file

@ -29,8 +29,7 @@ class FormInviteNew extends React.PureComponent {
}
render() {
const { errorMessage, isPending, rewardAmount } = this.props;
const label = `${__('Get')} ${rewardAmount} LBC`;
const { errorMessage, isPending } = this.props;
return (
<Form onSubmit={this.handleSubmit}>
@ -49,7 +48,7 @@ class FormInviteNew extends React.PureComponent {
/>
</FormRow>
<div className="card__actions">
<Submit label={label} disabled={isPending} />
<Submit label="Invite" disabled={isPending} />
</div>
</Form>
);

View file

@ -1,8 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import MarkdownPreview from './view';
const select = () => ({});
const perform = () => ({});
export default connect(select, perform)(MarkdownPreview);

View file

@ -1,32 +1,34 @@
// @flow
import * as React from 'react';
import Button from 'component/button';
import { buildURI } from 'lbry-redux';
import type { Claim } from 'types/claim';
type Props = {
uri: ?string,
editingURI: ?string,
isResolvingUri: boolean,
winningBidForClaimUri: ?number,
myClaimForUri: ?{},
onEditMyClaim: any => void,
myClaimForUri: ?Claim,
isStillEditing: boolean,
onEditMyClaim: (any, string) => void,
};
class BidHelpText extends React.PureComponent<Props> {
render() {
const {
uri,
editingURI,
isResolvingUri,
winningBidForClaimUri,
myClaimForUri,
onEditMyClaim,
isStillEditing,
} = this.props;
if (!uri) {
return __('Create a URL for this content');
}
if (uri === editingURI) {
if (isStillEditing) {
return __('You are currently editing this claim');
}
@ -35,11 +37,20 @@ class BidHelpText extends React.PureComponent<Props> {
}
if (myClaimForUri) {
const editUri = buildURI({
contentName: myClaimForUri.name,
claimId: myClaimForUri.claim_id,
});
return (
<React.Fragment>
{__('You already have a claim at')}
{` ${uri} `}
<Button button="link" label="Edit it" onClick={() => onEditMyClaim(myClaimForUri, uri)} />
<Button
button="link"
label="Edit it"
onClick={() => onEditMyClaim(myClaimForUri, editUri)}
/>
<br />
{__('Publishing will update your existing claim.')}
</React.Fragment>

View file

@ -1,15 +1,17 @@
// @flow
import * as React from 'react';
import { isNameValid, buildURI, regexInvalidURI } from 'lbry-redux';
import { isNameValid, buildURI, regexInvalidURI, THUMBNAIL_STATUSES } from 'lbry-redux';
import { Form, FormField, FormRow, FormFieldPrice, Submit } from 'component/common/form';
import Button from 'component/button';
import ChannelSection from 'component/selectChannel';
import classnames from 'classnames';
import type { PublishParams, UpdatePublishFormData } from 'redux/reducers/publish';
import FileSelector from 'component/common/file-selector';
import SelectThumbnail from 'component/selectThumbnail';
import { COPYRIGHT, OTHER } from 'constants/licenses';
import { CHANNEL_NEW, CHANNEL_ANONYMOUS, MINIMUM_PUBLISH_BID } from 'constants/claim';
import * as icons from 'constants/icons';
import type { Claim } from 'types/claim';
import BidHelpText from './internal/bid-help-text';
import LicenseType from './internal/license-type';
@ -20,6 +22,8 @@ type Props = {
editingURI: ?string,
title: ?string,
thumbnail: ?string,
uploadThumbnailStatus: ?string,
thumbnailPath: ?string,
description: ?string,
language: string,
nsfw: boolean,
@ -36,14 +40,7 @@ type Props = {
nameError: ?string,
isResolvingUri: boolean,
winningBidForClaimUri: number,
myClaimForUri: ?{
amount: number,
value: {
stream: {
source: { source: string },
},
},
},
myClaimForUri: ?Claim,
licenseType: string,
otherLicenseDescription: ?string,
licenseUrl: ?string,
@ -52,10 +49,12 @@ type Props = {
bidError: ?string,
publishing: boolean,
balance: number,
isStillEditing: boolean,
clearPublish: () => void,
resolveUri: string => void,
scrollToTop: () => void,
prepareEdit: ({}, uri) => void,
prepareEdit: ({}) => void,
resetThumbnailStatus: () => void,
};
class PublishForm extends React.PureComponent<Props> {
@ -73,7 +72,13 @@ class PublishForm extends React.PureComponent<Props> {
(this: any).getNewUri = this.getNewUri.bind(this);
}
// Returns a new uri to be used in the form and begins to resolve that uri for bid help text
componentWillMount() {
const { isStillEditing, thumbnail } = this.props;
if (!isStillEditing || !thumbnail) {
this.props.resetThumbnailStatus();
}
}
getNewUri(name: string, channel: string) {
const { resolveUri } = this.props;
// If they are midway through a channel creation, treat it as anonymous until it completes
@ -138,22 +143,26 @@ class PublishForm extends React.PureComponent<Props> {
handleChannelChange(channelName: string) {
const { name, updatePublishForm } = this.props;
const form = { channel: channelName };
if (name) {
const uri = this.getNewUri(name, channelName);
updatePublishForm({ channel: channelName, uri });
} else {
updatePublishForm({ channel: channelName });
form.uri = this.getNewUri(name, channelName);
}
updatePublishForm(form);
}
handleBidChange(bid: number) {
const { balance, updatePublishForm } = this.props;
let bidError;
if (balance <= bid) {
bidError = __('Not enough credits');
if (bid === 0) {
bidError = __('Deposit cannot be 0');
} else if (balance === bid) {
bidError = __('Please decrease your deposit to account for transaction fees');
} else if (balance < bid) {
bidError = __('Deposit cannot be higher than your balance');
} else if (bid <= MINIMUM_PUBLISH_BID) {
bidError = __('Your bid must be higher');
bidError = __('Your deposit must be higher');
}
updatePublishForm({ bid, bidError });
@ -175,24 +184,13 @@ class PublishForm extends React.PureComponent<Props> {
handlePublish() {
const {
publish,
filePath,
bid,
title,
thumbnail,
description,
language,
nsfw,
channel,
copyrightNotice,
licenseType,
licenseUrl,
otherLicenseDescription,
copyrightNotice,
name,
contentIsFree,
price,
uri,
myClaimForUri,
publish,
} = this.props;
let publishingLicense;
@ -211,21 +209,22 @@ class PublishForm extends React.PureComponent<Props> {
const publishParams = {
filePath,
bid,
title,
thumbnail,
description,
language,
nsfw,
channel,
bid: this.props.bid,
title: this.props.title,
thumbnail: this.props.thumbnail,
description: this.props.description,
language: this.props.language,
nsfw: this.props.nsfw,
license: publishingLicense,
licenseUrl: publishingLicenseUrl,
otherLicenseDescription,
copyrightNotice,
name,
contentIsFree,
price,
uri,
name: this.props.name,
contentIsFree: this.props.contentIsFree,
price: this.props.price,
uri: this.props.uri,
channel: this.props.channel,
isStillEditing: this.props.isStillEditing,
};
// Editing a claim
@ -273,6 +272,7 @@ class PublishForm extends React.PureComponent<Props> {
editingURI,
title,
thumbnail,
uploadThumbnailStatus,
description,
language,
nsfw,
@ -295,13 +295,14 @@ class PublishForm extends React.PureComponent<Props> {
bidError,
publishing,
clearPublish,
thumbnailPath,
resetThumbnailStatus,
isStillEditing,
} = this.props;
const formDisabled = (!filePath && !editingURI) || publishing;
const formValid = this.checkIsFormValid();
const simpleUri = uri && uri.split('#')[0];
const isStillEditing = editingURI === simpleUri;
let submitLabel;
if (isStillEditing) {
submitLabel = !publishing ? __('Edit') : __('Editing...');
@ -349,18 +350,6 @@ class PublishForm extends React.PureComponent<Props> {
onChange={e => updatePublishForm({ title: e.target.value })}
/>
</FormRow>
<FormRow padded>
<FormField
stretch
type="text"
name="content_thumbnail"
label={__('Thumbnail')}
placeholder="http://spee.ch/mylogo"
value={thumbnail}
disabled={formDisabled}
onChange={e => updatePublishForm({ thumbnail: e.target.value })}
/>
</FormRow>
<FormRow padded>
<FormField
stretch
@ -375,6 +364,30 @@ class PublishForm extends React.PureComponent<Props> {
</FormRow>
</section>
<section className="card card--section">
<div className="card__title">{__('Thumbnail')}</div>
<div className="card__subtitle">
{uploadThumbnailStatus === THUMBNAIL_STATUSES.API_DOWN ? (
__('Enter a url for your thumbnail.')
) : (
<React.Fragment>
{__(
'Upload your thumbnail to spee.ch, or enter the url manually. Learn more about spee.ch '
)}
<Button button="link" label={__('here')} href="https://spee.ch/about" />.
</React.Fragment>
)}
</div>
<SelectThumbnail
thumbnailPath={thumbnailPath}
thumbnail={thumbnail}
uploadThumbnailStatus={uploadThumbnailStatus}
updatePublishForm={updatePublishForm}
formDisabled={formDisabled}
resetThumbnailStatus={resetThumbnailStatus}
/>
</section>
<section className="card card--section">
<div className="card__title">{__('Price')}</div>
<div className="card__subtitle">{__('How much will this content cost?')}</div>
@ -447,7 +460,8 @@ class PublishForm extends React.PureComponent<Props> {
error={nameError}
helper={
<BidHelpText
uri={simpleUri}
isStillEditing={isStillEditing}
uri={uri}
editingURI={editingURI}
isResolvingUri={isResolvingUri}
winningBidForClaimUri={winningBidForClaimUri}
@ -554,7 +568,14 @@ class PublishForm extends React.PureComponent<Props> {
</section>
<div className="card__actions">
<Submit label={submitLabel} disabled={formDisabled || !formValid || publishing} />
<Submit
label={submitLabel}
disabled={
formDisabled ||
!formValid ||
uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS
}
/>
<Button button="alt" onClick={this.handleCancelPublish} label={__('Cancel')} />
</div>
{!formDisabled && !formValid && this.renderFormErrors()}

View file

@ -16,7 +16,7 @@ const makeSelect = () => {
const select = (state, props) => ({
errorMessage: selectError(state, props),
isPending: selectIsPending(state, props),
reward: selectReward(state, props),
reward: selectReward(state, props.reward_type),
});
return select;

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import { selectCurrentPage, selectCurrentParams } from 'lbry-redux';
import { selectCurrentPage, selectCurrentParams, doNotify } from 'lbry-redux';
import Router from './view';
const select = state => ({
@ -7,4 +7,4 @@ const select = state => ({
currentPage: selectCurrentPage(state),
});
export default connect(select, null)(Router);
export default connect(select, { doNotify })(Router);

View file

@ -17,17 +17,23 @@ import AuthPage from 'page/auth';
import InvitePage from 'page/invite';
import BackupPage from 'page/backup';
import SubscriptionsPage from 'page/subscriptions';
import SearchPage from 'page/search';
const route = (page, routesMap) => {
const route = (props, page, routesMap) => {
const component = routesMap[page];
return component || DiscoverPage;
if (!component) {
props.doNotify({
message: __('Invalid page requested'),
displayType: ['snackbar'],
});
}
return component || routesMap.discover;
};
const Router = props => {
const { currentPage, params } = props;
return route(currentPage, {
return route(props, currentPage, {
auth: <AuthPage params={params} />,
backup: <BackupPage params={params} />,
channel: <ChannelPage params={params} />,
@ -46,6 +52,7 @@ const Router = props => {
show: <ShowPage {...params} />,
wallet: <WalletPage params={params} />,
subscriptions: <SubscriptionsPage params={params} />,
search: <SearchPage {...params} />,
});
};

View file

@ -55,11 +55,13 @@ class ChannelSection extends React.PureComponent<Props, State> {
handleChannelChange(event: SyntheticInputEvent<*>) {
const { onChannelChange } = this.props;
const { newChannelBid } = this.state;
const channel = event.target.value;
if (channel === CHANNEL_NEW) {
this.setState({ addingChannel: true });
onChannelChange(channel);
this.handleNewChannelBidChange(newChannelBid);
} else {
this.setState({ addingChannel: false });
onChannelChange(channel);
@ -84,14 +86,15 @@ class ChannelSection extends React.PureComponent<Props, State> {
});
}
handleNewChannelBidChange(event: SyntheticInputEvent<*>) {
handleNewChannelBidChange(newChannelBid: number) {
const { balance } = this.props;
const newChannelBid = parseFloat(event.target.value);
let newChannelBidError;
if (newChannelBid === balance) {
newChannelBidError = __('Please decrease your bid to account for transaction fees');
if (newChannelBid === 0) {
newChannelBidError = __('Your deposit cannot be 0');
} else if (newChannelBid === balance) {
newChannelBidError = __('Please decrease your deposit to account for transaction fees');
} else if (newChannelBid > balance) {
newChannelBidError = __('Not enough credits');
newChannelBidError = __('Deposit cannot be higher than your balance');
}
this.setState({
@ -176,6 +179,7 @@ class ChannelSection extends React.PureComponent<Props, State> {
label={__('Name')}
type="text"
prefix="@"
placeholder={__('myChannelName')}
error={newChannelNameError}
value={newChannelName}
onChange={this.handleNewChannelNameChange}
@ -183,6 +187,7 @@ class ChannelSection extends React.PureComponent<Props, State> {
</FormRow>
<FormRow padded>
<FormField
className="input--price-amount"
label={__('Deposit')}
postfix="LBC"
step="any"
@ -193,7 +198,7 @@ class ChannelSection extends React.PureComponent<Props, State> {
)}
error={newChannelBidError}
value={newChannelBid}
onChange={this.handleNewChannelBidChange}
onChange={event => this.handleNewChannelBidChange(parseFloat(event.target.value))}
/>
</FormRow>
<div className="card__actions">

View file

@ -0,0 +1,12 @@
import { connect } from 'react-redux';
import { doNotify } from 'lbry-redux';
import SelectThumbnail from './view';
const perform = dispatch => ({
openModal: (modal, props) => dispatch(doNotify(modal, props)),
});
export default connect(
null,
perform
)(SelectThumbnail);

View file

@ -0,0 +1,90 @@
// @flow
import { THUMBNAIL_STATUSES, MODALS } from 'lbry-redux';
import React from 'react';
import { FormField, FormRow } from 'component/common/form';
import FileSelector from 'component/common/file-selector';
import Button from 'component/button';
type Props = {
thumbnail: ?string,
formDisabled: boolean,
uploadThumbnailStatus: string,
thumbnailPath: ?string,
openModal: ({ id: string }, {}) => void,
updatePublishForm: ({}) => void,
resetThumbnailStatus: () => void,
};
class SelectThumbnail extends React.PureComponent<Props> {
render() {
const {
thumbnail,
formDisabled,
uploadThumbnailStatus: status,
openModal,
updatePublishForm,
thumbnailPath,
resetThumbnailStatus,
} = this.props;
return (
<div>
{status === THUMBNAIL_STATUSES.API_DOWN || status === THUMBNAIL_STATUSES.MANUAL ? (
<FormRow padded>
<FormField
stretch
type="text"
name="content_thumbnail"
label={__('Url')}
placeholder="http://spee.ch/mylogo"
value={thumbnail}
disabled={formDisabled}
onChange={e => updatePublishForm({ thumbnail: e.target.value })}
/>
</FormRow>
) : (
<div className="form-row--padded">
{(status === THUMBNAIL_STATUSES.READY || status === THUMBNAIL_STATUSES.COMPLETE) && (
<FileSelector
currentPath={thumbnailPath}
fileLabel={__('Choose Thumbnail')}
onFileChosen={path => openModal({ id: MODALS.CONFIRM_THUMBNAIL_UPLOAD }, { path })}
/>
)}
{status === THUMBNAIL_STATUSES.COMPLETE && (
<div>
<p>
Upload complete. View it{' '}
<Button button="link" href={thumbnail} label={__('here')} />.
</p>
<Button button="link" label={__('New thumbnail')} onClick={resetThumbnailStatus} />
</div>
)}
</div>
)}
<div className="card__actions">
{status === THUMBNAIL_STATUSES.READY && (
<Button
button="link"
label={__('Or enter a URL manually')}
onClick={() =>
updatePublishForm({ uploadThumbnailStatus: THUMBNAIL_STATUSES.MANUAL })
}
/>
)}
{status === THUMBNAIL_STATUSES.MANUAL && (
<Button
button="link"
label={__('Use thumbnail upload tool')}
onClick={() => updatePublishForm({ uploadThumbnailStatus: THUMBNAIL_STATUSES.READY })}
/>
)}
</div>
{status === THUMBNAIL_STATUSES.IN_PROGRESS && <p>{__('Uploading thumbnail')}...</p>}
</div>
);
}
}
export default SelectThumbnail;

View file

@ -19,7 +19,10 @@ class LoadScreen extends React.PureComponent<Props> {
return (
<div className="load-screen">
<h1 className="load-screen__title">{__('LBRY')}</h1>
<div className="load-screen__header">
<h1 className="load-screen__title">{__('LBRY')}</h1>
<sup className="load-screen__beta">beta</sup>
</div>
{isWarning ? (
<span className="load-screen__message">
<Icon size={20} icon={icons.ALERT} />

View file

@ -37,8 +37,9 @@ export default (props: Props) => {
return channelName && uri ? (
<Button
iconColor="red"
icon={isSubscribed ? undefined : icons.HEART}
button={isSubscribed ? 'danger' : 'alt'}
button="alt"
label={subscriptionLabel}
onClick={() => {
if (!subscriptions.length) {

View file

@ -96,6 +96,7 @@ class TransactionList extends React.PureComponent<Props, State> {
type="select"
value={filter}
onChange={this.handleFilterChanged}
affixClass="form-field--align-center"
prefix={__('Show')}
postfix={
<Button

View file

@ -1,6 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { doUserEmailVerify, doUserEmailVerifyFailure } from 'redux/actions/user';
import {
doUserEmailVerify,
doUserEmailVerifyFailure,
doUserResendVerificationEmail,
} from 'redux/actions/user';
import {
selectEmailVerifyIsPending,
selectEmailToVerify,
@ -17,6 +20,7 @@ const select = state => ({
const perform = dispatch => ({
verifyUserEmail: (code, recaptcha) => dispatch(doUserEmailVerify(code, recaptcha)),
verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)),
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
});
export default connect(select, perform)(UserEmailVerify);

View file

@ -1,5 +1,5 @@
// @flow
import React from 'react';
import * as React from 'react';
import Button from 'component/button';
import { Form, FormField, FormRow, Submit } from 'component/common/form';
@ -10,6 +10,7 @@ type Props = {
isPending: boolean,
verifyUserEmail: (string, string) => void,
verifyUserEmailFailure: string => void,
resendVerificationEmail: string => void,
};
type State = {
@ -25,6 +26,7 @@ class UserEmailVerify extends React.PureComponent<Props, State> {
};
(this: any).handleSubmit = this.handleSubmit.bind(this);
(this: any).handleResendVerificationEmail = this.handleResendVerificationEmail.bind(this);
}
handleCodeChanged(event: SyntheticInputEvent<*>) {
@ -43,6 +45,10 @@ class UserEmailVerify extends React.PureComponent<Props, State> {
}
}
handleResendVerificationEmail() {
this.props.resendVerificationEmail(this.props.email);
}
render() {
const { cancelButton, errorMessage, email, isPending } = this.props;
@ -71,6 +77,11 @@ class UserEmailVerify extends React.PureComponent<Props, State> {
<div className="card__actions">
<Submit label={__('Verify')} disabled={isPending} />
{cancelButton}
<Button
button="link"
label={__('Resend verification email')}
onClick={this.handleResendVerificationEmail}
/>
</div>
</Form>
);

View file

@ -1,4 +1,5 @@
import { connect } from 'react-redux';
import { doNotify, MODALS } from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import { doUserIdentityVerify } from 'redux/actions/user';
import rewards from 'rewards';
@ -8,15 +9,14 @@ import {
selectIdentityVerifyErrorMessage,
} from 'redux/selectors/user';
import UserVerify from './view';
import { doNotify, MODALS } from 'lbry-redux';
const select = (state, props) => {
const select = state => {
const selectReward = makeSelectRewardByType();
return {
isPending: selectIdentityVerifyIsPending(state),
errorMessage: selectIdentityVerifyErrorMessage(state),
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
reward: selectReward(state, rewards.TYPE_NEW_USER),
};
};

View file

@ -46,6 +46,7 @@ const select = (state, props) => ({
const perform = dispatch => ({
play: uri => dispatch(doPlayUri(uri)),
load: uri => dispatch(doLoadVideo(uri)),
cancelPlay: () => dispatch(doSetPlayingUri(null)),
changeVolume: volume => dispatch(doChangeVolume(volume)),
doPlay: () => dispatch(doPlay()),
doPause: () => dispatch(doPause()),

View file

@ -17,9 +17,7 @@ class VideoPlayButton extends React.PureComponent<Props> {
const icon = doesPlayback ? 'Play' : 'Folder';
const label = doesPlayback ? 'Play' : 'View';
return (
<Button button="secondary" disabled={disabled} label={label} icon={icon} onClick={play} />
);
return <Button button="primary" disabled={disabled} label={label} icon={icon} onClick={play} />;
}
}

View file

@ -132,22 +132,11 @@ class Video extends React.PureComponent<Props> {
}
handleAutoplay = (props: Props) => {
const {
autoplay,
playingUri,
fileInfo,
costInfo,
isDownloading,
uri,
load,
play,
metadata,
} = props;
const { autoplay, playingUri, fileInfo, costInfo, isDownloading, uri, play, metadata } = props;
const playable = autoplay && playingUri !== uri && metadata && !metadata.nsfw;
if (playable && costInfo && costInfo.cost === 0 && !fileInfo && !isDownloading) {
load(uri);
play(uri);
} else if (playable && fileInfo && fileInfo.blobs_completed > 0) {
play(uri);
@ -263,7 +252,10 @@ class Video extends React.PureComponent<Props> {
style={layoverStyle}
>
<VideoPlayButton
play={this.playContent}
play={e => {
e.stopPropagation();
this.playContent();
}}
fileInfo={fileInfo}
uri={uri}
isLoading={isLoading}

View file

@ -0,0 +1,3 @@
import ViewOnWebButton from './view';
export default ViewOnWebButton;

View file

@ -0,0 +1,25 @@
// @flow
import React from 'react';
import * as icons from 'constants/icons';
import Button from 'component/button';
type Props = {
claimId: ?string,
claimName: ?string,
};
export default (props: Props) => {
const { claimId, claimName } = props;
const speechURL = claimName.startsWith('@')
? `${claimName}:${claimId}`
: `${claimId}/${claimName}`;
return claimId && claimName ? (
<Button
icon={icons.GLOBE}
button="alt"
label={__('View on Web')}
href={`http://spee.ch/${speechURL}`}
/>
) : null;
};

View file

@ -2,6 +2,7 @@
import React from 'react';
import Button from 'component/button';
import Address from 'component/address';
import QRCode from 'component/common/qr-code';
import * as icons from 'constants/icons';
type Props = {
@ -12,6 +13,20 @@ type Props = {
};
class WalletAddress extends React.PureComponent<Props> {
constructor(props: Props) {
super(props);
this.state = {
showQR: false,
};
}
toggleQR() {
this.setState({
showQR: !this.state.showQR,
});
}
componentWillMount() {
const { checkAddressIsMine, receiveAddress, getNewAddress } = this.props;
if (!receiveAddress) {
@ -23,6 +38,7 @@ class WalletAddress extends React.PureComponent<Props> {
render() {
const { receiveAddress, getNewAddress, gettingNewAddress } = this.props;
const { showQR } = this.state;
return (
<section className="card card--section">
@ -43,7 +59,17 @@ class WalletAddress extends React.PureComponent<Props> {
onClick={getNewAddress}
disabled={gettingNewAddress}
/>
<Button
button="link"
label={showQR ? __('Hide QR code') : __('Show QR code')}
onClick={this.toggleQR.bind(this)}
/>
</div>
<div className="card__content">
{showQR && <QRCode value={receiveAddress} paddingTop />}
</div>
<div className="card__content">
<div className="help">
<p>

View file

@ -1,9 +1,9 @@
import { connect } from 'react-redux';
import { doSendDraftTransaction, selectBalance } from 'lbry-redux';
import { selectBalance, doNotify } from 'lbry-redux';
import WalletSend from './view';
const perform = dispatch => ({
sendToAddress: (address, amount) => dispatch(doSendDraftTransaction(address, amount)),
openModal: (modal, props) => dispatch(doNotify(modal, props)),
});
const select = state => ({

View file

@ -1,5 +1,6 @@
// @flow
import React from 'react';
import { MODALS } from 'lbry-redux';
import Button from 'component/button';
import { Form, FormRow, FormField } from 'component/common/form';
import { Formik } from 'formik';
@ -11,7 +12,7 @@ type DraftTransaction = {
};
type Props = {
sendToAddress: (string, number) => void,
openModal: ({ id: string }, { address: string, amount: number }) => void,
balance: number,
};
@ -23,10 +24,12 @@ class WalletSend extends React.PureComponent<Props> {
}
handleSubmit(values: DraftTransaction) {
const { sendToAddress } = this.props;
const { openModal } = this.props;
const { address, amount } = values;
if (amount && address) {
sendToAddress(address, amount);
const notificationId = { id: MODALS.CONFIRM_TRANSACTION };
const modalProps = { address, amount };
openModal(notificationId, modalProps);
}
}
@ -53,17 +56,22 @@ class WalletSend extends React.PureComponent<Props> {
label={__('Amount')}
postfix={__('LBC')}
className="input--price-amount"
affixClass="form-field--fix-no-height"
min="0"
step="any"
placeholder="12.34"
onChange={handleChange}
onBlur={handleBlur}
value={values.amount}
error={
(!!values.amount && touched.amount && errors.amount) ||
(values.amount > balance && __('Not enough'))
(values.amount === balance &&
__('Decrease amount to account for transaction fee')) ||
(values.amount > balance && __('Not enough credits'))
}
/>
</FormRow>
<FormRow padded>
<FormField
type="text"
name="address"
@ -84,7 +92,8 @@ class WalletSend extends React.PureComponent<Props> {
disabled={
!values.address ||
!!Object.keys(errors).length ||
!(parseFloat(values.amount) > 0.0)
!(parseFloat(values.amount) > 0.0) ||
parseFloat(values.amount) === balance
}
/>
</div>

View file

@ -9,7 +9,6 @@ type Props = {
uri: string,
title: string,
claim: Claim,
errorMessage: string,
isPending: boolean,
sendSupport: (number, string, string) => void,
onCancel: () => void,
@ -18,7 +17,8 @@ type Props = {
};
type State = {
amount: number,
tipAmount: number,
newTipError: string,
};
class WalletSendTip extends React.PureComponent<Props, State> {
@ -26,7 +26,8 @@ class WalletSendTip extends React.PureComponent<Props, State> {
super(props);
this.state = {
amount: 0,
tipAmount: 0,
newTipError: '',
};
(this: any).handleSendButtonClicked = this.handleSendButtonClicked.bind(this);
@ -35,9 +36,9 @@ class WalletSendTip extends React.PureComponent<Props, State> {
handleSendButtonClicked() {
const { claim, uri, sendSupport, sendTipCallback } = this.props;
const { claim_id: claimId } = claim;
const { amount } = this.state;
const { tipAmount } = this.state;
sendSupport(amount, claimId, uri);
sendSupport(tipAmount, claimId, uri);
// ex: close modal
if (sendTipCallback) {
@ -46,14 +47,24 @@ class WalletSendTip extends React.PureComponent<Props, State> {
}
handleSupportPriceChange(event: SyntheticInputEvent<*>) {
const { balance } = this.props;
const tipAmount = parseFloat(event.target.value);
let newTipError;
if (tipAmount === balance) {
newTipError = __('Please decrease your tip to account for transaction fees');
} else if (tipAmount > balance) {
newTipError = __('Not enough credits');
}
this.setState({
amount: Number(event.target.value),
tipAmount,
newTipError,
});
}
render() {
const { title, errorMessage, isPending, uri, onCancel, balance } = this.props;
const { amount } = this.state;
const { title, isPending, uri, onCancel, balance } = this.props;
const { tipAmount, newTipError } = this.state;
return (
<div>
@ -67,7 +78,8 @@ class WalletSendTip extends React.PureComponent<Props, State> {
label={__('Amount')}
postfix={__('LBC')}
className="input--price-amount"
error={errorMessage}
error={newTipError}
value={tipAmount}
min="0"
step="any"
type="number"
@ -84,10 +96,15 @@ class WalletSendTip extends React.PureComponent<Props, State> {
<Button
button="primary"
label={__('Send')}
disabled={isPending || amount <= 0 || amount > balance}
disabled={isPending || tipAmount <= 0 || tipAmount > balance || tipAmount === balance}
onClick={this.handleSendButtonClicked}
/>
<Button button="alt" label={__('Cancel')} onClick={onCancel} navigateParams={{ uri }} />
<Button
button="link"
label={__('Cancel')}
onClick={onCancel}
navigateParams={{ uri }}
/>
</div>
</div>
</div>

View file

@ -7,6 +7,7 @@ import {
MODALS,
doFocusSearchInput,
doBlurSearchInput,
doSearch,
} from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import Wunderbar from './view';
@ -27,8 +28,8 @@ const select = state => {
const perform = dispatch => ({
onSearch: query => {
dispatch(doUpdateSearchQuery(query));
dispatch(doNotify({ id: MODALS.SEARCH }));
dispatch(doSearch(query));
dispatch(doNavigate(`/search`, { query }));
},
onSubmit: (uri, extraParams) => dispatch(doNavigate('/show', { uri, ...extraParams })),
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),

View file

@ -106,7 +106,7 @@ class WunderBar extends React.PureComponent<Props> {
<input
{...props}
className="wunderbar__input"
placeholder="Search for videos, music, games and more"
placeholder="Enter LBRY URL here or search for videos, music, games and more"
/>
)}
renderItem={({ value, type, shorthand }, isHighlighted) => (

View file

@ -117,6 +117,7 @@ export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE';
export const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED';
export const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS';
export const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE';
export const USER_EMAIL_VERIFY_RETRY = 'USER_EMAIL_VERIFY_RETRY';
export const USER_PHONE_RESET = 'USER_PHONE_RESET';
export const USER_PHONE_NEW_STARTED = 'USER_PHONE_NEW_STARTED';
export const USER_PHONE_NEW_SUCCESS = 'USER_PHONE_NEW_SUCCESS';

View file

@ -28,3 +28,4 @@ export const CHECK_SIMPLE = 'Check';
export const PLAY = 'Play';
export const MAXIMIZE = 'Maximize2';
export const PAUSE = 'Pause';
export const GLOBE = 'Globe';

View file

@ -0,0 +1,5 @@
export const API_DOWN = 'apiDown';
export const READY = 'ready';
export const IN_PROGRESS = 'inProgress';
export const COMPLETE = 'complete';
export const MANUAL = 'manual';

View file

@ -8,7 +8,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { doConditionalAuthNavigate, doDaemonReady, doAutoUpdate } from 'redux/actions/app';
import { doNotify, doBlackListedOutpointsSubscribe } from 'lbry-redux';
import { doNotify, doBlackListedOutpointsSubscribe, isURIValid } from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import { doDownloadLanguages, doUpdateIsNightAsync } from 'redux/actions/settings';
import { doUserEmailVerify } from 'redux/actions/user';
@ -17,8 +17,10 @@ import store from 'store';
import app from './app';
import analytics from './analytics';
import VideoOverlay from './component/videoOverlay/';
import doLogWarningConsoleMessage from './logWarningConsoleMessage';
const { autoUpdater } = remote.require('electron-updater');
const APPPAGEURL = 'lbry://?';
autoUpdater.logger = remote.require('electron-log');
@ -42,8 +44,18 @@ ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
})
);
}
} else {
} else if (uri.startsWith(APPPAGEURL)) {
const navpage = uri.replace(APPPAGEURL, '').toLowerCase();
app.store.dispatch(doNavigate(`/${navpage}`));
} else if (isURIValid(uri)) {
app.store.dispatch(doNavigate('/show', { uri }));
} else {
app.store.dispatch(
doNotify({
message: __('Invalid LBRY URL requested'),
displayType: ['snackbar'],
})
);
}
}
});
@ -62,6 +74,11 @@ ipcRenderer.on('window-is-focused', () => {
dock.setBadge('');
});
ipcRenderer.on('devtools-is-opened', () => {
const logOnDevelopment = false;
doLogWarningConsoleMessage(logOnDevelopment);
});
document.addEventListener('dragover', event => {
event.preventDefault();
});

View file

@ -0,0 +1,34 @@
import isDev from 'electron-is-dev';
export default function doLogWarningConsoleMessage(activeOnDev = false) {
if (isDev && !activeOnDev) return;
const style = {
redTitle:
'color: red; font-size: 50px; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;, font-weight: bold;',
normalText: 'font-size: 24px;',
redText:
'color: red; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; font-size: 24px;',
};
console.clear();
console.log('%cScam alert!', style.redTitle);
console.log(
'%cIf someone told you to copy / paste something here you have a chance of being scammed.',
style.normalText
);
console.log(
'%cPasting anything in here could give attackers access to your LBC credits or wallet.',
style.normalText
);
console.log(
"%cIf you don't understand what you are doing here, please close this window and keep your LBC credits/wallet safe.",
style.redText
);
console.log(
'%cIf you do understand exactly what you are doing, you should come work with us https://lbry.io/join-us',
style.normalText
);
}

View file

@ -56,7 +56,7 @@ export class Modal extends React.PureComponent<ModalProps> {
return (
<ReactModal
{...modalProps}
onCloseRequested={onAborted || onConfirmed}
onRequestClose={onAborted || onConfirmed}
className={classnames(className, {
modal: !fullScreen,
'modal--fullscreen': fullScreen,

View file

@ -3,6 +3,12 @@ import FilePrice from 'component/filePrice';
import { Modal } from 'modal/modal';
class ModalAffirmPurchase extends React.PureComponent {
constructor() {
super();
this.onAffirmPurchase = this.onAffirmPurchase.bind(this);
}
onAffirmPurchase() {
this.props.closeModal();
this.props.loadVideo(this.props.uri);
@ -20,7 +26,7 @@ class ModalAffirmPurchase extends React.PureComponent {
type="confirm"
isOpen
contentLabel={__('Confirm Purchase')}
onConfirmed={this.onAffirmPurchase.bind(this)}
onConfirmed={this.onAffirmPurchase}
onAborted={cancelPurchase}
>
{__('This will purchase')} <strong>{title}</strong> {__('for')}{' '}

View file

@ -0,0 +1,21 @@
import { connect } from 'react-redux';
import { doHideNotification } from 'lbry-redux';
import { doUploadThumbnail, doUpdatePublishForm } from 'redux/actions/publish';
import { selectPublishFormValues } from 'redux/selectors/publish';
import ModalConfirmThumbnailUpload from './view';
const select = state => {
const publishState = selectPublishFormValues(state);
return { nsfw: publishState.nsfw };
};
const perform = dispatch => ({
closeModal: () => dispatch(doHideNotification()),
upload: (path, nsfw = false) => dispatch(doUploadThumbnail(path, nsfw)),
updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
});
export default connect(
select,
perform
)(ModalConfirmThumbnailUpload);

View file

@ -0,0 +1,48 @@
// @flow
import React from 'react';
import { Modal } from 'modal/modal';
import { FormField } from 'component/common/form';
type Props = {
upload: (string, boolean) => void,
path: string,
nsfw: boolean,
closeModal: () => void,
updatePublishForm: ({}) => void,
};
class ModalConfirmThumbnailUpload extends React.PureComponent<Props> {
upload() {
const { upload, updatePublishForm, closeModal, path, nsfw } = this.props;
upload(path, nsfw);
updatePublishForm({ thumbnailPath: path });
closeModal();
}
render() {
const { closeModal, path, updatePublishForm, nsfw } = this.props;
return (
<Modal
isOpen
contentLabel={__('Confirm Thumbnail Upload')}
type="confirm"
confirmButtonLabel={__('Upload')}
onConfirmed={() => this.upload()}
onAborted={closeModal}
>
<p>{__('Are you sure you want to upload this thumbnail to spee.ch')}?</p>
<blockquote>{path}</blockquote>
<FormField
type="checkbox"
name="content_is_mature"
postfix={__('Mature audiences only')}
checked={nsfw}
onChange={event => updatePublishForm({ nsfw: event.target.checked })}
/>
</Modal>
);
}
}
export default ModalConfirmThumbnailUpload;

View file

@ -0,0 +1,10 @@
import { connect } from 'react-redux';
import { doHideNotification, doSendDraftTransaction } from 'lbry-redux';
import ModalConfirmTransaction from './view';
const perform = dispatch => ({
closeModal: () => dispatch(doHideNotification()),
sendToAddress: (address, amount) => dispatch(doSendDraftTransaction(address, amount)),
});
export default connect(null, perform)(ModalConfirmTransaction);

View file

@ -0,0 +1,44 @@
// @flow
import React from 'react';
import { Modal } from 'modal/modal';
type Props = {
address: string,
amount: number,
closeModal: () => void,
sendToAddress: (string, number) => void,
};
class ModalConfirmTransaction extends React.PureComponent<Props> {
onConfirmed() {
const { closeModal, sendToAddress, amount, address } = this.props;
sendToAddress(address, amount);
closeModal();
}
render() {
const { amount, address, closeModal } = this.props;
return (
<Modal
isOpen
contentLabel={__('Confirm Transaction')}
type="confirm"
confirmButtonLabel={__('Continue')}
onConfirmed={() => this.onConfirmed()}
onAborted={closeModal}
>
<p>{__('Are you sure you want to ')}</p>
<h1>
{__('send ')} {amount} LBC
</h1>
<p>{__('Sending: ')}</p>
<blockquote>{amount} LBC</blockquote>
<p>{__('To address: ')}</p>
<blockquote>{address}</blockquote>
<p>{__('Once the transaction is sent, it cannot be reversed.')}</p>
</Modal>
);
}
}
export default ModalConfirmTransaction;

View file

@ -1,14 +1,20 @@
// I'll come back to this
/* esline-disable */
// @flow
import React from 'react';
import { Modal } from 'modal/modal';
import CurrencySymbol from 'component/common/lbc-symbol';
import CreditAmount from 'component/common/credit-amount';
import Button from 'component/button';
const ModalCreditIntro = props => {
type Props = {
totalRewardValue: number,
currentBalance: number,
closeModal: () => void,
addBalance: () => void,
};
const ModalCreditIntro = (props: Props) => {
const { closeModal, totalRewardValue, currentBalance, addBalance } = props;
const totalRewardRounded = Math.round(totalRewardValue / 10) * 10;
const totalRewardRounded = Math.floor(totalRewardValue / 10) * 10;
return (
<Modal type="custom" isOpen contentLabel="Welcome to LBRY">
@ -25,16 +31,13 @@ const ModalCreditIntro = props => {
can take are limited.
</p>
)}
<p>
There are a variety of ways to get credits, including more than{' '}
{totalRewardValue ? (
<CreditAmount noStyle amount={totalRewardRounded} />
) : (
<span className="credit-amount">{__('?? credits')}</span>
)}{' '}
{__(' in free rewards for participating in the LBRY beta.')}
</p>
{Boolean(totalRewardValue) && (
<p>
There are a variety of ways to get credits, including more than{' '}
<CreditAmount noStyle amount={totalRewardRounded} />{' '}
{__('in free rewards for participating in the LBRY beta.')}
</p>
)}
<div className="card__actions card__actions--center">
<Button button="primary" onClick={addBalance} label={__('Get Credits')} />
<Button
@ -49,4 +52,3 @@ const ModalCreditIntro = props => {
};
export default ModalCreditIntro;
/* esline-enable */

View file

@ -34,7 +34,7 @@ class ModalDownloading extends React.PureComponent {
/>
) : null}
<Button
button="alt"
button="link"
label={__('Cancel')}
className="modal__button"
onClick={cancelUpgrade}

View file

@ -8,7 +8,7 @@ const select = state => {
const selectReward = makeSelectRewardByType();
return {
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
reward: selectReward(state, rewards.TYPE_NEW_USER),
};
};

View file

@ -6,7 +6,7 @@ import CreditAmount from 'component/common/credit-amount';
class ModalFirstReward extends React.PureComponent {
render() {
const { closeModal, reward } = this.props;
const { closeModal } = this.props;
return (
<Modal
@ -18,10 +18,7 @@ class ModalFirstReward extends React.PureComponent {
>
<section>
<h3 className="modal__header">{__('Your First Reward')}</h3>
<p>
{__('You just earned your first reward of')}{' '}
<CreditAmount amount={reward.reward_amount} />.
</p>
<p>{__('You just earned your first reward!')}</p>
<p>
{__(
"This reward will show in your Wallet in the top right momentarily (if it hasn't already)."

View file

@ -18,12 +18,17 @@ import ModalRevokeClaim from 'modal/modalRevokeClaim';
import ModalEmailCollection from 'modal/modalEmailCollection';
import ModalPhoneCollection from 'modal/modalPhoneCollection';
import ModalFirstSubscription from 'modal/modalFirstSubscription';
import ModalConfirmTransaction from 'modal/modalConfirmTransaction';
import ModalSendTip from '../modalSendTip';
import ModalPublish from '../modalPublish';
import ModalSearch from '../modalSearch';
import ModalOpenExternalLink from '../modalOpenExternalLink';
import ModalConfirmThumbnailUpload from 'modal/modalConfirmThumbnailUpload';
class ModalRouter extends React.PureComponent {
type Props = {
modal: string,
};
class ModalRouter extends React.PureComponent<Props> {
constructor(props) {
super(props);
@ -56,7 +61,7 @@ class ModalRouter extends React.PureComponent {
if (
transitionModal &&
(transitionModal != this.state.lastTransitionModal || page != this.state.lastTransitionPage)
(transitionModal !== this.state.lastTransitionModal || page !== this.state.lastTransitionPage)
) {
openModal({ id: transitionModal });
this.setState({
@ -89,7 +94,7 @@ class ModalRouter extends React.PureComponent {
const { balance, page, isCreditIntroAcknowledged } = props;
if (
balance <= 0 &&
balance === 0 &&
!isCreditIntroAcknowledged &&
(['send', 'publish'].includes(page) || this.isPaidShowPage(props))
) {
@ -154,10 +159,12 @@ class ModalRouter extends React.PureComponent {
return <ModalSendTip {...notificationProps} />;
case MODALS.PUBLISH:
return <ModalPublish {...notificationProps} />;
case MODALS.SEARCH:
return <ModalSearch {...notificationProps} />;
case MODALS.CONFIRM_EXTERNAL_LINK:
return <ModalOpenExternalLink {...notificationProps} />;
case MODALS.CONFIRM_TRANSACTION:
return <ModalConfirmTransaction {...notificationProps} />;
case MODALS.CONFIRM_THUMBNAIL_UPLOAD:
return <ModalConfirmThumbnailUpload {...notificationProps} />;
default:
return null;
}

View file

@ -1,13 +0,0 @@
import { connect } from 'react-redux';
import { doHideNotification } from 'lbry-redux';
import ModalSearch from './view';
import { doClearPublish } from 'redux/actions/publish';
import { doNavigate } from 'redux/actions/navigation';
const perform = dispatch => ({
closeModal: () => dispatch(doHideNotification()),
clearPublish: () => dispatch(doClearPublish()),
navigate: (path, params) => dispatch(doNavigate(path, params)),
});
export default connect(null, perform)(ModalSearch);

View file

@ -1,25 +0,0 @@
// @flow
import React from 'react';
import { Modal } from 'modal/modal';
import SearchPage from 'page/search';
import Button from 'component/button';
import * as icons from 'constants/icons';
type Props = {
closeModal: () => void,
query: string,
};
class ModalSearch extends React.PureComponent<Props> {
render() {
const { closeModal, query } = this.props;
return (
<Modal isOpen type="custom" fullScreen>
<Button noPadding button="alt" icon={icons.CLOSE} onClick={closeModal} />
<SearchPage />
</Modal>
);
}
}
export default ModalSearch;

View file

@ -1,13 +1,19 @@
import React from 'react';
import { connect } from 'react-redux';
import { doHideNotification } from 'lbry-redux';
import { doDownloadUpgrade, doSkipUpgrade } from 'redux/actions/app';
import ModalUpgrade from './view';
const select = state => ({});
const select = () => ({});
const perform = dispatch => ({
downloadUpgrade: () => dispatch(doDownloadUpgrade()),
skipUpgrade: () => dispatch(doSkipUpgrade()),
skipUpgrade: () => {
dispatch(doHideNotification());
dispatch(doSkipUpgrade());
},
});
export default connect(select, perform)(ModalUpgrade);
export default connect(
select,
perform
)(ModalUpgrade);

View file

@ -24,7 +24,11 @@ class ModalUpgrade extends React.PureComponent {
</p>
<p className="meta text-center">
{__('Want to know what has changed?')} See the{' '}
<Button label={__('release notes')} href="https://github.com/lbryio/lbry-app/releases" />.
<Button
button="link"
label={__('release notes')}
href="https://github.com/lbryio/lbry-app/releases"
/>.
</p>
</Modal>
);

View file

@ -6,7 +6,6 @@ import {
makeSelectFetchingChannelClaims,
makeSelectCurrentParam,
selectCurrentParams,
doNotify,
} from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import { makeSelectTotalPagesForChannel } from 'redux/selectors/content';
@ -25,7 +24,6 @@ const perform = dispatch => ({
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
fetchClaimCount: uri => dispatch(doFetchClaimCountByChannel(uri)),
navigate: (path, params) => dispatch(doNavigate(path, params)),
openModal: (modal, props) => dispatch(doNotify(modal, props)),
});
export default connect(select, perform)(ChannelPage);

View file

@ -4,6 +4,7 @@ import BusyIndicator from 'component/common/busy-indicator';
import { FormField, FormRow } from 'component/common/form';
import ReactPaginate from 'react-paginate';
import SubscribeButton from 'component/subscribeButton';
import ViewOnWebButton from 'component/viewOnWebButton';
import Page from 'component/page';
import FileList from 'component/fileList';
import type { Claim } from 'types/claim';
@ -47,17 +48,24 @@ class ChannelPage extends React.PureComponent<Props> {
this.props.navigate('/show', newParams);
}
paginate(e, totalPages) {
paginate(e, totalPages: number) {
// Change page if enter was pressed, and the given page is between
// the first and the last.
if (e.keyCode === 13 && e.target.value > 0 && e.target.value <= totalPages) {
this.changePage(e.target.value);
const pageFromInput = Number(e.target.value);
if (
e.keyCode === 13 &&
!Number.isNaN(pageFromInput) &&
pageFromInput > 0 &&
pageFromInput <= totalPages
) {
this.changePage(pageFromInput);
}
}
render() {
const { fetching, claimsInChannel, claim, page, totalPages } = this.props;
const { name, permanent_url: permanentUrl } = claim;
const { name, permanent_url: permanentUrl, claim_id: claimId } = claim;
let contentList;
if (fetching) {
@ -77,6 +85,7 @@ class ChannelPage extends React.PureComponent<Props> {
<h1>{name}</h1>
<div className="card__actions card__actions--no-margin">
<SubscribeButton uri={permanentUrl} channelName={name} />
<ViewOnWebButton claimId={claimId} claimName={name} />
</div>
</section>
<section>{contentList}</section>

View file

@ -1,7 +1,7 @@
// @flow
import React from 'react';
import Page from 'component/page';
import CategoryList from 'component/common/category-list';
import CategoryList from 'component/categoryList';
type Props = {
fetchFeaturedUris: () => void,
@ -27,7 +27,11 @@ class DiscoverPage extends React.PureComponent<Props> {
featuredUris[category].length ? (
<CategoryList key={category} category={category} names={featuredUris[category]} />
) : (
''
<CategoryList
key={category}
category={category.split('#')[0]}
categoryLink={category}
/>
)
)}
{failedToLoad && <div className="empty">{__('Failed to load landing content.')}</div>}

View file

@ -7,16 +7,21 @@ import FilePrice from 'component/filePrice';
import FileDetails from 'component/fileDetails';
import FileActions from 'component/fileActions';
import UriIndicator from 'component/uriIndicator';
import { FormField, FormRow } from 'component/common/form';
import Icon from 'component/common/icon';
import DateTime from 'component/dateTime';
import * as icons from 'constants/icons';
import Button from 'component/button';
import SubscribeButton from 'component/subscribeButton';
import ViewOnWebButton from 'component/viewOnWebButton';
import Page from 'component/page';
import player from 'render-media';
import * as settings from 'constants/settings';
import type { Claim } from 'types/claim';
import type { Subscription } from 'types/subscription';
import FileDownloadLink from 'component/fileDownloadLink';
import classnames from 'classnames';
import { FormField, FormRow } from 'component/common/form';
import ToolTip from 'component/common/tooltip';
type Props = {
claim: Claim,
@ -30,8 +35,6 @@ type Props = {
uri: string,
rewardedContentClaimIds: Array<string>,
obscureNsfw: boolean,
playingUri: ?string,
isPaused: boolean,
claimIsMine: boolean,
autoplay: boolean,
costInfo: ?{},
@ -39,11 +42,11 @@ type Props = {
openModal: ({ id: string }, { uri: string }) => void,
fetchFileInfo: string => void,
fetchCostInfo: string => void,
prepareEdit: ({}) => void,
prepareEdit: ({}, string) => void,
setClientSetting: (string, boolean | string) => void,
checkSubscription: ({ channelName: string, uri: string }) => void,
subscriptions: Array<{}>,
fromOverlay: boolean,
subscriptions: Array<Subscription>,
};
class FilePage extends React.Component<Props> {
@ -54,15 +57,14 @@ class FilePage extends React.Component<Props> {
}
componentDidMount() {
const { uri, fileInfo, fetchFileInfo, costInfo, fetchCostInfo } = this.props;
const { uri, fileInfo, fetchFileInfo, fetchCostInfo } = this.props;
if (fileInfo === undefined) {
fetchFileInfo(uri);
}
if (costInfo === undefined) {
fetchCostInfo(uri);
}
// See https://github.com/lbryio/lbry-app/pull/1563 for discussion
fetchCostInfo(uri);
this.checkSubscription(this.props);
}
@ -79,12 +81,7 @@ class FilePage extends React.Component<Props> {
}
checkSubscription = (props: Props) => {
if (
props.claim.value.publisherSignature &&
props.subscriptions
.map(subscription => subscription.channelName)
.indexOf(props.claim.channel_name) !== -1
) {
if (props.subscriptions.find(sub => sub.channelName === props.claim.channel_name)) {
props.checkSubscription({
channelName: props.claim.channel_name,
uri: buildURI(
@ -106,14 +103,13 @@ class FilePage extends React.Component<Props> {
uri,
rewardedContentClaimIds,
obscureNsfw,
playingUri,
isPaused,
openModal,
claimIsMine,
prepareEdit,
navigate,
autoplay,
fromOverlay,
costInfo,
} = this.props;
// File info
@ -122,16 +118,32 @@ class FilePage extends React.Component<Props> {
const shouldObscureThumbnail = obscureNsfw && metadata.nsfw;
const { height, channel_name: channelName, value } = claim;
const mediaType = Lbry.getMediaType(contentType);
const isPlayable =
Object.values(player.mime).indexOf(contentType) !== -1 || mediaType === 'audio';
const isPlayable = Object.values(player.mime).includes(contentType) || mediaType === 'audio';
const channelClaimId =
value && value.publisherSignature && value.publisherSignature.certificateId;
let subscriptionUri;
if (channelName && channelClaimId) {
subscriptionUri = buildURI({ channelName, claimId: channelClaimId }, false);
}
const speechSharable =
costInfo &&
costInfo.cost === 0 &&
contentType &&
['video', 'image'].includes(contentType.split('/')[0]);
// We want to use the short form uri for editing
// This is what the user is used to seeing, they don't care about the claim id
// We will select the claim id before they publish
let editUri;
if (claimIsMine) {
const uriObject = { contentName: claim.name, claimId: claim.claim_id };
if (channelName) {
uriObject.channelName = channelName;
}
editUri = buildURI(uriObject);
}
const isPlaying = playingUri === uri && !isPaused;
return (
<Page extraPadding>
{!claim || !metadata ? (
@ -140,22 +152,27 @@ class FilePage extends React.Component<Props> {
</section>
) : (
<section className="card">
{isPlayable ? (
<Video className="content__embedded" uri={uri} fromOverlay={fromOverlay} />
) : (
<Thumbnail shouldObscure={shouldObscureThumbnail} src={thumbnail} />
)}
{!isPlaying && (
<div className="card-media__internal-links">
<FileActions uri={uri} vertical />
</div>
)}
{isPlayable && <Video className="content__embedded" uri={uri} fromOverlay={fromOverlay} />}
{!isPlayable &&
(thumbnail ? (
<Thumbnail shouldObscure={shouldObscureThumbnail} src={thumbnail} />
) : (
<div
className={classnames('content__empty', {
'content__empty--nsfw': shouldObscureThumbnail,
})}
>
<div className="card__media-text">{__('This content is not playable.')}</div>
</div>
))}
<div className="card__content">
<div className="card__title-identity--file">
<h1 className="card__title card__title--file">{title}</h1>
<div className="card__title-identity-icons">
<FilePrice uri={normalizeURI(uri)} />
{isRewardContent && <Icon icon={icons.FEATURED} />}
{isRewardContent && (
<Icon iconColor="red" tooltip="bottom" icon={icons.FEATURED} />
)}
</div>
</div>
<span className="card__subtitle card__subtitle--file">
@ -172,35 +189,54 @@ class FilePage extends React.Component<Props> {
icon={icons.EDIT}
label={__('Edit')}
onClick={() => {
prepareEdit(claim, uri);
prepareEdit(claim, editUri);
navigate('/publish');
}}
/>
) : (
<React.Fragment>
<Button
button="alt"
iconRight="Send"
label={__('Enjoy this? Send a tip')}
onClick={() => openModal({ id: MODALS.SEND_TIP }, { uri })}
/>
<SubscribeButton uri={subscriptionUri} channelName={channelName} />
</React.Fragment>
<SubscribeButton uri={subscriptionUri} channelName={channelName} />
)}
</div>
</div>
<FormRow alignRight>
{(!claimIsMine || speechSharable) && (
<div className="card__actions card__actions--end">
{!claimIsMine && (
<Button
button="alt"
icon="Send"
label={__('Enjoy this? Send a tip')}
onClick={() => openModal({ id: MODALS.SEND_TIP }, { uri })}
/>
)}
{speechSharable && (
<ViewOnWebButton claimId={claim.claim_id} claimName={claim.name} />
)}
</div>
)}
<FormRow alignRight padded>
<FormField
type="checkbox"
useToggle
name="autoplay"
onChange={this.onAutoplayChange}
type="checkbox"
checked={autoplay}
postfix={__('Autoplay')}
onChange={this.onAutoplayChange}
postfix={
<ToolTip
onFormField
label={__('Autoplay')}
body={__('Automatically download and play free content.')}
/>
}
/>
</FormRow>
</div>
<div className="card__content">
<FileDownloadLink uri={uri} />
<FileActions uri={uri} claimId={claim.claim_id} />
</div>
<div className="card__content--extra-padding">
<FileDetails uri={uri} />
</div>
</section>

View file

@ -1,12 +1,11 @@
import { connect } from 'react-redux';
import { selectMyClaimsWithoutChannels } from 'lbry-redux';
import { selectPendingPublishes } from 'redux/selectors/publish';
import { selectPendingPublishes, selectClaimsWithPendingPublishes } from 'redux/selectors/publish';
import { doNavigate } from 'redux/actions/navigation';
import { doCheckPendingPublishes } from 'redux/actions/publish';
import FileListPublished from './view';
const select = state => ({
claims: selectMyClaimsWithoutChannels(state),
claims: selectClaimsWithPendingPublishes(state),
pendingPublishes: selectPendingPublishes(state),
});
@ -15,4 +14,7 @@ const perform = dispatch => ({
checkIfPublishesConfirmed: publishes => dispatch(doCheckPendingPublishes(publishes)),
});
export default connect(select, perform)(FileListPublished);
export default connect(
select,
perform
)(FileListPublished);

View file

@ -20,13 +20,12 @@ class FileListPublished extends React.PureComponent<Props> {
}
render() {
const { claims, pendingPublishes, navigate } = this.props;
const fileInfos = [...pendingPublishes, ...claims];
const { claims, navigate } = this.props;
return (
<Page notContained>
{fileInfos.length ? (
<FileList checkPending fileInfos={fileInfos} sortByHeight />
{claims.length ? (
<FileList checkPending fileInfos={claims} sortByHeight />
) : (
<div className="page__empty">
{__("It looks like you haven't published anything to LBRY yet.")}

View file

@ -3,18 +3,11 @@ import Button from 'component/button';
import RewardSummary from 'component/rewardSummary';
import ShapeShift from 'component/shapeShift';
import Page from 'component/page';
import * as icons from 'constants/icons';
const GetCreditsPage = props => (
const GetCreditsPage = () => (
<Page>
<RewardSummary />
<ShapeShift />
<section className="card card--section">
<div className="card__title">{__('From External Wallet')}</div>
<div className="card__actions">
<Button button="primary" icon={icons.SEND} navigate="/send" label={__('Send / Receive')} />
</div>
</section>
<section className="card card--section">
<div className="card__title">{__('More ways to get LBRY Credits')}</div>
<div className="card__content">

View file

@ -1,12 +1,14 @@
import { connect } from 'react-redux';
import { doAuthNavigate } from 'redux/actions/navigation';
import { doFetchAccessToken } from 'redux/actions/user';
import { selectDaemonSettings } from 'redux/selectors/settings';
import { selectAccessToken, selectUser } from 'redux/selectors/user';
import HelpPage from './view';
const select = state => ({
user: selectUser(state),
accessToken: selectAccessToken(state),
deamonSettings: selectDaemonSettings(state),
});
const perform = dispatch => ({
@ -14,4 +16,7 @@ const perform = dispatch => ({
fetchAccessToken: () => dispatch(doFetchAccessToken()),
});
export default connect(select, perform)(HelpPage);
export default connect(
select,
perform
)(HelpPage);

View file

@ -1,15 +1,44 @@
// @TODO: Customize advice based on OS
import React from 'react';
// @flow
import * as React from 'react';
import { shell } from 'electron';
import { Lbry } from 'lbry-redux';
import Native from 'native';
import Button from 'component/button';
import BusyIndicator from 'component/common/busy-indicator';
import Icon from 'component/common/icon';
import Page from 'component/page';
import * as icons from 'constants/icons';
class HelpPage extends React.PureComponent {
constructor(props) {
type DeamonSettings = {
data_dir: string | any,
};
type Props = {
deamonSettings: DeamonSettings,
accessToken: string,
fetchAccessToken: () => void,
doAuth: () => void,
user: any,
};
type VersionInfo = {
os_system: string,
os_release: string,
platform: string,
lbrynet_version: string,
lbryum_version: string,
};
type State = {
versionInfo: VersionInfo | any,
lbryId: String | any,
uiVersion: ?string,
upgradeAvailable: ?boolean,
accessTokenHidden: ?boolean,
};
class HelpPage extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
@ -20,11 +49,12 @@ class HelpPage extends React.PureComponent {
accessTokenHidden: true,
};
this.showAccessToken = this.showAccessToken.bind(this);
(this: any).showAccessToken = this.showAccessToken.bind(this);
(this: any).openLogFile = this.openLogFile.bind(this);
}
componentDidMount() {
Native.getAppVersionInfo().then(({ remoteVersion, localVersion, upgradeAvailable }) => {
Native.getAppVersionInfo().then(({ localVersion, upgradeAvailable }) => {
this.setState({
uiVersion: localVersion,
upgradeAvailable,
@ -50,13 +80,24 @@ class HelpPage extends React.PureComponent {
});
}
openLogFile(userHomeDirectory: string) {
const logFileName = 'lbrynet.log';
const os = this.state.versionInfo.os_system;
if (os === 'Darwin' || os === 'Linux') {
shell.openItem(`${userHomeDirectory}/${logFileName}`);
} else {
shell.openItem(`${userHomeDirectory}\\${logFileName}`);
}
}
render() {
let ver;
let osName;
let platform;
let newVerLink;
const { accessToken, doAuth, user } = this.props;
const { accessToken, doAuth, user, deamonSettings } = this.props;
const { data_dir: dataDirectory } = deamonSettings;
if (this.state.versionInfo) {
ver = this.state.versionInfo;
@ -109,13 +150,36 @@ class HelpPage extends React.PureComponent {
</section>
<section className="card card--section">
<div className="card__title">{__('Report a Bug')}</div>
<p className="card__subtitle">{__('Did you find something wrong?')}</p>
<div className="card__title">{__('View your Log')}</div>
<p className="card__subtitle">
{__('Did something go wrong? Have a look in your log file, or send it to')}{' '}
<Button button="link" label={__('support')} href="https://lbry.io/faq/support" />.
</p>
<div className="card__actions">
<Button
button="primary"
label={__('Open Log')}
icon={icons.REPORT}
onClick={() => this.openLogFile(dataDirectory)}
/>
<Button
button="primary"
label={__('Open Log Folder')}
icon={icons.REPORT}
onClick={() => shell.showItemInFolder(dataDirectory)}
/>
</div>
</section>
<section className="card card--section">
<div className="card__title">{__('Report a Bug or Suggest a New Feature')}</div>
<p className="card__subtitle">
{__('Did you find something wrong? Think LBRY could add something useful and cool?')}
</p>
<div className="card__actions">
<Button
navigate="/report"
label={__('Submit a Bug Report')}
label={__('Submit a Bug Report/Feature Request')}
icon={icons.REPORT}
button="primary"
/>

View file

@ -1,31 +1,25 @@
import { connect } from 'react-redux';
import { doClaimRewardType } from 'redux/actions/rewards';
import {
doHistoryBack,
doResolveUri,
makeSelectCostInfoForUri,
selectMyClaims,
selectFetchingMyChannels,
selectMyChannelClaims,
selectClaimsByUri,
selectResolvingUris,
selectBalance,
} from 'lbry-redux';
import {
doFetchClaimListMine,
doFetchChannelListMine,
doCreateChannel,
} from 'redux/actions/content';
import { doResolveUri, selectClaimsByUri, selectResolvingUris, selectBalance } from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import rewards from 'rewards';
import { selectPublishFormValues } from 'redux/selectors/publish';
import { doClearPublish, doUpdatePublishForm, doPublish } from 'redux/actions/publish';
import { doPrepareEdit } from 'redux/actions/publish';
import {
selectPublishFormValues,
selectIsStillEditing,
selectMyClaimForUri,
} from 'redux/selectors/publish';
import {
doResetThumbnailStatus,
doClearPublish,
doUpdatePublishForm,
doPublish,
doPrepareEdit,
} from 'redux/actions/publish';
import PublishPage from './view';
const select = (state, props) => {
const select = state => {
const isStillEditing = selectIsStillEditing(state);
const myClaimForUri = selectMyClaimForUri(state);
const publishState = selectPublishFormValues(state);
const { uri, name } = publishState;
const { uri } = publishState;
const resolvingUris = selectResolvingUris(state);
let isResolvingUri = false;
@ -33,24 +27,28 @@ const select = (state, props) => {
isResolvingUri = resolvingUris.includes(uri);
}
const claimsByUri = selectClaimsByUri(state);
const myClaims = selectMyClaims(state);
const claimForUri = claimsByUri[uri];
let claimForUri;
let winningBidForClaimUri;
let myClaimForUri;
if (claimForUri) {
winningBidForClaimUri = claimForUri.effective_amount;
myClaimForUri = myClaims.find(claim => claim.name === name);
if (!myClaimForUri) {
// if the uri isn't from a users claim, find the winning bid needed for the vanity url
// in the future we may want to display this on users claims
// ex: "you own this, for 5 more lbc you will win this claim"
const claimsByUri = selectClaimsByUri(state);
claimForUri = claimsByUri[uri];
winningBidForClaimUri = claimForUri ? claimForUri.effective_amount : null;
}
return {
...publishState,
isResolvingUri,
// The winning claim for a short lbry uri
claimForUri,
winningBidForClaimUri,
// My previously published claims under this short lbry uri
myClaimForUri,
costInfo: makeSelectCostInfoForUri(props.uri)(state),
// If I clicked the "edit" button, have I changed the uri?
// Need this to make it easier to find the source on previously published content
isStillEditing,
balance: selectBalance(state),
};
};
@ -62,6 +60,10 @@ const perform = dispatch => ({
publish: params => dispatch(doPublish(params)),
navigate: path => dispatch(doNavigate(path)),
prepareEdit: (claim, uri) => dispatch(doPrepareEdit(claim, uri)),
resetThumbnailStatus: () => dispatch(doResetThumbnailStatus()),
});
export default connect(select, perform)(PublishPage);
export default connect(
select,
perform
)(PublishPage);

View file

@ -49,10 +49,10 @@ class ReportPage extends React.Component {
<Page>
<section className="card card--section">
<div className="card__content">
<div className="card__title">{__('Report an Issue')}</div>
<div className="card__title">{__('Report an Issue/Request a Feature')}</div>
<p>
{__(
'Please describe the problem you experienced and any information you think might be useful to us. Links to screenshots are great!'
'Please describe the problem you experienced or the feature you want to see and any information you think might be useful to us. Links to screenshots are great!'
)}
</p>
<FormRow>
@ -65,7 +65,7 @@ class ReportPage extends React.Component {
onChange={event => {
this.onMessageChange(event);
}}
placeholder={__('Description of your issue')}
placeholder={__('Description of your issue or feature request')}
/>
</FormRow>
<div className="card__actions">

Some files were not shown because too many files have changed in this diff Show more