Merge branch 'internal-subscriptions'

This commit is contained in:
Jeremy Kauffman 2018-05-15 11:16:40 -04:00
commit 1c0275d69f
38 changed files with 1090 additions and 294 deletions

View file

@ -34,6 +34,7 @@
"singleQuote": true
}],
"func-names": ["warn", "as-needed"],
"jsx-a11y/label-has-for": 0
"jsx-a11y/label-has-for": 0,
"import/prefer-default-export": 0
}
}

View file

@ -0,0 +1,6 @@
<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 Normal file
View file

@ -0,0 +1,12 @@
<?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 Normal file
View file

@ -0,0 +1,6 @@
<?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 Normal file
View file

@ -0,0 +1,8 @@
<?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 Normal file
View file

@ -0,0 +1,6 @@
<?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 Normal file
View file

@ -0,0 +1,547 @@
<?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

@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
### 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))
### Fixed
* Fix content-type not shown correctly in file description ([#863](https://github.com/lbryio/lbry-app/pull/863))

View file

@ -2,7 +2,6 @@
import mixpanel from 'mixpanel-browser';
import Lbryio from 'lbryio';
import isDev from 'electron-is-dev';
import type { Subscription } from 'redux/reducers/subscriptions';
if (isDev) {
mixpanel.init('691723e855cabb9d27a7a79002216967');
@ -15,8 +14,6 @@ type Analytics = {
setUser: Object => void,
toggle: (boolean, ?boolean) => void,
apiLogView: (string, string, string) => void,
apiLogSubscribe: Subscription => void,
apiLogUnsubscribe: Subscription => void,
};
let analyticsEnabled: boolean = false;
@ -56,20 +53,6 @@ const analytics: Analytics = {
}).catch(() => {});
}
},
apiLogSubscribe: (subscription: Subscription): void => {
if (analyticsEnabled) {
Lbryio.call('subscription', 'new', {
subscription,
}).catch(() => {});
}
},
apiLogUnsubscribe: (subscription: Subscription): void => {
if (analyticsEnabled) {
Lbryio.call('subscription', 'delete', {
subscription,
}).catch(() => {});
}
},
};
export default analytics;

View file

@ -1,28 +0,0 @@
// @flow
import * as React from 'react';
import classnames from 'classnames';
type Props = {
dark?: boolean,
};
class Spinner extends React.Component<Props> {
static defaultProps = {
dark: false,
};
render() {
const { dark } = this.props;
return (
<div className={classnames('spinner', { 'spinner--dark': dark })}>
<div className="rect rect1" />
<div className="rect rect2" />
<div className="rect rect3" />
<div className="rect rect4" />
<div className="rect rect5" />
</div>
);
}
}
export default Spinner;

View file

@ -3,9 +3,10 @@ import * as React from 'react';
import ReactMarkdown from 'react-markdown';
import Button from 'component/button';
import path from 'path';
import type { Claim } from 'types/claim';
type Props = {
claim: {},
claim: Claim,
fileInfo: {
download_path: string,
},

View file

@ -53,7 +53,6 @@ class FileList extends React.PureComponent<Props, State> {
if (fileInfo1.pending) {
return -1;
}
const height1 = this.props.claimsById[fileInfo1.claim_id]
? this.props.claimsById[fileInfo1.claim_id].height
: 0;
@ -145,6 +144,10 @@ class FileList extends React.PureComponent<Props, State> {
const { sortBy } = this.state;
const content = [];
if (!fileInfos) {
return null;
}
this.sortFunctions[sortBy](fileInfos).forEach(fileInfo => {
const {
channel_name: channelName,

View file

@ -1,17 +1,76 @@
// @flow
import * as React from 'react';
import classnames from 'classnames';
import Spinner from 'component/spinner';
import { isShowingChildren } from 'util/dom';
// time in ms to wait to show loading spinner
const LOADER_TIMEOUT = 1500;
type Props = {
children: React.Node,
children: React.Node | Array<React.Node>,
pageTitle: ?string,
noPadding: ?boolean,
extraPadding: ?boolean,
notContained: ?boolean, // No max-width, but keep the padding
loading: ?boolean,
};
const Page = (props: Props) => {
const { pageTitle, children, noPadding, extraPadding, notContained } = props;
type State = {
showLoader: ?boolean,
};
class Page extends React.PureComponent<Props, State> {
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
const { children } = nextProps;
const { showLoader } = prevState;
// If we aren't showing the loader, don't bother updating
if (!showLoader) {
return null;
}
if (isShowingChildren(children)) {
return {
showLoader: false,
};
}
return null;
}
constructor() {
super();
this.state = {
showLoader: false,
};
this.loaderTimeout = null;
}
componentDidMount() {
const { children } = this.props;
if (!isShowingChildren(children))
this.loaderTimeout = setTimeout(() => {
this.setState({ showLoader: true });
}, LOADER_TIMEOUT);
}
componentWillUnmount() {
this.loaderTimeout = null;
}
loaderTimeout: ?TimeoutID;
render() {
const { pageTitle, children, noPadding, extraPadding, notContained, loading } = this.props;
const { showLoader } = this.state;
// We don't want to show the loading spinner right away if it will only flash on the
// screen for a short time, wait until we know it will be loading for a bit before showing it
const shouldShowLoader = !isShowingChildren(children) && showLoader;
return (
<main
className={classnames('main', {
@ -25,9 +84,15 @@ const Page = (props: Props) => {
{pageTitle && <h1 className="page__title">{pageTitle}</h1>}
</div>
)}
{children}
{!loading && children}
{shouldShowLoader && (
<div className="page__empty">
<Spinner />
</div>
)}
</main>
);
};
}
}
export default Page;

View file

@ -0,0 +1,9 @@
import { connect } from 'react-redux';
import { selectTheme } from 'redux/selectors/settings';
import Spinner from './view';
const mapStateToProps = state => ({
theme: selectTheme(state),
});
export default connect(mapStateToProps, null)(Spinner);

View file

@ -0,0 +1,36 @@
// @flow
import * as React from 'react';
import classnames from 'classnames';
import { DARK_THEME, LIGHT_THEME } from 'constants/themes';
type Props = {
dark?: boolean, // always a dark spinner
light?: boolean, // always a light spinner
theme: string,
};
const Spinner = (props: Props) => {
const { dark, light, theme } = props;
return (
<div
className={classnames('spinner', {
'spinner--dark': !light && (dark || theme === LIGHT_THEME),
'spinner--light': !dark && (light || theme === DARK_THEME),
})}
>
<div className="rect rect1" />
<div className="rect rect2" />
<div className="rect rect3" />
<div className="rect rect4" />
<div className="rect rect5" />
</div>
);
};
Spinner.defaultProps = {
dark: false,
light: false,
};
export default Spinner;

View file

@ -3,7 +3,7 @@ import React from 'react';
import { MODALS } from 'lbry-redux';
import * as icons from 'constants/icons';
import Button from 'component/button';
import type { Subscription } from 'redux/reducers/subscriptions';
import type { Subscription } from 'types/subscription';
type SubscribtionArgs = {
channelName: string,

View file

@ -3,21 +3,18 @@ import React from 'react';
import Button from 'component/button';
import { buildURI } from 'lbry-redux';
import classnames from 'classnames';
// import Icon from 'component/common/icon';
import type { Claim } from 'types/claim';
type Props = {
isResolvingUri: boolean,
resolveUri: string => void,
claim: {
channel_name: string,
has_signature: boolean,
signature_is_valid: boolean,
value: {
publisherSignature: { certificateId: string },
},
},
uri: string,
claim: Claim,
link: ?boolean,
// Lint thinks we aren't using these, even though we are.
// Possibly because the resolve function is an arrow function that is passed in props?
/* eslint-disable react/no-unused-prop-types */
resolveUri: string => void,
uri: string,
/* eslint-enable react/no-unused-prop-types */
};
class UriIndicator extends React.PureComponent<Props> {

View file

@ -1,6 +1,6 @@
// @flow
import React from 'react';
import Spinner from 'component/common/spinner';
import Spinner from 'component/spinner';
type Props = {
spinner: boolean,
@ -16,7 +16,7 @@ class LoadingScreen extends React.PureComponent<Props> {
const { status, spinner } = this.props;
return (
<div className="content__loading">
{spinner && <Spinner />}
{spinner && <Spinner light />}
<span className="content__loading-text">{status}</span>
</div>

View file

@ -2,6 +2,7 @@
import React from 'react';
import { Lbry } from 'lbry-redux';
import classnames from 'classnames';
import type { Claim } from 'types/claim';
import VideoPlayer from './internal/player';
import VideoPlayButton from './internal/play-button';
import LoadingScreen from './internal/loading-screen';
@ -26,7 +27,7 @@ type Props = {
contentType: string,
changeVolume: number => void,
volume: number,
claim: {},
claim: Claim,
uri: string,
doPlay: () => void,
doPause: () => void,

View file

@ -3,11 +3,12 @@ import React from 'react';
import Button from 'component/button';
import { FormField } from 'component/common/form';
import UriIndicator from 'component/uriIndicator';
import type { Claim } from 'types/claim';
type Props = {
uri: string,
title: string,
claim: { claim_id: string },
claim: Claim,
errorMessage: string,
isPending: boolean,
sendSupport: (number, string, string) => void,

View file

@ -1,3 +1,13 @@
/*
Constants for redux actions
All names should be in present tense
ex:
XXX_START
XXX_SUCCESS
XXX_FAIL
XXX_COMPLETE // if there is no fail case
*/
export const WINDOW_FOCUSED = 'WINDOW_FOCUSED';
export const DAEMON_READY = 'DAEMON_READY';
export const DAEMON_VERSION_MATCH = 'DAEMON_VERSION_MATCH';
@ -166,6 +176,9 @@ export const SET_SUBSCRIPTION_NOTIFICATIONS = 'SET_SUBSCRIPTION_NOTIFICATIONS';
export const CHECK_SUBSCRIPTION_STARTED = 'CHECK_SUBSCRIPTION_STARTED';
export const CHECK_SUBSCRIPTION_COMPLETED = 'CHECK_SUBSCRIPTION_COMPLETED';
export const CHECK_SUBSCRIPTIONS_SUBSCRIBE = 'CHECK_SUBSCRIPTIONS_SUBSCRIBE';
export const FETCH_SUBSCRIPTIONS_START = 'FETCH_SUBSCRIPTIONS_START';
export const FETCH_SUBSCRIPTIONS_FAIL = 'FETCH_SUBSCRIPTIONS_FAIL';
export const FETCH_SUBSCRIPTIONS_SUCCESS = 'FETCH_SUBSCRIPTIONS_SUCCESS';
// Video controls
export const SET_VIDEO_PAUSE = 'SET_VIDEO_PAUSE';

View file

@ -0,0 +1,4 @@
// css theme values
// saved in settings and found at /static/themes/{theme}.css
export const DARK_THEME = 'dark';
export const LIGHT_THEME = 'light';

View file

@ -6,6 +6,7 @@ import ReactPaginate from 'react-paginate';
import SubscribeButton from 'component/subscribeButton';
import Page from 'component/page';
import FileList from 'component/fileList';
import type { Claim } from 'types/claim';
type Props = {
uri: string,
@ -13,10 +14,7 @@ type Props = {
totalPages: number,
fetching: boolean,
params: { page: number },
claim: {
name: string,
claim_id: string,
},
claim: Claim,
claimsInChannel: Array<{}>,
fetchClaims: (string, number) => void,
fetchClaimCount: string => void,
@ -58,8 +56,8 @@ class ChannelPage extends React.PureComponent<Props> {
}
render() {
const { fetching, claimsInChannel, claim, uri, page, totalPages } = this.props;
const { name } = claim;
const { fetching, claimsInChannel, claim, page, totalPages } = this.props;
const { name, permanent_url: permanentUrl } = claim;
let contentList;
if (fetching) {
@ -78,7 +76,7 @@ class ChannelPage extends React.PureComponent<Props> {
<section className="card__channel-info card__channel-info--large">
<h1>{name}</h1>
<div className="card__actions card__actions--no-margin">
<SubscribeButton uri={uri} channelName={name} />
<SubscribeButton uri={permanentUrl} channelName={name} />
</div>
</section>
<section>{contentList}</section>

View file

@ -16,18 +16,10 @@ import SubscribeButton from 'component/subscribeButton';
import Page from 'component/page';
import player from 'render-media';
import * as settings from 'constants/settings';
import type { Claim } from 'types/claim';
type Props = {
claim: {
claim_id: string,
height: number,
channel_name: string,
value: {
publisherSignature: ?{
certificateId: ?string,
},
},
},
claim: Claim,
fileInfo: {},
metadata: {
title: string,

View file

@ -5,16 +5,13 @@ import ChannelPage from 'page/channel';
import FilePage from 'page/file';
import Page from 'component/page';
import Button from 'component/button';
import type { Claim } from 'types/claim';
type Props = {
isResolvingUri: boolean,
resolveUri: string => void,
uri: string,
claim: {
name: string,
txid: string,
nout: number,
},
claim: Claim,
blackListedOutpoints: Array<{
txid: string,
nout: number,

View file

@ -1,27 +1,25 @@
import React from 'react';
import { connect } from 'react-redux';
import {
selectSubscriptionsFromClaims,
selectSubscriptionClaims,
selectSubscriptions,
selectHasFetchedSubscriptions,
selectSubscriptionsBeingFetched,
selectIsFetchingSubscriptions,
selectNotifications,
} from 'redux/selectors/subscriptions';
import { doFetchClaimsByChannel } from 'redux/actions/content';
import {
setHasFetchedSubscriptions,
setSubscriptionNotifications,
} from 'redux/actions/subscriptions';
import { setSubscriptionNotifications, doFetchMySubscriptions } from 'redux/actions/subscriptions';
import SubscriptionsPage from './view';
const select = state => ({
hasFetchedSubscriptions: state.subscriptions.hasFetchedSubscriptions,
savedSubscriptions: selectSubscriptions(state),
subscriptions: selectSubscriptionsFromClaims(state),
isFetchingSubscriptions: selectIsFetchingSubscriptions(state),
subscriptionsBeingFetched: selectSubscriptionsBeingFetched(state),
subscriptions: selectSubscriptions(state),
subscriptionClaims: selectSubscriptionClaims(state),
notifications: selectNotifications(state),
});
export default connect(select, {
doFetchClaimsByChannel,
setHasFetchedSubscriptions,
setSubscriptionNotifications,
doFetchMySubscriptions,
})(SubscriptionsPage);

View file

@ -1,39 +1,28 @@
// @flow
import React from 'react';
import Page from 'component/page';
import CategoryList from 'component/common/category-list';
import type { Subscription } from 'redux/reducers/subscriptions';
import type { Subscription } from 'types/subscription';
import * as NOTIFICATION_TYPES from 'constants/notification_types';
import Button from 'component/button';
type SavedSubscriptions = Array<Subscription>;
import FileList from 'component/fileList';
import type { Claim } from 'types/claim';
type Props = {
doFetchClaimsByChannel: (string, number) => any,
savedSubscriptions: SavedSubscriptions,
// TODO build out claim types
subscriptions: Array<any>,
setHasFetchedSubscriptions: () => void,
hasFetchedSubscriptions: boolean,
doFetchClaimsByChannel: (string, number) => void,
doFetchMySubscriptions: () => void,
setSubscriptionNotifications: ({}) => void,
subscriptions: Array<Subscription>,
isFetchingSubscriptions: boolean,
subscriptionClaims: Array<{ uri: string, claims: Array<Claim> }>,
subscriptionsBeingFetched: {},
notifications: {},
};
export default class extends React.PureComponent<Props> {
// setHasFetchedSubscriptions is a terrible hack
// it allows the subscriptions to load correctly when refresing on the subscriptions page
// currently the page is rendered before the state is rehyrdated
// that causes this component to be rendered with zero savedSubscriptions
// we need to wait until persist/REHYDRATE has fired before rendering the page
componentDidMount() {
const {
savedSubscriptions,
setHasFetchedSubscriptions,
notifications,
setSubscriptionNotifications,
} = this.props;
if (savedSubscriptions.length) {
this.fetchSubscriptions(savedSubscriptions);
setHasFetchedSubscriptions();
}
const { notifications, setSubscriptionNotifications, doFetchMySubscriptions } = this.props;
doFetchMySubscriptions();
const newNotifications = {};
Object.keys(notifications).forEach(cur => {
if (notifications[cur].type === NOTIFICATION_TYPES.DOWNLOADING) {
@ -43,40 +32,37 @@ export default class extends React.PureComponent<Props> {
setSubscriptionNotifications(newNotifications);
}
componentWillReceiveProps(props: Props) {
const { savedSubscriptions, hasFetchedSubscriptions, setHasFetchedSubscriptions } = props;
componentDidUpdate() {
const {
subscriptions,
subscriptionClaims,
doFetchClaimsByChannel,
subscriptionsBeingFetched,
} = this.props;
if (!hasFetchedSubscriptions && savedSubscriptions.length) {
this.fetchSubscriptions(savedSubscriptions);
setHasFetchedSubscriptions();
}
}
fetchSubscriptions(savedSubscriptions: SavedSubscriptions) {
const { doFetchClaimsByChannel } = this.props;
if (savedSubscriptions.length) {
// can this use batchActions?
savedSubscriptions.forEach(sub => {
doFetchClaimsByChannel(sub.uri, 1);
const subscriptionClaimMap = {};
subscriptionClaims.forEach(claim => {
subscriptionClaimMap[claim.uri] = 1;
});
subscriptions.forEach(sub => {
if (!subscriptionClaimMap[sub.uri] && !subscriptionsBeingFetched[sub.uri]) {
doFetchClaimsByChannel(sub.uri, 1);
}
});
}
render() {
const { subscriptions, savedSubscriptions } = this.props;
const { subscriptions, subscriptionClaims, isFetchingSubscriptions } = this.props;
// TODO: if you are subscribed to an empty channel, this will always be true (but it should not be)
const someClaimsNotLoaded = Boolean(
subscriptions.find(subscription => !subscription.claims.length)
);
const fetchingSubscriptions =
!!savedSubscriptions.length &&
(subscriptions.length !== savedSubscriptions.length || someClaimsNotLoaded);
let claimList = [];
subscriptionClaims.forEach(claimData => {
claimList = claimList.concat(claimData.claims);
});
return (
<Page noPadding isLoading={fetchingSubscriptions}>
{!savedSubscriptions.length && (
<Page notContained loading={isFetchingSubscriptions}>
{!subscriptions.length && (
<div className="page__empty">
{__("It looks like you aren't subscribed to any channels yet.")}
<div className="card__actions card__actions--center">
@ -84,28 +70,7 @@ export default class extends React.PureComponent<Props> {
</div>
</div>
)}
{!!savedSubscriptions.length && (
<div>
{!!subscriptions.length &&
subscriptions.map(subscription => {
if (!subscription.claims.length) {
// will need to update when you can subscribe to empty channels
// for now this prevents issues with FeaturedCategory being rendered
// before the names (claim uris) are populated
return '';
}
return (
<CategoryList
key={subscription.channelName}
categoryLink={subscription.uri}
category={subscription.channelName}
names={subscription.claims}
/>
);
})}
</div>
)}
{!!claimList.length && <FileList hideFilter sortByHeight fileInfos={claimList} />}
</Page>
);
}

View file

@ -138,6 +138,7 @@ export function doUpdateLoadStatus(uri, outpoint) {
: acc,
0
);
const notif = new window.Notification(notifications[uri].subscription.channelName, {
body: `Posted ${fileInfo.metadata.title}${
count > 1 && count < 10 ? ` and ${count - 1} other new items` : ''

View file

@ -2,57 +2,128 @@
import * as ACTIONS from 'constants/action_types';
import * as NOTIFICATION_TYPES from 'constants/notification_types';
import type {
Subscription,
Dispatch,
SubscriptionState,
SubscriptionNotifications,
} from 'redux/reducers/subscriptions';
import type { Subscription } from 'types/subscription';
import { selectSubscriptions } from 'redux/selectors/subscriptions';
import { Lbry, buildURI } from 'lbry-redux';
import { Lbry, buildURI, parseURI } from 'lbry-redux';
import { doPurchaseUri } from 'redux/actions/content';
import { doNavigate } from 'redux/actions/navigation';
import analytics from 'analytics';
import Promise from 'bluebird';
import Lbryio from 'lbryio';
const CHECK_SUBSCRIPTIONS_INTERVAL = 60 * 60 * 1000;
const SUBSCRIPTION_DOWNLOAD_LIMIT = 1;
export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dispatch) => {
dispatch({
type: ACTIONS.CHANNEL_SUBSCRIBE,
data: subscription,
export const doFetchMySubscriptions = () => (dispatch: Dispatch, getState: () => any) => {
const {
subscriptions: subscriptionState,
settings: { daemonSettings },
} = getState();
const { subscriptions: reduxSubscriptions } = subscriptionState;
const { share_usage_data: isSharingData } = daemonSettings;
if (!isSharingData && isSharingData !== undefined) {
// They aren't sharing their data, subscriptions will be handled by persisted redux state
return;
}
// most of this logic comes from scenarios where the db isn't synced with redux
// this will happen if the user stops sharing data
dispatch({ type: ACTIONS.FETCH_SUBSCRIPTIONS_START });
Lbryio.call('subscription', 'list')
.then(dbSubscriptions => {
const storedSubscriptions = dbSubscriptions || [];
// User has no subscriptions in db or redux
if (!storedSubscriptions.length && (!reduxSubscriptions || !reduxSubscriptions.length)) {
return [];
}
// There is some mismatch between redux state and db state
// If something is in the db, but not in redux, add it to redux
// If something is in redux, but not in the db, add it to the db
if (storedSubscriptions.length !== reduxSubscriptions.length) {
const dbSubMap = {};
const reduxSubMap = {};
const subsNotInDB = [];
const subscriptionsToReturn = reduxSubscriptions.slice();
storedSubscriptions.forEach(sub => {
dbSubMap[sub.claim_id] = 1;
});
analytics.apiLogSubscribe(subscription);
reduxSubscriptions.forEach(sub => {
const { claimId } = parseURI(sub.uri);
reduxSubMap[claimId] = 1;
dispatch(doCheckSubscription(subscription, true));
};
export const doChannelUnsubscribe = (subscription: Subscription) => (dispatch: Dispatch) => {
dispatch({
type: ACTIONS.CHANNEL_UNSUBSCRIBE,
data: subscription,
if (!dbSubMap[claimId]) {
subsNotInDB.push({
claim_id: claimId,
channel_name: sub.channelName,
});
}
});
analytics.apiLogUnsubscribe(subscription);
};
storedSubscriptions.forEach(sub => {
if (!reduxSubMap[sub.claim_id]) {
const uri = `lbry://${sub.channel_name}#${sub.claim_id}`;
subscriptionsToReturn.push({ uri, channelName: sub.channel_name });
}
});
export const doCheckSubscriptions = () => (
dispatch: Dispatch,
getState: () => SubscriptionState
) => {
const checkSubscriptionsTimer = setInterval(
return Promise.all(subsNotInDB.map(payload => Lbryio.call('subscription', 'new', payload)))
.then(() => subscriptionsToReturn)
.catch(
() =>
selectSubscriptions(getState()).map((subscription: Subscription) =>
dispatch(doCheckSubscription(subscription, true))
),
CHECK_SUBSCRIPTIONS_INTERVAL
// let it fail, we will try again when the navigate to the subscriptions page
subscriptionsToReturn
);
}
// DB is already synced, just return the subscriptions in redux
return reduxSubscriptions;
})
.then(subscriptions => {
dispatch({
type: ACTIONS.CHECK_SUBSCRIPTIONS_SUBSCRIBE,
data: { checkSubscriptionsTimer },
type: ACTIONS.FETCH_SUBSCRIPTIONS_SUCCESS,
data: subscriptions,
});
})
.catch(() => {
dispatch({
type: ACTIONS.FETCH_SUBSCRIPTIONS_FAIL,
});
});
};
export const setSubscriptionLatest = (subscription: Subscription, uri: string) => (
dispatch: Dispatch
) =>
dispatch({
type: ACTIONS.SET_SUBSCRIPTION_LATEST,
data: {
subscription,
uri,
},
});
export const setSubscriptionNotification = (
subscription: Subscription,
uri: string,
notificationType: string
) => (dispatch: Dispatch) =>
dispatch({
type: ACTIONS.SET_SUBSCRIPTION_NOTIFICATION,
data: {
subscription,
uri,
type: notificationType,
},
});
export const doCheckSubscription = (subscription: Subscription, notify?: boolean) => (
dispatch: Dispatch
) => {
@ -114,31 +185,6 @@ export const doCheckSubscription = (subscription: Subscription, notify?: boolean
});
};
export const setSubscriptionLatest = (subscription: Subscription, uri: string) => (
dispatch: Dispatch
) =>
dispatch({
type: ACTIONS.SET_SUBSCRIPTION_LATEST,
data: {
subscription,
uri,
},
});
export const setSubscriptionNotification = (
subscription: Subscription,
uri: string,
notificationType: string
) => (dispatch: Dispatch) =>
dispatch({
type: ACTIONS.SET_SUBSCRIPTION_NOTIFICATION,
data: {
subscription,
uri,
type: notificationType,
},
});
export const setSubscriptionNotifications = (notifications: SubscriptionNotifications) => (
dispatch: Dispatch
) =>
@ -149,5 +195,68 @@ export const setSubscriptionNotifications = (notifications: SubscriptionNotifica
},
});
export const setHasFetchedSubscriptions = () => (dispatch: Dispatch) =>
dispatch({ type: ACTIONS.HAS_FETCHED_SUBSCRIPTIONS });
export const doChannelSubscribe = (subscription: Subscription) => (
dispatch: Dispatch,
getState: () => any
) => {
const {
settings: { daemonSettings },
} = getState();
const { share_usage_data: isSharingData } = daemonSettings;
dispatch({
type: ACTIONS.CHANNEL_SUBSCRIBE,
data: subscription,
});
// if the user isn't sharing data, keep the subscriptions entirely in the app
if (isSharingData) {
const { claimId } = parseURI(subscription.uri);
// They are sharing data, we can store their subscriptions in our internal database
Lbryio.call('subscription', 'new', {
channel_name: subscription.channelName,
claim_id: claimId,
});
}
dispatch(doCheckSubscription(subscription, true));
};
export const doChannelUnsubscribe = (subscription: Subscription) => (
dispatch: Dispatch,
getState: () => any
) => {
const {
settings: { daemonSettings },
} = getState();
const { share_usage_data: isSharingData } = daemonSettings;
dispatch({
type: ACTIONS.CHANNEL_UNSUBSCRIBE,
data: subscription,
});
if (isSharingData) {
const { claimId } = parseURI(subscription.uri);
Lbryio.call('subscription', 'delete', {
claim_id: claimId,
});
}
};
export const doCheckSubscriptions = () => (
dispatch: Dispatch,
getState: () => SubscriptionState
) => {
const checkSubscriptionsTimer = setInterval(
() =>
selectSubscriptions(getState()).map((subscription: Subscription) =>
dispatch(doCheckSubscription(subscription, true))
),
CHECK_SUBSCRIPTIONS_INTERVAL
);
dispatch({
type: ACTIONS.CHECK_SUBSCRIPTIONS_SUBSCRIBE,
data: { checkSubscriptionsTimer },
});
};

View file

@ -2,12 +2,7 @@
import * as ACTIONS from 'constants/action_types';
import * as NOTIFICATION_TYPES from 'constants/notification_types';
import { handleActions } from 'util/redux-utils';
export type Subscription = {
channelName: string,
uri: string,
latest: ?string,
};
import type { Subscription } from 'types/subscription';
export type NotificationType =
| NOTIFICATION_TYPES.DOWNLOADING
@ -24,8 +19,8 @@ export type SubscriptionNotifications = {
// Subscription redux types
export type SubscriptionState = {
subscriptions: Array<Subscription>,
hasFetchedSubscriptions: boolean,
notifications: SubscriptionNotifications,
loading: boolean,
};
// Subscription action types
@ -39,10 +34,6 @@ type doChannelUnsubscribe = {
data: Subscription,
};
type HasFetchedSubscriptions = {
type: ACTIONS.HAS_FETCHED_SUBSCRIPTIONS,
};
type setSubscriptionLatest = {
type: ACTIONS.SET_SUBSCRIPTION_LATEST,
data: {
@ -75,10 +66,14 @@ type CheckSubscriptionCompleted = {
type: ACTIONS.CHECK_SUBSCRIPTION_COMPLETED,
};
type fetchedSubscriptionsSucess = {
type: ACTIONS.FETCH_SUBSCRIPTIONS_SUCCESS,
data: Array<Subscription>,
};
export type Action =
| doChannelSubscribe
| doChannelUnsubscribe
| HasFetchedSubscriptions
| setSubscriptionLatest
| setSubscriptionNotification
| CheckSubscriptionStarted
@ -88,8 +83,8 @@ export type Dispatch = (action: Action) => any;
const defaultState = {
subscriptions: [],
hasFetchedSubscriptions: false,
notifications: {},
loading: false,
};
export default handleActions(
@ -122,10 +117,6 @@ export default handleActions(
subscriptions: newSubscriptions,
};
},
[ACTIONS.HAS_FETCHED_SUBSCRIPTIONS]: (state: SubscriptionState): SubscriptionState => ({
...state,
hasFetchedSubscriptions: true,
}),
[ACTIONS.SET_SUBSCRIPTION_LATEST]: (
state: SubscriptionState,
action: setSubscriptionLatest
@ -155,6 +146,22 @@ export default handleActions(
...state,
notifications: action.data.notifications,
}),
[ACTIONS.FETCH_SUBSCRIPTIONS_START]: (state: SubscriptionState): SubscriptionState => ({
...state,
loading: true,
}),
[ACTIONS.FETCH_SUBSCRIPTIONS_FAIL]: (state: SubscriptionState): SubscriptionState => ({
...state,
loading: false,
}),
[ACTIONS.FETCH_SUBSCRIPTIONS_SUCCESS]: (
state: SubscriptionState,
action: fetchedSubscriptionsSucess
): SubscriptionState => ({
...state,
loading: false,
subscriptions: action.data,
}),
},
defaultState
);

View file

@ -1,15 +1,21 @@
import { createSelector } from 'reselect';
import { selectAllClaimsByChannel, selectClaimsById } from 'lbry-redux';
import {
selectAllClaimsByChannel,
selectClaimsById,
selectAllFetchingChannelClaims,
} from 'lbry-redux';
// get the entire subscriptions state
const selectState = state => state.subscriptions || {};
export const selectIsFetchingSubscriptions = createSelector(selectState, state => state.loading);
export const selectNotifications = createSelector(selectState, state => state.notifications);
// list of saved channel names and uris
export const selectSubscriptions = createSelector(selectState, state => state.subscriptions);
export const selectSubscriptionsFromClaims = createSelector(
export const selectSubscriptionClaims = createSelector(
selectAllClaimsByChannel,
selectClaimsById,
selectSubscriptions,
@ -37,9 +43,6 @@ export const selectSubscriptionsFromClaims = createSelector(
});
}
// all we really need is a uri for each claim
channelClaims = channelClaims.map(claim => `${claim.name}#${claim.claim_id}`);
fetchedSubscriptions.push({
claims: channelClaims,
channelName: subscription.channelName,
@ -50,3 +53,19 @@ export const selectSubscriptionsFromClaims = createSelector(
return fetchedSubscriptions;
}
);
export const selectSubscriptionsBeingFetched = createSelector(
selectSubscriptions,
selectAllFetchingChannelClaims,
(subscriptions, fetchingChannelClaims) => {
const fetchingSubscriptionMap = {};
subscriptions.forEach(sub => {
const isFetching = fetchingChannelClaims && fetchingChannelClaims[sub.uri];
if (isFetching) {
fetchingSubscriptionMap[sub.uri] = 1;
}
});
return fetchingSubscriptionMap;
}
);

View file

@ -221,6 +221,9 @@ p {
margin-top: 200px;
text-align: center;
font-family: 'metropolis-medium';
display: flex;
flex-direction: column;
align-items: center;
}
.columns {

View file

@ -299,25 +299,12 @@
display: inline-block;
vertical-align: top;
margin-bottom: 60px;
@media only screen and (max-width: $medium-breakpoint) {
width: calc((100% / 3) - (40px / 3));
&:not(:nth-child(3n + 1)) {
margin-left: 20px;
}
}
}
@media only screen and (min-width: $medium-breakpoint) {
.card {
width: calc((100% / 4) - (60px / 4));
&:not(:nth-child(4n + 1)) {
margin-left: 20px;
}
}
}
}
.card__list--rewards {
@ -341,8 +328,8 @@
display: inline-block;
vertical-align: top;
overflow: visible;
// 35 px to handle to padding between cards
width: calc((100% / 3) - 35px);
// 31 px to handle to padding between cards
width: calc((100% / 4) - 31px);
}
.card:not(:first-of-type) {
@ -352,13 +339,6 @@
.card:last-of-type {
margin-right: 20px;
}
@media only screen and (min-width: $medium-breakpoint) {
.card {
// 31 px to handle to padding between cards
width: calc((100% / 4) - 31px);
}
}
}
.card__success-msg {

View file

@ -10,7 +10,6 @@
height: 100%;
width: 6px;
margin: 0 2px;
background-color: var(--color-white);
animation: sk-stretchdelay 1.2s infinite ease-in-out;
&.rect2 {
@ -31,6 +30,12 @@
}
}
.spinner--light {
.rect {
background-color: var(--color-white);
}
}
.spinner--dark {
.rect {
background-color: var(--color-black);

View file

@ -102,6 +102,7 @@ const store = createStore(
const compressor = createCompressor();
const saveClaimsFilter = createFilter('claims', ['byId', 'claimsByUri']);
const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
// We only need to persist the receiveAddress for the wallet
const walletFilter = createFilter('wallet', ['receiveAddress']);

View file

@ -0,0 +1,30 @@
// @flow
// Actual claim type has more values than this
// Add them as they are used
export type Claim = {
address: string,
amount: number,
claim_id: string,
claim_sequence: number,
decoded_claim: boolean,
depth: number,
effective_amount: number,
has_signature: boolean,
height: number,
has_signature: boolean,
hex: string,
name: string,
nout: number,
permanent_url: string,
channel_name: ?string,
txid: string,
nout: number,
signature_is_valid: boolean,
valid_at_height: number,
value: {
publisherSignature: ?{
certificateId: ?string,
},
},
};

View file

@ -0,0 +1,7 @@
// @flow
export type Subscription = {
channelName: string, // @CryptoCandor,
uri: string, // lbry://@CryptoCandor#9152f3b054f692076a6882d1b58a30e8781cc8e6
latest: string, // substratum#b0ab143243020e7831fd070d9f871e1fda948620
};

12
src/renderer/util/dom.js Normal file
View file

@ -0,0 +1,12 @@
// @flow
import * as React from 'react';
// is a child component being rendered?
export const isShowingChildren = (children: React.Node): boolean => {
if (Array.isArray(children)) {
const firstChildIndex = children.findIndex(child => child);
return firstChildIndex > -1;
}
return !!children;
};