Desktop cherry-pick: "7240 Integrate lbry redux and lbryinc"
This commit is contained in:
parent
702297e722
commit
30023422b8
419 changed files with 12085 additions and 1172 deletions
|
@ -7,13 +7,6 @@
|
||||||
[include]
|
[include]
|
||||||
|
|
||||||
[libs]
|
[libs]
|
||||||
./flow-typed
|
|
||||||
node_modules/lbry-redux/flow-typed/
|
|
||||||
node_modules/lbryinc/flow-typed/
|
|
||||||
|
|
||||||
[untyped]
|
|
||||||
.*/node_modules/lbry-redux
|
|
||||||
.*/node_modules/lbryinc
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|
||||||
|
@ -31,7 +24,7 @@ module.name_mapper='^modal\(.*\)$' -> '<PROJECT_ROOT>/ui/modal\1'
|
||||||
module.name_mapper='^app\(.*\)$' -> '<PROJECT_ROOT>/ui/app\1'
|
module.name_mapper='^app\(.*\)$' -> '<PROJECT_ROOT>/ui/app\1'
|
||||||
module.name_mapper='^native\(.*\)$' -> '<PROJECT_ROOT>/ui/native\1'
|
module.name_mapper='^native\(.*\)$' -> '<PROJECT_ROOT>/ui/native\1'
|
||||||
module.name_mapper='^analytics\(.*\)$' -> '<PROJECT_ROOT>/ui/analytics\1'
|
module.name_mapper='^analytics\(.*\)$' -> '<PROJECT_ROOT>/ui/analytics\1'
|
||||||
module.name_mapper='^recsys\(.*\)$' -> '<PROJECT_ROOT>/ui/recsys\1'
|
module.name_mapper='^recsys\(.*\)$' -> '<PROJECT_ROOT>/extras/recsys\1'
|
||||||
module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/ui/rewards\1'
|
module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/ui/rewards\1'
|
||||||
module.name_mapper='^i18n\(.*\)$' -> '<PROJECT_ROOT>/ui/i18n\1'
|
module.name_mapper='^i18n\(.*\)$' -> '<PROJECT_ROOT>/ui/i18n\1'
|
||||||
module.name_mapper='^effects\(.*\)$' -> '<PROJECT_ROOT>/ui/effects\1'
|
module.name_mapper='^effects\(.*\)$' -> '<PROJECT_ROOT>/ui/effects\1'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { spawn, execSync } from 'child_process';
|
import { spawn, execSync } from 'child_process';
|
||||||
import { Lbry } from 'lbry-redux';
|
import Lbry from 'lbry';
|
||||||
|
|
||||||
export default class Daemon {
|
export default class Daemon {
|
||||||
static lbrynetPath =
|
static lbrynetPath =
|
||||||
|
|
|
@ -6,7 +6,7 @@ import SemVer from 'semver';
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
import { app, dialog, ipcMain, session, shell } from 'electron';
|
import { app, dialog, ipcMain, session, shell } from 'electron';
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
import { Lbry } from 'lbry-redux';
|
import Lbry from 'lbry';
|
||||||
import LbryFirstInstance from './LbryFirstInstance';
|
import LbryFirstInstance from './LbryFirstInstance';
|
||||||
import Daemon from './Daemon';
|
import Daemon from './Daemon';
|
||||||
import isDev from 'electron-is-dev';
|
import isDev from 'electron-is-dev';
|
||||||
|
|
|
@ -8,7 +8,7 @@ if (typeof global.fetch === 'object') {
|
||||||
global.fetch = global.fetch.default;
|
global.fetch = global.fetch.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Lbry } = require('lbry-redux');
|
const Lbry = require('lbry');
|
||||||
|
|
||||||
delete global.window;
|
delete global.window;
|
||||||
|
|
||||||
|
|
184
extras/lbry-first/lbry-first.js
Normal file
184
extras/lbry-first/lbry-first.js
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
// @flow
|
||||||
|
/*
|
||||||
|
LBRY FIRST does not work due to api changes
|
||||||
|
*/
|
||||||
|
import 'proxy-polyfill';
|
||||||
|
|
||||||
|
const CHECK_LBRYFIRST_STARTED_TRY_NUMBER = 200;
|
||||||
|
//
|
||||||
|
// Basic LBRYFIRST connection config
|
||||||
|
// Offers a proxy to call LBRYFIRST methods
|
||||||
|
|
||||||
|
//
|
||||||
|
const LbryFirst: LbryFirstTypes = {
|
||||||
|
isConnected: false,
|
||||||
|
connectPromise: null,
|
||||||
|
lbryFirstConnectionString: 'http://localhost:1337/rpc',
|
||||||
|
apiRequestHeaders: { 'Content-Type': 'application/json' },
|
||||||
|
|
||||||
|
// Allow overriding lbryFirst connection string (e.g. to `/api/proxy` for lbryweb)
|
||||||
|
setLbryFirstConnectionString: (value: string) => {
|
||||||
|
LbryFirst.lbryFirstConnectionString = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
setApiHeader: (key: string, value: string) => {
|
||||||
|
LbryFirst.apiRequestHeaders = Object.assign(LbryFirst.apiRequestHeaders, { [key]: value });
|
||||||
|
},
|
||||||
|
|
||||||
|
unsetApiHeader: key => {
|
||||||
|
Object.keys(LbryFirst.apiRequestHeaders).includes(key) &&
|
||||||
|
delete LbryFirst.apiRequestHeaders['key'];
|
||||||
|
},
|
||||||
|
// Allow overriding Lbry methods
|
||||||
|
overrides: {},
|
||||||
|
setOverride: (methodName, newMethod) => {
|
||||||
|
LbryFirst.overrides[methodName] = newMethod;
|
||||||
|
},
|
||||||
|
getApiRequestHeaders: () => LbryFirst.apiRequestHeaders,
|
||||||
|
|
||||||
|
// LbryFirst Methods
|
||||||
|
status: (params = {}) => lbryFirstCallWithResult('status', params),
|
||||||
|
stop: () => lbryFirstCallWithResult('stop', {}),
|
||||||
|
version: () => lbryFirstCallWithResult('version', {}),
|
||||||
|
|
||||||
|
// Upload to youtube
|
||||||
|
upload: (params: { title: string, description: string, file_path: ?string } = {}) => {
|
||||||
|
// Only upload when originally publishing for now
|
||||||
|
if (!params.file_path) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadParams: {
|
||||||
|
Title: string,
|
||||||
|
Description: string,
|
||||||
|
FilePath: string,
|
||||||
|
Category: string,
|
||||||
|
Keywords: string,
|
||||||
|
} = {
|
||||||
|
Title: params.title,
|
||||||
|
Description: params.description,
|
||||||
|
FilePath: params.file_path,
|
||||||
|
Category: '',
|
||||||
|
Keywords: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
return lbryFirstCallWithResult('youtube.Upload', uploadParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasYTAuth: (token: string) => {
|
||||||
|
const hasYTAuthParams = {};
|
||||||
|
hasYTAuthParams.AuthToken = token;
|
||||||
|
return lbryFirstCallWithResult('youtube.HasAuth', hasYTAuthParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
ytSignup: () => {
|
||||||
|
const emptyParams = {};
|
||||||
|
return lbryFirstCallWithResult('youtube.Signup', emptyParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: () => {
|
||||||
|
const emptyParams = {};
|
||||||
|
return lbryFirstCallWithResult('youtube.Remove', emptyParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Connect to lbry-first
|
||||||
|
connect: () => {
|
||||||
|
if (LbryFirst.connectPromise === null) {
|
||||||
|
LbryFirst.connectPromise = new Promise((resolve, reject) => {
|
||||||
|
let tryNum = 0;
|
||||||
|
// Check every half second to see if the lbryFirst is accepting connections
|
||||||
|
function checkLbryFirstStarted() {
|
||||||
|
tryNum += 1;
|
||||||
|
LbryFirst.status()
|
||||||
|
.then(resolve)
|
||||||
|
.catch(() => {
|
||||||
|
if (tryNum <= CHECK_LBRYFIRST_STARTED_TRY_NUMBER) {
|
||||||
|
setTimeout(checkLbryFirstStarted, tryNum < 50 ? 400 : 1000);
|
||||||
|
} else {
|
||||||
|
reject(new Error('Unable to connect to LBRY'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLbryFirstStarted();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flow thinks this could be empty, but it will always return a promise
|
||||||
|
// $FlowFixMe
|
||||||
|
return LbryFirst.connectPromise;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkAndParse(response) {
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
return response.json().then(json => {
|
||||||
|
let error;
|
||||||
|
if (json.error) {
|
||||||
|
const errorMessage = typeof json.error === 'object' ? json.error.message : json.error;
|
||||||
|
error = new Error(errorMessage);
|
||||||
|
} else {
|
||||||
|
error = new Error('Protocol error with unknown response signature');
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function apiCall(method: string, params: ?{}, resolve: Function, reject: Function) {
|
||||||
|
const counter = new Date().getTime();
|
||||||
|
const paramsArray = [params];
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: LbryFirst.apiRequestHeaders,
|
||||||
|
body: JSON.stringify({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
method,
|
||||||
|
params: paramsArray,
|
||||||
|
id: counter,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetch(LbryFirst.lbryFirstConnectionString, options)
|
||||||
|
.then(checkAndParse)
|
||||||
|
.then(response => {
|
||||||
|
const error = response.error || (response.result && response.result.error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return reject(error);
|
||||||
|
}
|
||||||
|
return resolve(response.result);
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lbryFirstCallWithResult(name: string, params: ?{} = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
apiCall(
|
||||||
|
name,
|
||||||
|
params,
|
||||||
|
result => {
|
||||||
|
resolve(result);
|
||||||
|
},
|
||||||
|
reject
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is only for a fallback
|
||||||
|
// If there is a LbryFirst method that is being called by an app, it should be added to /flow-typed/LbryFirst.js
|
||||||
|
const lbryFirstProxy = new Proxy(LbryFirst, {
|
||||||
|
get(target: LbryFirstTypes, name: string) {
|
||||||
|
if (name in target) {
|
||||||
|
return target[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (params = {}) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
apiCall(name, params, resolve, reject);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default lbryFirstProxy;
|
97
extras/lbryinc/constants/action_types.js
Normal file
97
extras/lbryinc/constants/action_types.js
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// Claims
|
||||||
|
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
|
||||||
|
export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED';
|
||||||
|
export const FETCH_TRENDING_CONTENT_STARTED = 'FETCH_TRENDING_CONTENT_STARTED';
|
||||||
|
export const FETCH_TRENDING_CONTENT_COMPLETED = 'FETCH_TRENDING_CONTENT_COMPLETED';
|
||||||
|
export const RESOLVE_URIS_STARTED = 'RESOLVE_URIS_STARTED';
|
||||||
|
export const RESOLVE_URIS_COMPLETED = 'RESOLVE_URIS_COMPLETED';
|
||||||
|
export const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED';
|
||||||
|
export const FETCH_CHANNEL_CLAIMS_COMPLETED = 'FETCH_CHANNEL_CLAIMS_COMPLETED';
|
||||||
|
export const FETCH_CHANNEL_CLAIM_COUNT_STARTED = 'FETCH_CHANNEL_CLAIM_COUNT_STARTED';
|
||||||
|
export const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED = 'FETCH_CHANNEL_CLAIM_COUNT_COMPLETED';
|
||||||
|
export const FETCH_CLAIM_LIST_MINE_STARTED = 'FETCH_CLAIM_LIST_MINE_STARTED';
|
||||||
|
export const FETCH_CLAIM_LIST_MINE_COMPLETED = 'FETCH_CLAIM_LIST_MINE_COMPLETED';
|
||||||
|
export const ABANDON_CLAIM_STARTED = 'ABANDON_CLAIM_STARTED';
|
||||||
|
export const ABANDON_CLAIM_SUCCEEDED = 'ABANDON_CLAIM_SUCCEEDED';
|
||||||
|
export const FETCH_CHANNEL_LIST_STARTED = 'FETCH_CHANNEL_LIST_STARTED';
|
||||||
|
export const FETCH_CHANNEL_LIST_COMPLETED = 'FETCH_CHANNEL_LIST_COMPLETED';
|
||||||
|
export const CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED';
|
||||||
|
export const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
|
||||||
|
export const PUBLISH_STARTED = 'PUBLISH_STARTED';
|
||||||
|
export const PUBLISH_COMPLETED = 'PUBLISH_COMPLETED';
|
||||||
|
export const PUBLISH_FAILED = 'PUBLISH_FAILED';
|
||||||
|
export const SET_PLAYING_URI = 'SET_PLAYING_URI';
|
||||||
|
export const SET_CONTENT_POSITION = 'SET_CONTENT_POSITION';
|
||||||
|
export const SET_CONTENT_LAST_VIEWED = 'SET_CONTENT_LAST_VIEWED';
|
||||||
|
export const CLEAR_CONTENT_HISTORY_URI = 'CLEAR_CONTENT_HISTORY_URI';
|
||||||
|
export const CLEAR_CONTENT_HISTORY_ALL = 'CLEAR_CONTENT_HISTORY_ALL';
|
||||||
|
|
||||||
|
// Subscriptions
|
||||||
|
export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
|
||||||
|
export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
|
||||||
|
export const CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS =
|
||||||
|
'CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS';
|
||||||
|
export const CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS =
|
||||||
|
'CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS';
|
||||||
|
export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
|
||||||
|
export const SET_SUBSCRIPTION_LATEST = 'SET_SUBSCRIPTION_LATEST';
|
||||||
|
export const UPDATE_SUBSCRIPTION_UNREADS = 'UPDATE_SUBSCRIPTION_UNREADS';
|
||||||
|
export const REMOVE_SUBSCRIPTION_UNREADS = 'REMOVE_SUBSCRIPTION_UNREADS';
|
||||||
|
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';
|
||||||
|
export const SET_VIEW_MODE = 'SET_VIEW_MODE';
|
||||||
|
export const GET_SUGGESTED_SUBSCRIPTIONS_START = 'GET_SUGGESTED_SUBSCRIPTIONS_START';
|
||||||
|
export const GET_SUGGESTED_SUBSCRIPTIONS_SUCCESS = 'GET_SUGGESTED_SUBSCRIPTIONS_SUCCESS';
|
||||||
|
export const GET_SUGGESTED_SUBSCRIPTIONS_FAIL = 'GET_SUGGESTED_SUBSCRIPTIONS_FAIL';
|
||||||
|
export const SUBSCRIPTION_FIRST_RUN_COMPLETED = 'SUBSCRIPTION_FIRST_RUN_COMPLETED';
|
||||||
|
export const VIEW_SUGGESTED_SUBSCRIPTIONS = 'VIEW_SUGGESTED_SUBSCRIPTIONS';
|
||||||
|
|
||||||
|
// Blacklist
|
||||||
|
export const FETCH_BLACK_LISTED_CONTENT_STARTED = 'FETCH_BLACK_LISTED_CONTENT_STARTED';
|
||||||
|
export const FETCH_BLACK_LISTED_CONTENT_COMPLETED = 'FETCH_BLACK_LISTED_CONTENT_COMPLETED';
|
||||||
|
export const FETCH_BLACK_LISTED_CONTENT_FAILED = 'FETCH_BLACK_LISTED_CONTENT_FAILED';
|
||||||
|
export const BLACK_LISTED_CONTENT_SUBSCRIBE = 'BLACK_LISTED_CONTENT_SUBSCRIBE';
|
||||||
|
|
||||||
|
// Filtered list
|
||||||
|
export const FETCH_FILTERED_CONTENT_STARTED = 'FETCH_FILTERED_CONTENT_STARTED';
|
||||||
|
export const FETCH_FILTERED_CONTENT_COMPLETED = 'FETCH_FILTERED_CONTENT_COMPLETED';
|
||||||
|
export const FETCH_FILTERED_CONTENT_FAILED = 'FETCH_FILTERED_CONTENT_FAILED';
|
||||||
|
export const FILTERED_CONTENT_SUBSCRIBE = 'FILTERED_CONTENT_SUBSCRIBE';
|
||||||
|
|
||||||
|
// Cost Info
|
||||||
|
export const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED';
|
||||||
|
export const FETCH_COST_INFO_COMPLETED = 'FETCH_COST_INFO_COMPLETED';
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
export const FETCH_VIEW_COUNT_STARTED = 'FETCH_VIEW_COUNT_STARTED';
|
||||||
|
export const FETCH_VIEW_COUNT_FAILED = 'FETCH_VIEW_COUNT_FAILED';
|
||||||
|
export const FETCH_VIEW_COUNT_COMPLETED = 'FETCH_VIEW_COUNT_COMPLETED';
|
||||||
|
export const FETCH_SUB_COUNT_STARTED = 'FETCH_SUB_COUNT_STARTED';
|
||||||
|
export const FETCH_SUB_COUNT_FAILED = 'FETCH_SUB_COUNT_FAILED';
|
||||||
|
export const FETCH_SUB_COUNT_COMPLETED = 'FETCH_SUB_COUNT_COMPLETED';
|
||||||
|
|
||||||
|
// Cross-device Sync
|
||||||
|
export const GET_SYNC_STARTED = 'GET_SYNC_STARTED';
|
||||||
|
export const GET_SYNC_COMPLETED = 'GET_SYNC_COMPLETED';
|
||||||
|
export const GET_SYNC_FAILED = 'GET_SYNC_FAILED';
|
||||||
|
export const SET_SYNC_STARTED = 'SET_SYNC_STARTED';
|
||||||
|
export const SET_SYNC_FAILED = 'SET_SYNC_FAILED';
|
||||||
|
export const SET_SYNC_COMPLETED = 'SET_SYNC_COMPLETED';
|
||||||
|
export const SET_DEFAULT_ACCOUNT = 'SET_DEFAULT_ACCOUNT';
|
||||||
|
export const SYNC_APPLY_STARTED = 'SYNC_APPLY_STARTED';
|
||||||
|
export const SYNC_APPLY_COMPLETED = 'SYNC_APPLY_COMPLETED';
|
||||||
|
export const SYNC_APPLY_FAILED = 'SYNC_APPLY_FAILED';
|
||||||
|
export const SYNC_APPLY_BAD_PASSWORD = 'SYNC_APPLY_BAD_PASSWORD';
|
||||||
|
export const SYNC_RESET = 'SYNC_RESET';
|
||||||
|
|
||||||
|
// Lbry.tv
|
||||||
|
export const UPDATE_UPLOAD_PROGRESS = 'UPDATE_UPLOAD_PROGRESS';
|
||||||
|
|
||||||
|
// User
|
||||||
|
export const GENERATE_AUTH_TOKEN_FAILURE = 'GENERATE_AUTH_TOKEN_FAILURE';
|
||||||
|
export const GENERATE_AUTH_TOKEN_STARTED = 'GENERATE_AUTH_TOKEN_STARTED';
|
||||||
|
export const GENERATE_AUTH_TOKEN_SUCCESS = 'GENERATE_AUTH_TOKEN_SUCCESS';
|
5
extras/lbryinc/constants/claim.js
Normal file
5
extras/lbryinc/constants/claim.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export const MINIMUM_PUBLISH_BID = 0.00000001;
|
||||||
|
|
||||||
|
export const CHANNEL_ANONYMOUS = 'anonymous';
|
||||||
|
export const CHANNEL_NEW = 'new';
|
||||||
|
export const PAGE_SIZE = 20;
|
4
extras/lbryinc/constants/errors.js
Normal file
4
extras/lbryinc/constants/errors.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export const ALREADY_CLAIMED =
|
||||||
|
'once the invite reward has been claimed the referrer cannot be changed';
|
||||||
|
export const REFERRER_NOT_FOUND =
|
||||||
|
'A lbry.tv account could not be found for the referrer you provided.';
|
11
extras/lbryinc/constants/youtube.js
Normal file
11
extras/lbryinc/constants/youtube.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export const YOUTUBE_SYNC_NOT_TRANSFERRED = 'not_transferred';
|
||||||
|
export const YOUTUBE_SYNC_PENDING = 'pending';
|
||||||
|
export const YOUTUBE_SYNC_PENDING_EMAIL = 'pendingemail';
|
||||||
|
export const YOUTUBE_SYNC_PENDING_TRANSFER = 'pending_transfer';
|
||||||
|
export const YOUTUBE_SYNC_COMPLETED_TRANSFER = 'completed_transfer';
|
||||||
|
export const YOUTUBE_SYNC_QUEUED = 'queued';
|
||||||
|
export const YOUTUBE_SYNC_SYNCING = 'syncing';
|
||||||
|
export const YOUTUBE_SYNC_SYNCED = 'synced';
|
||||||
|
export const YOUTUBE_SYNC_FAILED = 'failed';
|
||||||
|
export const YOUTUBE_SYNC_PENDINGUPGRADE = 'pendingupgrade';
|
||||||
|
export const YOUTUBE_SYNC_ABANDONDED = 'abandoned';
|
83
extras/lbryinc/index.js
Normal file
83
extras/lbryinc/index.js
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import * as LBRYINC_ACTIONS from 'constants/action_types';
|
||||||
|
import * as YOUTUBE_STATUSES from 'constants/youtube';
|
||||||
|
import * as ERRORS from 'constants/errors';
|
||||||
|
import Lbryio from './lbryio';
|
||||||
|
|
||||||
|
export { Lbryio };
|
||||||
|
|
||||||
|
export function testTheThing() {
|
||||||
|
console.log('tested');
|
||||||
|
}
|
||||||
|
|
||||||
|
// constants
|
||||||
|
export { LBRYINC_ACTIONS, YOUTUBE_STATUSES, ERRORS };
|
||||||
|
|
||||||
|
// utils
|
||||||
|
export { doTransifexUpload } from 'util/transifex-upload';
|
||||||
|
|
||||||
|
// actions
|
||||||
|
export { doGenerateAuthToken } from './redux/actions/auth';
|
||||||
|
export { doFetchCostInfoForUri } from './redux/actions/cost_info';
|
||||||
|
export { doBlackListedOutpointsSubscribe } from './redux/actions/blacklist';
|
||||||
|
export { doFilteredOutpointsSubscribe } from './redux/actions/filtered';
|
||||||
|
// export { doFetchFeaturedUris, doFetchTrendingUris } from './redux/actions/homepage';
|
||||||
|
export { doFetchViewCount, doFetchSubCount } from './redux/actions/stats';
|
||||||
|
export {
|
||||||
|
doCheckSync,
|
||||||
|
doGetSync,
|
||||||
|
doSetSync,
|
||||||
|
doSetDefaultAccount,
|
||||||
|
doSyncApply,
|
||||||
|
doResetSync,
|
||||||
|
doSyncEncryptAndDecrypt,
|
||||||
|
} from 'redux/actions/sync';
|
||||||
|
export { doUpdateUploadProgress } from './redux/actions/web';
|
||||||
|
|
||||||
|
// reducers
|
||||||
|
export { authReducer } from './redux/reducers/auth';
|
||||||
|
export { costInfoReducer } from './redux/reducers/cost_info';
|
||||||
|
export { blacklistReducer } from './redux/reducers/blacklist';
|
||||||
|
export { filteredReducer } from './redux/reducers/filtered';
|
||||||
|
// export { homepageReducer } from './redux/reducers/homepage';
|
||||||
|
export { statsReducer } from './redux/reducers/stats';
|
||||||
|
export { syncReducer } from './redux/reducers/sync';
|
||||||
|
export { webReducer } from './redux/reducers/web';
|
||||||
|
|
||||||
|
// selectors
|
||||||
|
export { selectAuthToken, selectIsAuthenticating } from './redux/selectors/auth';
|
||||||
|
export {
|
||||||
|
makeSelectFetchingCostInfoForUri,
|
||||||
|
makeSelectCostInfoForUri,
|
||||||
|
selectAllCostInfoByUri,
|
||||||
|
selectFetchingCostInfo,
|
||||||
|
} from './redux/selectors/cost_info';
|
||||||
|
export {
|
||||||
|
selectBlackListedOutpoints,
|
||||||
|
selectBlacklistedOutpointMap,
|
||||||
|
} from './redux/selectors/blacklist';
|
||||||
|
export { selectFilteredOutpoints, selectFilteredOutpointMap } from './redux/selectors/filtered';
|
||||||
|
// export {
|
||||||
|
// selectFeaturedUris,
|
||||||
|
// selectFetchingFeaturedUris,
|
||||||
|
// selectTrendingUris,
|
||||||
|
// selectFetchingTrendingUris,
|
||||||
|
// } from './redux/selectors/homepage';
|
||||||
|
export {
|
||||||
|
selectViewCount,
|
||||||
|
makeSelectViewCountForUri,
|
||||||
|
makeSelectSubCountForUri,
|
||||||
|
} from './redux/selectors/stats';
|
||||||
|
export {
|
||||||
|
selectHasSyncedWallet,
|
||||||
|
selectSyncData,
|
||||||
|
selectSyncHash,
|
||||||
|
selectSetSyncErrorMessage,
|
||||||
|
selectGetSyncErrorMessage,
|
||||||
|
selectGetSyncIsPending,
|
||||||
|
selectSetSyncIsPending,
|
||||||
|
selectSyncApplyIsPending,
|
||||||
|
selectHashChanged,
|
||||||
|
selectSyncApplyErrorMessage,
|
||||||
|
selectSyncApplyPasswordError,
|
||||||
|
} from './redux/selectors/sync';
|
||||||
|
export { selectCurrentUploads, selectUploadCount } from './redux/selectors/web';
|
238
extras/lbryinc/lbryio.js
Normal file
238
extras/lbryinc/lbryio.js
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import Lbry from 'lbry';
|
||||||
|
import querystring from 'querystring';
|
||||||
|
|
||||||
|
const Lbryio = {
|
||||||
|
enabled: true,
|
||||||
|
authenticationPromise: null,
|
||||||
|
exchangePromise: null,
|
||||||
|
exchangeLastFetched: null,
|
||||||
|
CONNECTION_STRING: 'https://api.lbry.com/',
|
||||||
|
};
|
||||||
|
|
||||||
|
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
||||||
|
const INTERNAL_APIS_DOWN = 'internal_apis_down';
|
||||||
|
|
||||||
|
// We can't use env's because they aren't passed into node_modules
|
||||||
|
Lbryio.setLocalApi = endpoint => {
|
||||||
|
Lbryio.CONNECTION_STRING = endpoint.replace(/\/*$/, '/'); // exactly one slash at the end;
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.call = (resource, action, params = {}, method = 'get') => {
|
||||||
|
if (!Lbryio.enabled) {
|
||||||
|
return Promise.reject(new Error(__('LBRY internal API is disabled')));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(method === 'get' || method === 'post')) {
|
||||||
|
return Promise.reject(new Error(__('Invalid method')));
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAndParse(response) {
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === 500) {
|
||||||
|
return Promise.reject(INTERNAL_APIS_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response)
|
||||||
|
return response.json().then(json => {
|
||||||
|
let error;
|
||||||
|
if (json.error) {
|
||||||
|
error = new Error(json.error);
|
||||||
|
} else {
|
||||||
|
error = new Error('Unknown API error signature');
|
||||||
|
}
|
||||||
|
error.response = response; // This is primarily a hack used in actions/user.js
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRequest(url, options) {
|
||||||
|
return fetch(url, options).then(checkAndParse);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbryio.getAuthToken().then(token => {
|
||||||
|
const fullParams = { auth_token: token, ...params };
|
||||||
|
Object.keys(fullParams).forEach(key => {
|
||||||
|
const value = fullParams[key];
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
fullParams[key] = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const qs = querystring.stringify(fullParams);
|
||||||
|
let url = `${Lbryio.CONNECTION_STRING}${resource}/${action}?${qs}`;
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (method === 'post') {
|
||||||
|
options = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: qs,
|
||||||
|
};
|
||||||
|
url = `${Lbryio.CONNECTION_STRING}${resource}/${action}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeRequest(url, options).then(response => response.data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.authToken = null;
|
||||||
|
|
||||||
|
Lbryio.getAuthToken = () =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
if (Lbryio.authToken) {
|
||||||
|
resolve(Lbryio.authToken);
|
||||||
|
} else if (Lbryio.overrides.getAuthToken) {
|
||||||
|
Lbryio.overrides.getAuthToken().then(token => {
|
||||||
|
resolve(token);
|
||||||
|
});
|
||||||
|
} else if (typeof window !== 'undefined') {
|
||||||
|
const { store } = window;
|
||||||
|
if (store) {
|
||||||
|
const state = store.getState();
|
||||||
|
const token = state.auth ? state.auth.authToken : null;
|
||||||
|
Lbryio.authToken = token;
|
||||||
|
resolve(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbryio.getCurrentUser = () => Lbryio.call('user', 'me');
|
||||||
|
|
||||||
|
Lbryio.authenticate = (domain, language) => {
|
||||||
|
if (!Lbryio.enabled) {
|
||||||
|
const params = {
|
||||||
|
id: 1,
|
||||||
|
primary_email: 'disabled@lbry.io',
|
||||||
|
has_verified_email: true,
|
||||||
|
is_identity_verified: true,
|
||||||
|
is_reward_approved: false,
|
||||||
|
language: language || 'en',
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
resolve(params);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lbryio.authenticationPromise === null) {
|
||||||
|
Lbryio.authenticationPromise = new Promise((resolve, reject) => {
|
||||||
|
Lbryio.getAuthToken()
|
||||||
|
.then(token => {
|
||||||
|
if (!token || token.length > 60) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that token works
|
||||||
|
return Lbryio.getCurrentUser()
|
||||||
|
.then(user => user)
|
||||||
|
.catch(error => {
|
||||||
|
if (error === INTERNAL_APIS_DOWN) {
|
||||||
|
throw new Error('Internal APIS down');
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(user => {
|
||||||
|
if (user) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbry.status()
|
||||||
|
.then(
|
||||||
|
status =>
|
||||||
|
new Promise((res, rej) => {
|
||||||
|
const appId =
|
||||||
|
domain && domain !== 'lbry.tv'
|
||||||
|
? (domain.replace(/[.]/gi, '') + status.installation_id).slice(0, 66)
|
||||||
|
: status.installation_id;
|
||||||
|
Lbryio.call(
|
||||||
|
'user',
|
||||||
|
'new',
|
||||||
|
{
|
||||||
|
auth_token: '',
|
||||||
|
language: language || 'en',
|
||||||
|
app_id: appId,
|
||||||
|
},
|
||||||
|
'post'
|
||||||
|
)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.auth_token) {
|
||||||
|
throw new Error('auth_token was not set in the response');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { store } = window;
|
||||||
|
if (Lbryio.overrides.setAuthToken) {
|
||||||
|
Lbryio.overrides.setAuthToken(response.auth_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (store) {
|
||||||
|
store.dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS,
|
||||||
|
data: { authToken: response.auth_token },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Lbryio.authToken = response.auth_token;
|
||||||
|
return res(response);
|
||||||
|
})
|
||||||
|
.catch(error => rej(error));
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(newUser => {
|
||||||
|
if (!newUser) {
|
||||||
|
return Lbryio.getCurrentUser();
|
||||||
|
}
|
||||||
|
return newUser;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(resolve, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbryio.authenticationPromise;
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.getStripeToken = () =>
|
||||||
|
Lbryio.CONNECTION_STRING.startsWith('http://localhost:')
|
||||||
|
? 'pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
|
||||||
|
: 'pk_live_e8M4dRNnCCbmpZzduEUZBgJO';
|
||||||
|
|
||||||
|
Lbryio.getExchangeRates = () => {
|
||||||
|
if (
|
||||||
|
!Lbryio.exchangeLastFetched ||
|
||||||
|
Date.now() - Lbryio.exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
|
||||||
|
) {
|
||||||
|
Lbryio.exchangePromise = new Promise((resolve, reject) => {
|
||||||
|
Lbryio.call('lbc', 'exchange_rate', {}, 'get', true)
|
||||||
|
.then(({ lbc_usd: LBC_USD, lbc_btc: LBC_BTC, btc_usd: BTC_USD }) => {
|
||||||
|
const rates = { LBC_USD, LBC_BTC, BTC_USD };
|
||||||
|
resolve(rates);
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
Lbryio.exchangeLastFetched = Date.now();
|
||||||
|
}
|
||||||
|
return Lbryio.exchangePromise;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allow overriding lbryio methods
|
||||||
|
// The desktop app will need to use it for getAuthToken because we use electron's ipcRenderer
|
||||||
|
Lbryio.overrides = {};
|
||||||
|
Lbryio.setOverride = (methodName, newMethod) => {
|
||||||
|
Lbryio.overrides[methodName] = newMethod;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Lbryio;
|
38
extras/lbryinc/redux/actions/auth.js
Normal file
38
extras/lbryinc/redux/actions/auth.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
|
||||||
|
export function doGenerateAuthToken(installationId) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbryio.call(
|
||||||
|
'user',
|
||||||
|
'new',
|
||||||
|
{
|
||||||
|
auth_token: '',
|
||||||
|
language: 'en',
|
||||||
|
app_id: installationId,
|
||||||
|
},
|
||||||
|
'post'
|
||||||
|
)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.auth_token) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_FAILURE,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS,
|
||||||
|
data: { authToken: response.auth_token },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_FAILURE,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
52
extras/lbryinc/redux/actions/blacklist.js
Normal file
52
extras/lbryinc/redux/actions/blacklist.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const CHECK_BLACK_LISTED_CONTENT_INTERVAL = 60 * 60 * 1000;
|
||||||
|
|
||||||
|
export function doFetchBlackListedOutpoints() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const success = ({ outpoints }) => {
|
||||||
|
const splitOutpoints = [];
|
||||||
|
if (outpoints) {
|
||||||
|
outpoints.forEach((outpoint, index) => {
|
||||||
|
const [txid, nout] = outpoint.split(':');
|
||||||
|
|
||||||
|
splitOutpoints[index] = { txid, nout: Number.parseInt(nout, 10) };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_COMPLETED,
|
||||||
|
data: {
|
||||||
|
outpoints: splitOutpoints,
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const failure = ({ message: error }) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_FAILED,
|
||||||
|
data: {
|
||||||
|
error,
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.call('file', 'list_blocked', {
|
||||||
|
auth_token: '',
|
||||||
|
}).then(success, failure);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doBlackListedOutpointsSubscribe() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch(doFetchBlackListedOutpoints());
|
||||||
|
setInterval(() => dispatch(doFetchBlackListedOutpoints()), CHECK_BLACK_LISTED_CONTENT_INTERVAL);
|
||||||
|
};
|
||||||
|
}
|
35
extras/lbryinc/redux/actions/cost_info.js
Normal file
35
extras/lbryinc/redux/actions/cost_info.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import { selectClaimsByUri } from 'redux/selectors/claims';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export function doFetchCostInfoForUri(uri) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const claim = selectClaimsByUri(state)[uri];
|
||||||
|
|
||||||
|
if (!claim) return;
|
||||||
|
|
||||||
|
function resolve(costInfo) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_COST_INFO_COMPLETED,
|
||||||
|
data: {
|
||||||
|
uri,
|
||||||
|
costInfo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fee = claim.value ? claim.value.fee : undefined;
|
||||||
|
|
||||||
|
if (fee === undefined) {
|
||||||
|
resolve({ cost: 0, includesData: true });
|
||||||
|
} else if (fee.currency === 'LBC') {
|
||||||
|
resolve({ cost: fee.amount, includesData: true });
|
||||||
|
} else {
|
||||||
|
Lbryio.getExchangeRates().then(({ LBC_USD }) => {
|
||||||
|
resolve({ cost: fee.amount / LBC_USD, includesData: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
47
extras/lbryinc/redux/actions/filtered.js
Normal file
47
extras/lbryinc/redux/actions/filtered.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const CHECK_FILTERED_CONTENT_INTERVAL = 60 * 60 * 1000;
|
||||||
|
|
||||||
|
export function doFetchFilteredOutpoints() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILTERED_CONTENT_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const success = ({ outpoints }) => {
|
||||||
|
let formattedOutpoints = [];
|
||||||
|
if (outpoints) {
|
||||||
|
formattedOutpoints = outpoints.map(outpoint => {
|
||||||
|
const [txid, nout] = outpoint.split(':');
|
||||||
|
return { txid, nout: Number.parseInt(nout, 10) };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILTERED_CONTENT_COMPLETED,
|
||||||
|
data: {
|
||||||
|
outpoints: formattedOutpoints,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const failure = ({ error }) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILTERED_CONTENT_FAILED,
|
||||||
|
data: {
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.call('file', 'list_filtered', { auth_token: '' }).then(success, failure);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doFilteredOutpointsSubscribe() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch(doFetchFilteredOutpoints());
|
||||||
|
setInterval(() => dispatch(doFetchFilteredOutpoints()), CHECK_FILTERED_CONTENT_INTERVAL);
|
||||||
|
};
|
||||||
|
}
|
79
extras/lbryinc/redux/actions/homepage.js
Normal file
79
extras/lbryinc/redux/actions/homepage.js
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import { batchActions } from 'util/batch-actions';
|
||||||
|
import { doResolveUris } from 'util/lbryURI';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
export function doFetchFeaturedUris(offloadResolve = false) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FEATURED_CONTENT_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const success = ({ Uris }) => {
|
||||||
|
let urisToResolve = [];
|
||||||
|
Object.keys(Uris).forEach(category => {
|
||||||
|
urisToResolve = [...urisToResolve, ...Uris[category]];
|
||||||
|
});
|
||||||
|
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
type: ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED,
|
||||||
|
data: {
|
||||||
|
uris: Uris,
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (urisToResolve.length && !offloadResolve) {
|
||||||
|
actions.push(doResolveUris(urisToResolve));
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(batchActions(...actions));
|
||||||
|
};
|
||||||
|
|
||||||
|
const failure = () => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED,
|
||||||
|
data: {
|
||||||
|
uris: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.call('file', 'list_homepage').then(success, failure);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doFetchTrendingUris() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_TRENDING_CONTENT_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const success = data => {
|
||||||
|
const urisToResolve = data.map(uri => uri.url);
|
||||||
|
const actions = [
|
||||||
|
doResolveUris(urisToResolve),
|
||||||
|
{
|
||||||
|
type: ACTIONS.FETCH_TRENDING_CONTENT_COMPLETED,
|
||||||
|
data: {
|
||||||
|
uris: data,
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
dispatch(batchActions(...actions));
|
||||||
|
};
|
||||||
|
|
||||||
|
const failure = () => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_TRENDING_CONTENT_COMPLETED,
|
||||||
|
data: {
|
||||||
|
uris: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.call('file', 'list_trending').then(success, failure);
|
||||||
|
};
|
||||||
|
}
|
32
extras/lbryinc/redux/actions/stats.js
Normal file
32
extras/lbryinc/redux/actions/stats.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// @flow
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
export const doFetchViewCount = (claimIdCsv: string) => (dispatch: Dispatch) => {
|
||||||
|
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_STARTED });
|
||||||
|
|
||||||
|
return Lbryio.call('file', 'view_count', { claim_id: claimIdCsv })
|
||||||
|
.then((result: Array<number>) => {
|
||||||
|
const viewCounts = result;
|
||||||
|
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_COMPLETED, data: { claimIdCsv, viewCounts } });
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_FAILED, data: error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doFetchSubCount = (claimId: string) => (dispatch: Dispatch) => {
|
||||||
|
dispatch({ type: ACTIONS.FETCH_SUB_COUNT_STARTED });
|
||||||
|
|
||||||
|
return Lbryio.call('subscription', 'sub_count', { claim_id: claimId })
|
||||||
|
.then((result: Array<number>) => {
|
||||||
|
const subCount = result[0];
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_SUB_COUNT_COMPLETED,
|
||||||
|
data: { claimId, subCount },
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
dispatch({ type: ACTIONS.FETCH_SUB_COUNT_FAILED, data: error });
|
||||||
|
});
|
||||||
|
};
|
289
extras/lbryinc/redux/actions/sync.js
Normal file
289
extras/lbryinc/redux/actions/sync.js
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import Lbry from 'lbry';
|
||||||
|
import { doWalletEncrypt, doWalletDecrypt } from 'redux/actions/wallet';
|
||||||
|
|
||||||
|
const NO_WALLET_ERROR = 'no wallet found for this user';
|
||||||
|
|
||||||
|
export function doSetDefaultAccount(success, failure) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_DEFAULT_ACCOUNT,
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbry.account_list()
|
||||||
|
.then(accountList => {
|
||||||
|
const { lbc_mainnet: accounts } = accountList;
|
||||||
|
let defaultId;
|
||||||
|
for (let i = 0; i < accounts.length; ++i) {
|
||||||
|
if (accounts[i].satoshis > 0) {
|
||||||
|
defaultId = accounts[i].id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In a case where there's no balance on either account
|
||||||
|
// assume the second (which is created after sync) as default
|
||||||
|
if (!defaultId && accounts.length > 1) {
|
||||||
|
defaultId = accounts[1].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default account
|
||||||
|
if (defaultId) {
|
||||||
|
Lbry.account_set({ account_id: defaultId, default: true })
|
||||||
|
.then(() => {
|
||||||
|
if (success) {
|
||||||
|
success();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (failure) {
|
||||||
|
failure(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (failure) {
|
||||||
|
// no default account to set
|
||||||
|
failure('Could not set a default account'); // fail
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (failure) {
|
||||||
|
failure(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doSetSync(oldHash, newHash, data) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_SYNC_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
|
||||||
|
.then(response => {
|
||||||
|
if (!response.hash) {
|
||||||
|
throw Error('No hash returned for sync/set.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return dispatch({
|
||||||
|
type: ACTIONS.SET_SYNC_COMPLETED,
|
||||||
|
data: { syncHash: response.hash },
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_SYNC_FAILED,
|
||||||
|
data: { error },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doGetSync(passedPassword, callback) {
|
||||||
|
const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
|
||||||
|
|
||||||
|
function handleCallback(error, hasNewData) {
|
||||||
|
if (callback) {
|
||||||
|
if (typeof callback !== 'function') {
|
||||||
|
throw new Error('Second argument passed to "doGetSync" must be a function');
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(error, hasNewData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = {};
|
||||||
|
|
||||||
|
Lbry.wallet_status()
|
||||||
|
.then(status => {
|
||||||
|
if (status.is_locked) {
|
||||||
|
return Lbry.wallet_unlock({ password });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wallet is already unlocked
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.then(isUnlocked => {
|
||||||
|
if (isUnlocked) {
|
||||||
|
return Lbry.sync_hash();
|
||||||
|
}
|
||||||
|
data.unlockFailed = true;
|
||||||
|
throw new Error();
|
||||||
|
})
|
||||||
|
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||||
|
.then(response => {
|
||||||
|
const syncHash = response.hash;
|
||||||
|
data.syncHash = syncHash;
|
||||||
|
data.syncData = response.data;
|
||||||
|
data.changed = response.changed;
|
||||||
|
data.hasSyncedWallet = true;
|
||||||
|
|
||||||
|
if (response.changed) {
|
||||||
|
return Lbry.sync_apply({ password, data: response.data, blocking: true });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response) {
|
||||||
|
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||||
|
handleCallback(null, data.changed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { hash: walletHash, data: walletData } = response;
|
||||||
|
|
||||||
|
if (walletHash !== data.syncHash) {
|
||||||
|
// different local hash, need to synchronise
|
||||||
|
dispatch(doSetSync(data.syncHash, walletHash, walletData));
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||||
|
handleCallback(null, data.changed);
|
||||||
|
})
|
||||||
|
.catch(syncAttemptError => {
|
||||||
|
if (data.unlockFailed) {
|
||||||
|
dispatch({ type: ACTIONS.GET_SYNC_FAILED, data: { error: syncAttemptError } });
|
||||||
|
|
||||||
|
if (password !== '') {
|
||||||
|
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCallback(syncAttemptError);
|
||||||
|
} else if (data.hasSyncedWallet) {
|
||||||
|
const error =
|
||||||
|
(syncAttemptError && syncAttemptError.message) || 'Error getting synced wallet';
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_FAILED,
|
||||||
|
data: {
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Temp solution until we have a bad password error code
|
||||||
|
// Don't fail on blank passwords so we don't show a "password error" message
|
||||||
|
// before users have ever entered a password
|
||||||
|
if (password !== '') {
|
||||||
|
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCallback(error);
|
||||||
|
} else {
|
||||||
|
// user doesn't have a synced wallet
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_COMPLETED,
|
||||||
|
data: { hasSyncedWallet: false, syncHash: null },
|
||||||
|
});
|
||||||
|
|
||||||
|
// call sync_apply to get data to sync
|
||||||
|
// first time sync. use any string for old hash
|
||||||
|
if (syncAttemptError.message === NO_WALLET_ERROR) {
|
||||||
|
Lbry.sync_apply({ password })
|
||||||
|
.then(({ hash: walletHash, data: syncApplyData }) => {
|
||||||
|
dispatch(doSetSync('', walletHash, syncApplyData, password));
|
||||||
|
handleCallback();
|
||||||
|
})
|
||||||
|
.catch(syncApplyError => {
|
||||||
|
handleCallback(syncApplyError);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doSyncApply(syncHash, syncData, password) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SYNC_APPLY_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbry.sync_apply({ password, data: syncData })
|
||||||
|
.then(({ hash: walletHash, data: walletData }) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SYNC_APPLY_COMPLETED,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (walletHash !== syncHash) {
|
||||||
|
// different local hash, need to synchronise
|
||||||
|
dispatch(doSetSync(syncHash, walletHash, walletData));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SYNC_APPLY_FAILED,
|
||||||
|
data: {
|
||||||
|
error:
|
||||||
|
'Invalid password specified. Please enter the password for your previously synchronised wallet.',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doCheckSync() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbry.sync_hash().then(hash => {
|
||||||
|
Lbryio.call('sync', 'get', { hash }, 'post')
|
||||||
|
.then(response => {
|
||||||
|
const data = {
|
||||||
|
hasSyncedWallet: true,
|
||||||
|
syncHash: response.hash,
|
||||||
|
syncData: response.data,
|
||||||
|
hashChanged: response.changed,
|
||||||
|
};
|
||||||
|
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// user doesn't have a synced wallet
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_COMPLETED,
|
||||||
|
data: { hasSyncedWallet: false, syncHash: null },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doResetSync() {
|
||||||
|
return dispatch =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
dispatch({ type: ACTIONS.SYNC_RESET });
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doSyncEncryptAndDecrypt(oldPassword, newPassword, encrypt) {
|
||||||
|
return dispatch => {
|
||||||
|
const data = {};
|
||||||
|
return Lbry.sync_hash()
|
||||||
|
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||||
|
.then(syncGetResponse => {
|
||||||
|
data.oldHash = syncGetResponse.hash;
|
||||||
|
|
||||||
|
return Lbry.sync_apply({ password: oldPassword, data: syncGetResponse.data });
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
if (encrypt) {
|
||||||
|
dispatch(doWalletEncrypt(newPassword));
|
||||||
|
} else {
|
||||||
|
dispatch(doWalletDecrypt());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => Lbry.sync_apply({ password: newPassword }))
|
||||||
|
.then(syncApplyResponse => {
|
||||||
|
if (syncApplyResponse.hash !== data.oldHash) {
|
||||||
|
return dispatch(doSetSync(data.oldHash, syncApplyResponse.hash, syncApplyResponse.data));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
};
|
||||||
|
}
|
12
extras/lbryinc/redux/actions/web.js
Normal file
12
extras/lbryinc/redux/actions/web.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// @flow
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
export const doUpdateUploadProgress = (
|
||||||
|
progress: string,
|
||||||
|
params: { [key: string]: any },
|
||||||
|
xhr: any
|
||||||
|
) => (dispatch: Dispatch) =>
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.UPDATE_UPLOAD_PROGRESS,
|
||||||
|
data: { progress, params, xhr },
|
||||||
|
});
|
29
extras/lbryinc/redux/reducers/auth.js
Normal file
29
extras/lbryinc/redux/reducers/auth.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const reducers = {};
|
||||||
|
const defaultState = {
|
||||||
|
authenticating: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.GENERATE_AUTH_TOKEN_FAILURE] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
authToken: null,
|
||||||
|
authenticating: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.GENERATE_AUTH_TOKEN_STARTED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
authenticating: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
authToken: action.data.authToken,
|
||||||
|
authenticating: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function authReducer(state = defaultState, action) {
|
||||||
|
const handler = reducers[action.type];
|
||||||
|
if (handler) return handler(state, action);
|
||||||
|
return state;
|
||||||
|
}
|
37
extras/lbryinc/redux/reducers/blacklist.js
Normal file
37
extras/lbryinc/redux/reducers/blacklist.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
fetchingBlackListedOutpoints: false,
|
||||||
|
fetchingBlackListedOutpointsSucceed: undefined,
|
||||||
|
blackListedOutpoints: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const blacklistReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_STARTED]: state => ({
|
||||||
|
...state,
|
||||||
|
fetchingBlackListedOutpoints: true,
|
||||||
|
}),
|
||||||
|
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_COMPLETED]: (state, action) => {
|
||||||
|
const { outpoints, success } = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingBlackListedOutpoints: false,
|
||||||
|
fetchingBlackListedOutpointsSucceed: success,
|
||||||
|
blackListedOutpoints: outpoints,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_FAILED]: (state, action) => {
|
||||||
|
const { error, success } = action.data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingBlackListedOutpoints: false,
|
||||||
|
fetchingBlackListedOutpointsSucceed: success,
|
||||||
|
fetchingBlackListedOutpointsError: error,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
38
extras/lbryinc/redux/reducers/cost_info.js
Normal file
38
extras/lbryinc/redux/reducers/cost_info.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
fetching: {},
|
||||||
|
byUri: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const costInfoReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.FETCH_COST_INFO_STARTED]: (state, action) => {
|
||||||
|
const { uri } = action.data;
|
||||||
|
const newFetching = Object.assign({}, state.fetching);
|
||||||
|
newFetching[uri] = true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetching: newFetching,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.FETCH_COST_INFO_COMPLETED]: (state, action) => {
|
||||||
|
const { uri, costInfo } = action.data;
|
||||||
|
const newByUri = Object.assign({}, state.byUri);
|
||||||
|
const newFetching = Object.assign({}, state.fetching);
|
||||||
|
|
||||||
|
newByUri[uri] = costInfo;
|
||||||
|
delete newFetching[uri];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
byUri: newByUri,
|
||||||
|
fetching: newFetching,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
34
extras/lbryinc/redux/reducers/filtered.js
Normal file
34
extras/lbryinc/redux/reducers/filtered.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
loading: false,
|
||||||
|
filteredOutpoints: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filteredReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.FETCH_FILTERED_CONTENT_STARTED]: state => ({
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
}),
|
||||||
|
[ACTIONS.FETCH_FILTERED_CONTENT_COMPLETED]: (state, action) => {
|
||||||
|
const { outpoints } = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
filteredOutpoints: outpoints,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[ACTIONS.FETCH_FILTERED_CONTENT_FAILED]: (state, action) => {
|
||||||
|
const { error } = action.data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
fetchingFilteredOutpointsError: error,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
48
extras/lbryinc/redux/reducers/homepage.js
Normal file
48
extras/lbryinc/redux/reducers/homepage.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
fetchingFeaturedContent: false,
|
||||||
|
fetchingFeaturedContentFailed: false,
|
||||||
|
featuredUris: undefined,
|
||||||
|
fetchingTrendingContent: false,
|
||||||
|
fetchingTrendingContentFailed: false,
|
||||||
|
trendingUris: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const homepageReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.FETCH_FEATURED_CONTENT_STARTED]: state => ({
|
||||||
|
...state,
|
||||||
|
fetchingFeaturedContent: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
[ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED]: (state, action) => {
|
||||||
|
const { uris, success } = action.data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingFeaturedContent: false,
|
||||||
|
fetchingFeaturedContentFailed: !success,
|
||||||
|
featuredUris: uris,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.FETCH_TRENDING_CONTENT_STARTED]: state => ({
|
||||||
|
...state,
|
||||||
|
fetchingTrendingContent: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
[ACTIONS.FETCH_TRENDING_CONTENT_COMPLETED]: (state, action) => {
|
||||||
|
const { uris, success } = action.data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingTrendingContent: false,
|
||||||
|
fetchingTrendingContentFailed: !success,
|
||||||
|
trendingUris: uris,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
55
extras/lbryinc/redux/reducers/stats.js
Normal file
55
extras/lbryinc/redux/reducers/stats.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
fetchingViewCount: false,
|
||||||
|
viewCountError: undefined,
|
||||||
|
viewCountById: {},
|
||||||
|
fetchingSubCount: false,
|
||||||
|
subCountError: undefined,
|
||||||
|
subCountById: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const statsReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.FETCH_VIEW_COUNT_STARTED]: state => ({ ...state, fetchingViewCount: true }),
|
||||||
|
[ACTIONS.FETCH_VIEW_COUNT_FAILED]: (state, action) => ({
|
||||||
|
...state,
|
||||||
|
viewCountError: action.data,
|
||||||
|
}),
|
||||||
|
[ACTIONS.FETCH_VIEW_COUNT_COMPLETED]: (state, action) => {
|
||||||
|
const { claimIdCsv, viewCounts } = action.data;
|
||||||
|
|
||||||
|
const viewCountById = Object.assign({}, state.viewCountById);
|
||||||
|
const claimIds = claimIdCsv.split(',');
|
||||||
|
|
||||||
|
if (claimIds.length === viewCounts.length) {
|
||||||
|
claimIds.forEach((claimId, index) => {
|
||||||
|
viewCountById[claimId] = viewCounts[index];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingViewCount: false,
|
||||||
|
viewCountById,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[ACTIONS.FETCH_SUB_COUNT_STARTED]: state => ({ ...state, fetchingSubCount: true }),
|
||||||
|
[ACTIONS.FETCH_SUB_COUNT_FAILED]: (state, action) => ({
|
||||||
|
...state,
|
||||||
|
subCountError: action.data,
|
||||||
|
}),
|
||||||
|
[ACTIONS.FETCH_SUB_COUNT_COMPLETED]: (state, action) => {
|
||||||
|
const { claimId, subCount } = action.data;
|
||||||
|
|
||||||
|
const subCountById = { ...state.subCountById, [claimId]: subCount };
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingSubCount: false,
|
||||||
|
subCountById,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
89
extras/lbryinc/redux/reducers/sync.js
Normal file
89
extras/lbryinc/redux/reducers/sync.js
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const reducers = {};
|
||||||
|
const defaultState = {
|
||||||
|
hasSyncedWallet: false,
|
||||||
|
syncHash: null,
|
||||||
|
syncData: null,
|
||||||
|
setSyncErrorMessage: null,
|
||||||
|
getSyncErrorMessage: null,
|
||||||
|
syncApplyErrorMessage: '',
|
||||||
|
syncApplyIsPending: false,
|
||||||
|
syncApplyPasswordError: false,
|
||||||
|
getSyncIsPending: false,
|
||||||
|
setSyncIsPending: false,
|
||||||
|
hashChanged: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.GET_SYNC_STARTED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
getSyncIsPending: true,
|
||||||
|
getSyncErrorMessage: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.GET_SYNC_COMPLETED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncHash: action.data.syncHash,
|
||||||
|
syncData: action.data.syncData,
|
||||||
|
hasSyncedWallet: action.data.hasSyncedWallet,
|
||||||
|
getSyncIsPending: false,
|
||||||
|
hashChanged: action.data.hashChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.GET_SYNC_FAILED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
getSyncIsPending: false,
|
||||||
|
getSyncErrorMessage: action.data.error,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SET_SYNC_STARTED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
setSyncIsPending: true,
|
||||||
|
setSyncErrorMessage: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SET_SYNC_FAILED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
setSyncIsPending: false,
|
||||||
|
setSyncErrorMessage: action.data.error,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SET_SYNC_COMPLETED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
setSyncIsPending: false,
|
||||||
|
setSyncErrorMessage: null,
|
||||||
|
hasSyncedWallet: true, // sync was successful, so the user has a synced wallet at this point
|
||||||
|
syncHash: action.data.syncHash,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_APPLY_STARTED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncApplyPasswordError: false,
|
||||||
|
syncApplyIsPending: true,
|
||||||
|
syncApplyErrorMessage: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_APPLY_COMPLETED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncApplyIsPending: false,
|
||||||
|
syncApplyErrorMessage: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_APPLY_FAILED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncApplyIsPending: false,
|
||||||
|
syncApplyErrorMessage: action.data.error,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_APPLY_BAD_PASSWORD] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncApplyPasswordError: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
|
||||||
|
|
||||||
|
export function syncReducer(state = defaultState, action) {
|
||||||
|
const handler = reducers[action.type];
|
||||||
|
if (handler) return handler(state, action);
|
||||||
|
return state;
|
||||||
|
}
|
62
extras/lbryinc/redux/reducers/web.js
Normal file
62
extras/lbryinc/redux/reducers/web.js
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// @flow
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
/*
|
||||||
|
test mock:
|
||||||
|
currentUploads: {
|
||||||
|
'test#upload': {
|
||||||
|
progress: 50,
|
||||||
|
params: {
|
||||||
|
name: 'steve',
|
||||||
|
thumbnail_url: 'https://dev2.spee.ch/4/KMNtoSZ009fawGz59VG8PrID.jpeg',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type Params = {
|
||||||
|
channel?: string,
|
||||||
|
name: string,
|
||||||
|
thumbnail_url: ?string,
|
||||||
|
title: ?string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UploadItem = {
|
||||||
|
progess: string,
|
||||||
|
params: Params,
|
||||||
|
xhr?: any,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TvState = {
|
||||||
|
currentUploads: { [key: string]: UploadItem },
|
||||||
|
};
|
||||||
|
|
||||||
|
const reducers = {};
|
||||||
|
|
||||||
|
const defaultState: TvState = {
|
||||||
|
currentUploads: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.UPDATE_UPLOAD_PROGRESS] = (state: TvState, action) => {
|
||||||
|
const { progress, params, xhr } = action.data;
|
||||||
|
const key = params.channel ? `${params.name}#${params.channel}` : `${params.name}#anonymous`;
|
||||||
|
let currentUploads;
|
||||||
|
if (!progress) {
|
||||||
|
currentUploads = Object.assign({}, state.currentUploads);
|
||||||
|
Object.keys(currentUploads).forEach(k => {
|
||||||
|
if (k === key) {
|
||||||
|
delete currentUploads[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
currentUploads = Object.assign({}, state.currentUploads);
|
||||||
|
currentUploads[key] = { progress, params, xhr };
|
||||||
|
}
|
||||||
|
return { ...state, currentUploads };
|
||||||
|
};
|
||||||
|
|
||||||
|
export function webReducer(state: TvState = defaultState, action: any) {
|
||||||
|
const handler = reducers[action.type];
|
||||||
|
if (handler) return handler(state, action);
|
||||||
|
return state;
|
||||||
|
}
|
7
extras/lbryinc/redux/selectors/auth.js
Normal file
7
extras/lbryinc/redux/selectors/auth.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const selectState = state => state.auth || {};
|
||||||
|
|
||||||
|
export const selectAuthToken = createSelector(selectState, state => state.authToken);
|
||||||
|
|
||||||
|
export const selectIsAuthenticating = createSelector(selectState, state => state.authenticating);
|
20
extras/lbryinc/redux/selectors/blacklist.js
Normal file
20
extras/lbryinc/redux/selectors/blacklist.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
export const selectState = state => state.blacklist || {};
|
||||||
|
|
||||||
|
export const selectBlackListedOutpoints = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.blackListedOutpoints
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectBlacklistedOutpointMap = createSelector(
|
||||||
|
selectBlackListedOutpoints,
|
||||||
|
outpoints =>
|
||||||
|
outpoints
|
||||||
|
? outpoints.reduce((acc, val) => {
|
||||||
|
const outpoint = `${val.txid}:${val.nout}`;
|
||||||
|
acc[outpoint] = 1;
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
: {}
|
||||||
|
);
|
13
extras/lbryinc/redux/selectors/cost_info.js
Normal file
13
extras/lbryinc/redux/selectors/cost_info.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
export const selectState = state => state.costInfo || {};
|
||||||
|
|
||||||
|
export const selectAllCostInfoByUri = createSelector(selectState, state => state.byUri || {});
|
||||||
|
|
||||||
|
export const makeSelectCostInfoForUri = uri =>
|
||||||
|
createSelector(selectAllCostInfoByUri, costInfos => costInfos && costInfos[uri]);
|
||||||
|
|
||||||
|
export const selectFetchingCostInfo = createSelector(selectState, state => state.fetching || {});
|
||||||
|
|
||||||
|
export const makeSelectFetchingCostInfoForUri = uri =>
|
||||||
|
createSelector(selectFetchingCostInfo, fetchingByUri => fetchingByUri && fetchingByUri[uri]);
|
20
extras/lbryinc/redux/selectors/filtered.js
Normal file
20
extras/lbryinc/redux/selectors/filtered.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
export const selectState = state => state.filtered || {};
|
||||||
|
|
||||||
|
export const selectFilteredOutpoints = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.filteredOutpoints
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectFilteredOutpointMap = createSelector(
|
||||||
|
selectFilteredOutpoints,
|
||||||
|
outpoints =>
|
||||||
|
outpoints
|
||||||
|
? outpoints.reduce((acc, val) => {
|
||||||
|
const outpoint = `${val.txid}:${val.nout}`;
|
||||||
|
acc[outpoint] = 1;
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
: {}
|
||||||
|
);
|
17
extras/lbryinc/redux/selectors/homepage.js
Normal file
17
extras/lbryinc/redux/selectors/homepage.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const selectState = state => state.homepage || {};
|
||||||
|
|
||||||
|
export const selectFeaturedUris = createSelector(selectState, state => state.featuredUris);
|
||||||
|
|
||||||
|
export const selectFetchingFeaturedUris = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.fetchingFeaturedContent
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectTrendingUris = createSelector(selectState, state => state.trendingUris);
|
||||||
|
|
||||||
|
export const selectFetchingTrendingUris = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.fetchingTrendingContent
|
||||||
|
);
|
20
extras/lbryinc/redux/selectors/stats.js
Normal file
20
extras/lbryinc/redux/selectors/stats.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
|
|
||||||
|
const selectState = state => state.stats || {};
|
||||||
|
export const selectViewCount = createSelector(selectState, state => state.viewCountById);
|
||||||
|
export const selectSubCount = createSelector(selectState, state => state.subCountById);
|
||||||
|
|
||||||
|
export const makeSelectViewCountForUri = uri =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectClaimForUri(uri),
|
||||||
|
selectViewCount,
|
||||||
|
(claim, viewCountById) => (claim ? viewCountById[claim.claim_id] || 0 : 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectSubCountForUri = uri =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectClaimForUri(uri),
|
||||||
|
selectSubCount,
|
||||||
|
(claim, subCountById) => (claim ? subCountById[claim.claim_id] || 0 : 0)
|
||||||
|
);
|
40
extras/lbryinc/redux/selectors/sync.js
Normal file
40
extras/lbryinc/redux/selectors/sync.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const selectState = state => state.sync || {};
|
||||||
|
|
||||||
|
export const selectHasSyncedWallet = createSelector(selectState, state => state.hasSyncedWallet);
|
||||||
|
|
||||||
|
export const selectSyncHash = createSelector(selectState, state => state.syncHash);
|
||||||
|
|
||||||
|
export const selectSyncData = createSelector(selectState, state => state.syncData);
|
||||||
|
|
||||||
|
export const selectSetSyncErrorMessage = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.setSyncErrorMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectGetSyncErrorMessage = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.getSyncErrorMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectGetSyncIsPending = createSelector(selectState, state => state.getSyncIsPending);
|
||||||
|
|
||||||
|
export const selectSetSyncIsPending = createSelector(selectState, state => state.setSyncIsPending);
|
||||||
|
|
||||||
|
export const selectHashChanged = createSelector(selectState, state => state.hashChanged);
|
||||||
|
|
||||||
|
export const selectSyncApplyIsPending = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.syncApplyIsPending
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectSyncApplyErrorMessage = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.syncApplyErrorMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectSyncApplyPasswordError = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.syncApplyPasswordError
|
||||||
|
);
|
10
extras/lbryinc/redux/selectors/web.js
Normal file
10
extras/lbryinc/redux/selectors/web.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const selectState = state => state.web || {};
|
||||||
|
|
||||||
|
export const selectCurrentUploads = createSelector(selectState, state => state.currentUploads);
|
||||||
|
|
||||||
|
export const selectUploadCount = createSelector(
|
||||||
|
selectCurrentUploads,
|
||||||
|
currentUploads => currentUploads && Object.keys(currentUploads).length
|
||||||
|
);
|
17
extras/lbryinc/util/redux-utils-delete-me.js
Normal file
17
extras/lbryinc/util/redux-utils-delete-me.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// util for creating reducers
|
||||||
|
// based off of redux-actions
|
||||||
|
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const handleActions = (actionMap, defaultState) => (state = defaultState, action) => {
|
||||||
|
const handler = actionMap[action.type];
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
const newState = handler(state, action);
|
||||||
|
return Object.assign({}, state, newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// just return the original state if no handler
|
||||||
|
// returning a copy here breaks redux-persist
|
||||||
|
return state;
|
||||||
|
};
|
10
extras/lbryinc/util/swap-json.js
Normal file
10
extras/lbryinc/util/swap-json.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export function swapKeyAndValue(dict) {
|
||||||
|
const ret = {};
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const key in dict) {
|
||||||
|
if (dict.hasOwnProperty(key)) {
|
||||||
|
ret[dict[key]] = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
78
extras/lbryinc/util/transifex-upload.js
Normal file
78
extras/lbryinc/util/transifex-upload.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
const apiBaseUrl = 'https://www.transifex.com/api/2/project';
|
||||||
|
const resource = 'app-strings';
|
||||||
|
|
||||||
|
export function doTransifexUpload(contents, project, token, success, fail) {
|
||||||
|
const url = `${apiBaseUrl}/${project}/resources/`;
|
||||||
|
const updateUrl = `${apiBaseUrl}/${project}/resource/${resource}/content/`;
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Basic ${Buffer.from(`api:${token}`).toString('base64')}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
accept_translations: true,
|
||||||
|
i18n_type: 'KEYVALUEJSON',
|
||||||
|
name: resource,
|
||||||
|
slug: resource,
|
||||||
|
content: contents,
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleResponse(text) {
|
||||||
|
let json;
|
||||||
|
try {
|
||||||
|
// transifex api returns Python dicts for some reason.
|
||||||
|
// Any way to get the api to return valid JSON?
|
||||||
|
json = JSON.parse(text);
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
success(json || text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(err) {
|
||||||
|
if (fail) {
|
||||||
|
fail(err.message ? err.message : 'Could not upload strings resource to Transifex');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the resource exists
|
||||||
|
fetch(updateUrl, { headers })
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(() => {
|
||||||
|
// perform an update
|
||||||
|
fetch(updateUrl, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify({ content: contents }),
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status !== 200 && response.status !== 201) {
|
||||||
|
throw new Error('failed to update transifex');
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then(handleResponse)
|
||||||
|
.catch(handleError);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// resource doesn't exist, create a fresh resource
|
||||||
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify(req),
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status !== 200 && response.status !== 201) {
|
||||||
|
throw new Error('failed to upload to transifex');
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then(handleResponse)
|
||||||
|
.catch(handleError);
|
||||||
|
});
|
||||||
|
}
|
3
extras/recsys/index.js
Normal file
3
extras/recsys/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import Recsys from './recsys';
|
||||||
|
|
||||||
|
export default Recsys;
|
|
@ -1,10 +1,12 @@
|
||||||
import { selectUser } from 'redux/selectors/user';
|
import { selectUser } from 'redux/selectors/user';
|
||||||
import { makeSelectRecommendedRecsysIdForClaimId } from 'redux/selectors/search';
|
import { makeSelectRecommendedRecsysIdForClaimId } from 'redux/selectors/search';
|
||||||
import { v4 as Uuidv4 } from 'uuid';
|
import { v4 as Uuidv4 } from 'uuid';
|
||||||
import { parseURI, SETTINGS, makeSelectClaimForUri } from 'lbry-redux';
|
import { parseURI } from 'util/lbryURI';
|
||||||
|
import * as SETTINGS from 'constants/settings';
|
||||||
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
import { selectPlayingUri, selectPrimaryUri } from 'redux/selectors/content';
|
import { selectPlayingUri, selectPrimaryUri } from 'redux/selectors/content';
|
||||||
import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
|
||||||
import { history } from './store';
|
import { history } from 'ui/store';
|
||||||
|
|
||||||
const recsysEndpoint = 'https://clickstream.odysee.com/log/video/view';
|
const recsysEndpoint = 'https://clickstream.odysee.com/log/video/view';
|
||||||
const recsysId = 'lighthouse-v0';
|
const recsysId = 'lighthouse-v0';
|
10
flow-typed/Blocklist.js
vendored
Normal file
10
flow-typed/Blocklist.js
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
declare type BlocklistState = {
|
||||||
|
blockedChannels: Array<string>
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type BlocklistAction = {
|
||||||
|
type: string,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
},
|
||||||
|
};
|
214
flow-typed/Claim.js
vendored
Normal file
214
flow-typed/Claim.js
vendored
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
declare type Claim = StreamClaim | ChannelClaim | CollectionClaim;
|
||||||
|
|
||||||
|
declare type ChannelClaim = GenericClaim & {
|
||||||
|
value: ChannelMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionClaim = GenericClaim & {
|
||||||
|
value: CollectionMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type StreamClaim = GenericClaim & {
|
||||||
|
value: StreamMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type GenericClaim = {
|
||||||
|
address: string, // address associated with tx
|
||||||
|
amount: string, // bid amount at time of tx
|
||||||
|
canonical_url: string, // URL with short id, includes channel with short id
|
||||||
|
claim_id: string, // unique claim identifier
|
||||||
|
claim_sequence: number, // not being used currently
|
||||||
|
claim_op: 'create' | 'update',
|
||||||
|
confirmations: number,
|
||||||
|
decoded_claim: boolean, // Not available currently https://github.com/lbryio/lbry/issues/2044
|
||||||
|
timestamp?: number, // date of last transaction
|
||||||
|
height: number, // block height the tx was confirmed
|
||||||
|
is_channel_signature_valid?: boolean,
|
||||||
|
is_my_output: boolean,
|
||||||
|
name: string,
|
||||||
|
normalized_name: string, // `name` normalized via unicode NFD spec,
|
||||||
|
nout: number, // index number for an output of a tx
|
||||||
|
permanent_url: string, // name + claim_id
|
||||||
|
short_url: string, // permanent_url with short id, no channel
|
||||||
|
txid: string, // unique tx id
|
||||||
|
type: 'claim' | 'update' | 'support',
|
||||||
|
value_type: 'stream' | 'channel' | 'collection',
|
||||||
|
signing_channel?: ChannelClaim,
|
||||||
|
reposted_claim?: GenericClaim,
|
||||||
|
repost_channel_url?: string,
|
||||||
|
repost_url?: string,
|
||||||
|
repost_bid_amount?: string,
|
||||||
|
purchase_receipt?: PurchaseReceipt,
|
||||||
|
meta: {
|
||||||
|
activation_height: number,
|
||||||
|
claims_in_channel?: number,
|
||||||
|
creation_height: number,
|
||||||
|
creation_timestamp: number,
|
||||||
|
effective_amount: string,
|
||||||
|
expiration_height: number,
|
||||||
|
is_controlling: boolean,
|
||||||
|
support_amount: string,
|
||||||
|
reposted: number,
|
||||||
|
trending_global: number,
|
||||||
|
trending_group: number,
|
||||||
|
trending_local: number,
|
||||||
|
trending_mixed: number,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type GenericMetadata = {
|
||||||
|
title?: string,
|
||||||
|
description?: string,
|
||||||
|
thumbnail?: {
|
||||||
|
url?: string,
|
||||||
|
},
|
||||||
|
languages?: Array<string>,
|
||||||
|
tags?: Array<string>,
|
||||||
|
locations?: Array<Location>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelMetadata = GenericMetadata & {
|
||||||
|
public_key: string,
|
||||||
|
public_key_id: string,
|
||||||
|
cover_url?: string,
|
||||||
|
email?: string,
|
||||||
|
website_url?: string,
|
||||||
|
featured?: Array<string>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionMetadata = GenericMetadata & {
|
||||||
|
claims: Array<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type StreamMetadata = GenericMetadata & {
|
||||||
|
license?: string, // License "title" ex: Creative Commons, Custom copyright
|
||||||
|
license_url?: string, // Link to full license
|
||||||
|
release_time?: number, // linux timestamp
|
||||||
|
author?: string,
|
||||||
|
|
||||||
|
source: {
|
||||||
|
sd_hash: string,
|
||||||
|
media_type?: string,
|
||||||
|
hash?: string,
|
||||||
|
name?: string, // file name
|
||||||
|
size?: number, // size of file in bytes
|
||||||
|
},
|
||||||
|
|
||||||
|
// Only exists if a stream has a fee
|
||||||
|
fee?: Fee,
|
||||||
|
|
||||||
|
stream_type: 'video' | 'audio' | 'image' | 'software',
|
||||||
|
// Below correspond to `stream_type`
|
||||||
|
video?: {
|
||||||
|
duration: number,
|
||||||
|
height: number,
|
||||||
|
width: number,
|
||||||
|
},
|
||||||
|
audio?: {
|
||||||
|
duration: number,
|
||||||
|
},
|
||||||
|
image?: {
|
||||||
|
height: number,
|
||||||
|
width: number,
|
||||||
|
},
|
||||||
|
software?: {
|
||||||
|
os: string,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Location = {
|
||||||
|
latitude?: number,
|
||||||
|
longitude?: number,
|
||||||
|
country?: string,
|
||||||
|
state?: string,
|
||||||
|
city?: string,
|
||||||
|
code?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Fee = {
|
||||||
|
amount: string,
|
||||||
|
currency: string,
|
||||||
|
address: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseReceipt = {
|
||||||
|
address: string,
|
||||||
|
amount: string,
|
||||||
|
claim_id: string,
|
||||||
|
confirmations: number,
|
||||||
|
height: number,
|
||||||
|
nout: number,
|
||||||
|
timestamp: number,
|
||||||
|
txid: string,
|
||||||
|
type: 'purchase',
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ClaimActionResolveInfo = {
|
||||||
|
[string]: {
|
||||||
|
stream: ?StreamClaim,
|
||||||
|
channel: ?ChannelClaim,
|
||||||
|
claimsInChannel: ?number,
|
||||||
|
collection: ?CollectionClaim,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ChannelUpdateParams = {
|
||||||
|
claim_id: string,
|
||||||
|
bid?: string,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
languages?: Array<string>,
|
||||||
|
locations?: Array<string>,
|
||||||
|
blocking?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ChannelPublishParams = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionUpdateParams = {
|
||||||
|
claim_id: string,
|
||||||
|
claim_ids?: Array<string>,
|
||||||
|
bid?: string,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
languages?: Array<string>,
|
||||||
|
locations?: Array<string>,
|
||||||
|
blocking?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionPublishParams = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
claim_ids: Array<string>,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
}
|
29
flow-typed/CoinSwap.js
vendored
Normal file
29
flow-typed/CoinSwap.js
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
declare type CoinSwapInfo = {
|
||||||
|
chargeCode: string,
|
||||||
|
coins: Array<string>,
|
||||||
|
sendAddresses: { [string]: string},
|
||||||
|
sendAmounts: { [string]: any },
|
||||||
|
lbcAmount: number,
|
||||||
|
status?: {
|
||||||
|
status: string,
|
||||||
|
receiptCurrency: string,
|
||||||
|
receiptTxid: string,
|
||||||
|
lbcTxid: string,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CoinSwapState = {
|
||||||
|
coinSwaps: Array<CoinSwapInfo>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CoinSwapAddAction = {
|
||||||
|
type: string,
|
||||||
|
data: CoinSwapInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CoinSwapRemoveAction = {
|
||||||
|
type: string,
|
||||||
|
data: {
|
||||||
|
chargeCode: string,
|
||||||
|
},
|
||||||
|
};
|
34
flow-typed/Collections.js
vendored
Normal file
34
flow-typed/Collections.js
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
declare type Collection = {
|
||||||
|
id: string,
|
||||||
|
items: Array<?string>,
|
||||||
|
name: string,
|
||||||
|
type: string,
|
||||||
|
updatedAt: number,
|
||||||
|
totalItems?: number,
|
||||||
|
sourceId?: string, // if copied, claimId of original collection
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionState = {
|
||||||
|
unpublished: CollectionGroup,
|
||||||
|
resolved: CollectionGroup,
|
||||||
|
pending: CollectionGroup,
|
||||||
|
edited: CollectionGroup,
|
||||||
|
builtin: CollectionGroup,
|
||||||
|
saved: Array<string>,
|
||||||
|
isResolvingCollectionById: { [string]: boolean },
|
||||||
|
error?: string | null,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionGroup = {
|
||||||
|
[string]: Collection,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionEditParams = {
|
||||||
|
claims?: Array<Claim>,
|
||||||
|
remove?: boolean,
|
||||||
|
claimIds?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
order?: { from: number, to: number },
|
||||||
|
type?: string,
|
||||||
|
name?: string,
|
||||||
|
}
|
2
flow-typed/Comment.js
vendored
2
flow-typed/Comment.js
vendored
|
@ -177,7 +177,7 @@ declare type CommentCreateParams = {
|
||||||
claim_id: string,
|
claim_id: string,
|
||||||
parent_id?: string,
|
parent_id?: string,
|
||||||
signature: string,
|
signature: string,
|
||||||
signing_ts: number,
|
signing_ts: string,
|
||||||
support_tx_id?: string,
|
support_tx_id?: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
369
flow-typed/Lbry.js
vendored
Normal file
369
flow-typed/Lbry.js
vendored
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
// @flow
|
||||||
|
declare type StatusResponse = {
|
||||||
|
blob_manager: {
|
||||||
|
finished_blobs: number,
|
||||||
|
},
|
||||||
|
blockchain_headers: {
|
||||||
|
download_progress: number,
|
||||||
|
downloading_headers: boolean,
|
||||||
|
},
|
||||||
|
dht: {
|
||||||
|
node_id: string,
|
||||||
|
peers_in_routing_table: number,
|
||||||
|
},
|
||||||
|
hash_announcer: {
|
||||||
|
announce_queue_size: number,
|
||||||
|
},
|
||||||
|
installation_id: string,
|
||||||
|
is_running: boolean,
|
||||||
|
skipped_components: Array<string>,
|
||||||
|
startup_status: {
|
||||||
|
blob_manager: boolean,
|
||||||
|
blockchain_headers: boolean,
|
||||||
|
database: boolean,
|
||||||
|
dht: boolean,
|
||||||
|
exchange_rate_manager: boolean,
|
||||||
|
hash_announcer: boolean,
|
||||||
|
peer_protocol_server: boolean,
|
||||||
|
stream_manager: boolean,
|
||||||
|
upnp: boolean,
|
||||||
|
wallet: boolean,
|
||||||
|
},
|
||||||
|
stream_manager: {
|
||||||
|
managed_files: number,
|
||||||
|
},
|
||||||
|
upnp: {
|
||||||
|
aioupnp_version: string,
|
||||||
|
dht_redirect_set: boolean,
|
||||||
|
external_ip: string,
|
||||||
|
gateway: string,
|
||||||
|
peer_redirect_set: boolean,
|
||||||
|
redirects: {},
|
||||||
|
},
|
||||||
|
wallet: ?{
|
||||||
|
connected: string,
|
||||||
|
best_blockhash: string,
|
||||||
|
blocks: number,
|
||||||
|
blocks_behind: number,
|
||||||
|
is_encrypted: boolean,
|
||||||
|
is_locked: boolean,
|
||||||
|
headers_synchronization_progress: number,
|
||||||
|
available_servers: number,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type VersionResponse = {
|
||||||
|
build: string,
|
||||||
|
lbrynet_version: string,
|
||||||
|
os_release: string,
|
||||||
|
os_system: string,
|
||||||
|
platform: string,
|
||||||
|
processor: string,
|
||||||
|
python_version: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type BalanceResponse = {
|
||||||
|
available: string,
|
||||||
|
reserved: string,
|
||||||
|
reserved_subtotals: ?{
|
||||||
|
claims: string,
|
||||||
|
supports: string,
|
||||||
|
tips: string,
|
||||||
|
},
|
||||||
|
total: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ResolveResponse = {
|
||||||
|
// Keys are the url(s) passed to resolve
|
||||||
|
[string]: { error?: {}, stream?: StreamClaim, channel?: ChannelClaim, collection?: CollectionClaim, claimsInChannel?: number },
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type GetResponse = FileListItem & { error?: string };
|
||||||
|
|
||||||
|
declare type GenericTxResponse = {
|
||||||
|
height: number,
|
||||||
|
hex: string,
|
||||||
|
inputs: Array<{}>,
|
||||||
|
outputs: Array<{}>,
|
||||||
|
total_fee: string,
|
||||||
|
total_input: string,
|
||||||
|
total_output: string,
|
||||||
|
txid: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PublishResponse = GenericTxResponse & {
|
||||||
|
// Only first value in outputs is a claim
|
||||||
|
// That's the only value we care about
|
||||||
|
outputs: Array<Claim>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ClaimSearchResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ClaimListResponse = {
|
||||||
|
items: Array<ChannelClaim | Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelCreateResponse = GenericTxResponse & {
|
||||||
|
outputs: Array<ChannelClaim>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelUpdateResponse = GenericTxResponse & {
|
||||||
|
outputs: Array<ChannelClaim>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentCreateResponse = Comment;
|
||||||
|
declare type CommentUpdateResponse = Comment;
|
||||||
|
|
||||||
|
declare type MyReactions = {
|
||||||
|
// Keys are the commentId
|
||||||
|
[string]: Array<string>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type OthersReactions = {
|
||||||
|
// Keys are the commentId
|
||||||
|
[string]: {
|
||||||
|
// Keys are the reaction_type, e.g. 'like'
|
||||||
|
[string]: number,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentReactListResponse = {
|
||||||
|
my_reactions: Array<MyReactions>,
|
||||||
|
others_reactions: Array<OthersReactions>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentHideResponse = {
|
||||||
|
// keyed by the CommentIds entered
|
||||||
|
[string]: { hidden: boolean },
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentPinResponse = {
|
||||||
|
// keyed by the CommentIds entered
|
||||||
|
items: Comment,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentAbandonResponse = {
|
||||||
|
// keyed by the CommentId given
|
||||||
|
abandoned: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelListResponse = {
|
||||||
|
items: Array<ChannelClaim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelSignResponse = {
|
||||||
|
signature: string,
|
||||||
|
signing_ts: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionCreateResponse = {
|
||||||
|
outputs: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionListResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionResolveResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
total_items: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionResolveOptions = {
|
||||||
|
claim_id: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionListOptions = {
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
resolve?: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type FileListResponse = {
|
||||||
|
items: Array<FileListItem>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type TxListResponse = {
|
||||||
|
items: Array<Transaction>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type SupportListResponse = {
|
||||||
|
items: Array<Support>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type BlobListResponse = { items: Array<string> };
|
||||||
|
|
||||||
|
declare type WalletListResponse = Array<{
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
}>;
|
||||||
|
|
||||||
|
declare type WalletStatusResponse = {
|
||||||
|
is_encrypted: boolean,
|
||||||
|
is_locked: boolean,
|
||||||
|
is_syncing: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type SyncApplyResponse = {
|
||||||
|
hash: string,
|
||||||
|
data: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type SupportAbandonResponse = GenericTxResponse;
|
||||||
|
|
||||||
|
declare type StreamListResponse = {
|
||||||
|
items: Array<StreamClaim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type StreamRepostOptions = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
claim_id: string,
|
||||||
|
channel_id?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type StreamRepostResponse = GenericTxResponse;
|
||||||
|
|
||||||
|
declare type PurchaseListResponse = {
|
||||||
|
items: Array<PurchaseReceipt & { claim: StreamClaim }>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseListOptions = {
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
resolve: boolean,
|
||||||
|
claim_id?: string,
|
||||||
|
channel_id?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Types used in the generic Lbry object that is exported
|
||||||
|
//
|
||||||
|
declare type LbryTypes = {
|
||||||
|
isConnected: boolean,
|
||||||
|
connectPromise: any, // null |
|
||||||
|
connect: () => any, // void | Promise<any> ?
|
||||||
|
daemonConnectionString: string,
|
||||||
|
alternateConnectionString: string,
|
||||||
|
methodsUsingAlternateConnectionString: Array<string>,
|
||||||
|
apiRequestHeaders: { [key: string]: string },
|
||||||
|
setDaemonConnectionString: string => void,
|
||||||
|
setApiHeader: (string, string) => void,
|
||||||
|
unsetApiHeader: string => void,
|
||||||
|
overrides: { [string]: ?Function },
|
||||||
|
setOverride: (string, Function) => void,
|
||||||
|
// getMediaType: (?string, ?string) => string,
|
||||||
|
|
||||||
|
// Lbry Methods
|
||||||
|
stop: () => Promise<string>,
|
||||||
|
status: () => Promise<StatusResponse>,
|
||||||
|
version: () => Promise<VersionResponse>,
|
||||||
|
resolve: (params: {}) => Promise<ResolveResponse>,
|
||||||
|
get: (params: {}) => Promise<GetResponse>,
|
||||||
|
publish: (params: {}) => Promise<PublishResponse>,
|
||||||
|
|
||||||
|
claim_search: (params: {}) => Promise<ClaimSearchResponse>,
|
||||||
|
claim_list: (params: {}) => Promise<ClaimListResponse>,
|
||||||
|
channel_create: (params: {}) => Promise<ChannelCreateResponse>,
|
||||||
|
channel_update: (params: {}) => Promise<ChannelUpdateResponse>,
|
||||||
|
channel_import: (params: {}) => Promise<string>,
|
||||||
|
channel_list: (params: {}) => Promise<ChannelListResponse>,
|
||||||
|
channel_sign: (params: {}) => Promise<ChannelSignResponse>,
|
||||||
|
stream_abandon: (params: {}) => Promise<GenericTxResponse>,
|
||||||
|
stream_list: (params: {}) => Promise<StreamListResponse>,
|
||||||
|
channel_abandon: (params: {}) => Promise<GenericTxResponse>,
|
||||||
|
support_create: (params: {}) => Promise<GenericTxResponse>,
|
||||||
|
support_list: (params: {}) => Promise<SupportListResponse>,
|
||||||
|
support_abandon: (params: {}) => Promise<SupportAbandonResponse>,
|
||||||
|
stream_repost: (params: StreamRepostOptions) => Promise<StreamRepostResponse>,
|
||||||
|
purchase_list: (params: PurchaseListOptions) => Promise<PurchaseListResponse>,
|
||||||
|
collection_resolve: (params: CollectionResolveOptions) => Promise<CollectionResolveResponse>,
|
||||||
|
collection_list: (params: CollectionListOptions) => Promise<CollectionListResponse>,
|
||||||
|
collection_create: (params: {}) => Promise<CollectionCreateResponse>,
|
||||||
|
collection_update: (params: {}) => Promise<CollectionCreateResponse>,
|
||||||
|
|
||||||
|
// File fetching and manipulation
|
||||||
|
file_list: (params: {}) => Promise<FileListResponse>,
|
||||||
|
file_delete: (params: {}) => Promise<boolean>,
|
||||||
|
blob_delete: (params: {}) => Promise<string>,
|
||||||
|
blob_list: (params: {}) => Promise<BlobListResponse>,
|
||||||
|
file_set_status: (params: {}) => Promise<any>,
|
||||||
|
file_reflect: (params: {}) => Promise<any>,
|
||||||
|
|
||||||
|
// Preferences
|
||||||
|
preference_get: (params?: {}) => Promise<any>,
|
||||||
|
preference_set: (params: {}) => Promise<any>,
|
||||||
|
|
||||||
|
// Commenting
|
||||||
|
comment_update: (params: {}) => Promise<CommentUpdateResponse>,
|
||||||
|
comment_hide: (params: {}) => Promise<CommentHideResponse>,
|
||||||
|
comment_abandon: (params: {}) => Promise<CommentAbandonResponse>,
|
||||||
|
comment_list: (params: {}) => Promise<any>,
|
||||||
|
comment_create: (params: {}) => Promise<any>,
|
||||||
|
|
||||||
|
// Wallet utilities
|
||||||
|
wallet_balance: (params: {}) => Promise<BalanceResponse>,
|
||||||
|
wallet_decrypt: (prams: {}) => Promise<boolean>,
|
||||||
|
wallet_encrypt: (params: {}) => Promise<boolean>,
|
||||||
|
wallet_unlock: (params: {}) => Promise<boolean>,
|
||||||
|
wallet_list: (params: {}) => Promise<WalletListResponse>,
|
||||||
|
wallet_send: (params: {}) => Promise<GenericTxResponse>,
|
||||||
|
wallet_status: (params?: {}) => Promise<WalletStatusResponse>,
|
||||||
|
address_is_mine: (params: {}) => Promise<boolean>,
|
||||||
|
address_unused: (params: {}) => Promise<string>, // New address
|
||||||
|
address_list: (params: {}) => Promise<string>,
|
||||||
|
transaction_list: (params: {}) => Promise<TxListResponse>,
|
||||||
|
txo_list: (params: {}) => Promise<any>,
|
||||||
|
account_set: (params: {}) => Promise<any>,
|
||||||
|
account_list: (params?: {}) => Promise<any>,
|
||||||
|
|
||||||
|
// Sync
|
||||||
|
sync_hash: (params?: {}) => Promise<string>,
|
||||||
|
sync_apply: (params: {}) => Promise<SyncApplyResponse>,
|
||||||
|
// syncGet
|
||||||
|
|
||||||
|
// The app shouldn't need to do this
|
||||||
|
utxo_release: () => Promise<any>,
|
||||||
|
};
|
99
flow-typed/LbryFirst.js
vendored
Normal file
99
flow-typed/LbryFirst.js
vendored
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// @flow
|
||||||
|
declare type LbryFirstStatusResponse = {
|
||||||
|
Version: string,
|
||||||
|
Message: string,
|
||||||
|
Running: boolean,
|
||||||
|
Commit: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type LbryFirstVersionResponse = {
|
||||||
|
build: string,
|
||||||
|
lbrynet_version: string,
|
||||||
|
os_release: string,
|
||||||
|
os_system: string,
|
||||||
|
platform: string,
|
||||||
|
processor: string,
|
||||||
|
python_version: string,
|
||||||
|
};
|
||||||
|
/* SAMPLE UPLOAD RESPONSE (FULL)
|
||||||
|
"Video": {
|
||||||
|
"etag": "\"Dn5xIderbhAnUk5TAW0qkFFir0M/xlGLrlTox7VFTRcR8F77RbKtaU4\"",
|
||||||
|
"id": "8InjtdvVmwE",
|
||||||
|
"kind": "youtube#video",
|
||||||
|
"snippet": {
|
||||||
|
"categoryId": "22",
|
||||||
|
"channelId": "UCXiVsGTU88fJjheB2rqF0rA",
|
||||||
|
"channelTitle": "Mark Beamer",
|
||||||
|
"liveBroadcastContent": "none",
|
||||||
|
"localized": {
|
||||||
|
"title": "my title"
|
||||||
|
},
|
||||||
|
"publishedAt": "2020-05-05T04:17:53.000Z",
|
||||||
|
"thumbnails": {
|
||||||
|
"default": {
|
||||||
|
"height": 90,
|
||||||
|
"url": "https://i9.ytimg.com/vi/8InjtdvVmwE/default.jpg?sqp=CMTQw_UF&rs=AOn4CLB6dlhZMSMrazDlWRsitPgCsn8fVw",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
"high": {
|
||||||
|
"height": 360,
|
||||||
|
"url": "https://i9.ytimg.com/vi/8InjtdvVmwE/hqdefault.jpg?sqp=CMTQw_UF&rs=AOn4CLB-Je_7l6qvASRAR_bSGWZHaXaJWQ",
|
||||||
|
"width": 480
|
||||||
|
},
|
||||||
|
"medium": {
|
||||||
|
"height": 180,
|
||||||
|
"url": "https://i9.ytimg.com/vi/8InjtdvVmwE/mqdefault.jpg?sqp=CMTQw_UF&rs=AOn4CLCvSnDLqVznRNMKuvJ_0misY_chPQ",
|
||||||
|
"width": 320
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "my title"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"embeddable": true,
|
||||||
|
"license": "youtube",
|
||||||
|
"privacyStatus": "private",
|
||||||
|
"publicStatsViewable": true,
|
||||||
|
"uploadStatus": "uploaded"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
declare type UploadResponse = {
|
||||||
|
Video: {
|
||||||
|
id: string,
|
||||||
|
snippet: {
|
||||||
|
channelId: string,
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
uploadStatus: string,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type HasYTAuthResponse = {
|
||||||
|
HashAuth: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type YTSignupResponse = {};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Types used in the generic LbryFirst object that is exported
|
||||||
|
//
|
||||||
|
declare type LbryFirstTypes = {
|
||||||
|
isConnected: boolean,
|
||||||
|
connectPromise: ?Promise<any>,
|
||||||
|
connect: () => void,
|
||||||
|
lbryFirstConnectionString: string,
|
||||||
|
apiRequestHeaders: { [key: string]: string },
|
||||||
|
setApiHeader: (string, string) => void,
|
||||||
|
unsetApiHeader: string => void,
|
||||||
|
overrides: { [string]: ?Function },
|
||||||
|
setOverride: (string, Function) => void,
|
||||||
|
|
||||||
|
// LbryFirst Methods
|
||||||
|
stop: () => Promise<string>,
|
||||||
|
status: () => Promise<StatusResponse>,
|
||||||
|
version: () => Promise<VersionResponse>,
|
||||||
|
upload: any => Promise<?UploadResponse>,
|
||||||
|
hasYTAuth: string => Promise<HasYTAuthResponse>,
|
||||||
|
ytSignup: () => Promise<YTSignupResponse>,
|
||||||
|
};
|
93
flow-typed/Notification.js
vendored
Normal file
93
flow-typed/Notification.js
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
// @flow
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Toasts:
|
||||||
|
- First-in, first-out queue
|
||||||
|
- Simple messages that are shown in response to user interactions
|
||||||
|
- Never saved
|
||||||
|
- If they are the result of errors, use the isError flag when creating
|
||||||
|
- For errors that should interrupt user behavior, use Error
|
||||||
|
*/
|
||||||
|
declare type ToastParams = {
|
||||||
|
message: string,
|
||||||
|
title?: string,
|
||||||
|
linkText?: string,
|
||||||
|
linkTarget?: string,
|
||||||
|
isError?: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Toast = {
|
||||||
|
id: string,
|
||||||
|
params: ToastParams,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoToast = {
|
||||||
|
type: ACTIONS.CREATE_TOAST,
|
||||||
|
data: Toast,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Notifications:
|
||||||
|
- List of notifications based on user interactions/app notifications
|
||||||
|
- Always saved, but can be manually deleted
|
||||||
|
- Can happen in the background, or because of user interaction (ex: publish confirmed)
|
||||||
|
*/
|
||||||
|
declare type Notification = {
|
||||||
|
id: string, // Unique id
|
||||||
|
dateCreated: number,
|
||||||
|
isRead: boolean, // Used to display "new" notifications that a user hasn't seen yet
|
||||||
|
source?: string, // The type/area an notification is from. Used for sorting (ex: publishes, transactions)
|
||||||
|
// We may want to use priority/isDismissed in the future to specify how urgent a notification is
|
||||||
|
// and if the user should see it immediately
|
||||||
|
// isDissmied: boolean,
|
||||||
|
// priority?: number
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoNotification = {
|
||||||
|
type: ACTIONS.CREATE_NOTIFICATION,
|
||||||
|
data: Notification,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoEditNotification = {
|
||||||
|
type: ACTIONS.EDIT_NOTIFICATION,
|
||||||
|
data: {
|
||||||
|
notification: Notification,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoDeleteNotification = {
|
||||||
|
type: ACTIONS.DELETE_NOTIFICATION,
|
||||||
|
data: {
|
||||||
|
id: string, // The id to delete
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Errors:
|
||||||
|
- First-in, first-out queue
|
||||||
|
- Errors that should interupt user behavior
|
||||||
|
- For errors that can be shown without interrupting a user, use Toast with the isError flag
|
||||||
|
*/
|
||||||
|
declare type ErrorNotification = {
|
||||||
|
title: string,
|
||||||
|
text: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoError = {
|
||||||
|
type: ACTIONS.CREATE_ERROR,
|
||||||
|
data: ErrorNotification,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoDismissError = {
|
||||||
|
type: ACTIONS.DISMISS_ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
NotificationState
|
||||||
|
*/
|
||||||
|
declare type NotificationState = {
|
||||||
|
notifications: Array<Notification>,
|
||||||
|
errors: Array<ErrorNotification>,
|
||||||
|
toasts: Array<Toast>,
|
||||||
|
};
|
27
flow-typed/Publish.js
vendored
Normal file
27
flow-typed/Publish.js
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
declare type UpdatePublishFormData = {
|
||||||
|
filePath?: string,
|
||||||
|
contentIsFree?: boolean,
|
||||||
|
fee?: {
|
||||||
|
amount: string,
|
||||||
|
currency: string,
|
||||||
|
},
|
||||||
|
title?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
uploadThumbnailStatus?: string,
|
||||||
|
thumbnailPath?: string,
|
||||||
|
description?: string,
|
||||||
|
language?: string,
|
||||||
|
channel?: string,
|
||||||
|
channelId?: string,
|
||||||
|
name?: string,
|
||||||
|
nameError?: string,
|
||||||
|
bid?: string,
|
||||||
|
bidError?: string,
|
||||||
|
otherLicenseDescription?: string,
|
||||||
|
licenseUrl?: string,
|
||||||
|
licenseType?: string,
|
||||||
|
uri?: string,
|
||||||
|
nsfw: boolean,
|
||||||
|
};
|
6
flow-typed/Redux.js
vendored
Normal file
6
flow-typed/Redux.js
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// @flow
|
||||||
|
/* eslint-disable no-use-before-define */
|
||||||
|
declare type GetState = () => any;
|
||||||
|
declare type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
|
||||||
|
declare type Dispatch = (action: {} | Promise<*> | Array<{}> | ThunkAction) => any; // Need to refer to ThunkAction
|
||||||
|
/* eslint-enable */
|
5
flow-typed/Reflector.js
vendored
Normal file
5
flow-typed/Reflector.js
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
declare type ReflectingUpdate = {
|
||||||
|
fileListItem: FileListItem,
|
||||||
|
progress: number | boolean,
|
||||||
|
stalled: boolean,
|
||||||
|
};
|
21
flow-typed/Tags.js
vendored
Normal file
21
flow-typed/Tags.js
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
declare type TagState = {
|
||||||
|
followedTags: FollowedTags,
|
||||||
|
knownTags: KnownTags,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Tag = {
|
||||||
|
name: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type KnownTags = {
|
||||||
|
[string]: Tag,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type FollowedTags = Array<string>;
|
||||||
|
|
||||||
|
declare type TagAction = {
|
||||||
|
type: string,
|
||||||
|
data: {
|
||||||
|
name: string,
|
||||||
|
},
|
||||||
|
};
|
28
flow-typed/Transaction.js
vendored
Normal file
28
flow-typed/Transaction.js
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// @flow
|
||||||
|
declare type Transaction = {
|
||||||
|
amount: number,
|
||||||
|
claim_id: string,
|
||||||
|
claim_name: string,
|
||||||
|
fee: number,
|
||||||
|
nout: number,
|
||||||
|
txid: string,
|
||||||
|
type: string,
|
||||||
|
date: Date,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Support = {
|
||||||
|
address: string,
|
||||||
|
amount: string,
|
||||||
|
claim_id: string,
|
||||||
|
confirmations: number,
|
||||||
|
height: string,
|
||||||
|
is_change: string,
|
||||||
|
is_mine: string,
|
||||||
|
name: string,
|
||||||
|
normalized_name: string,
|
||||||
|
nout: string,
|
||||||
|
permanent_url: string,
|
||||||
|
timestamp: number,
|
||||||
|
txid: string,
|
||||||
|
type: string,
|
||||||
|
};
|
27
flow-typed/Txo.js
vendored
Normal file
27
flow-typed/Txo.js
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
declare type Txo = {
|
||||||
|
amount: number,
|
||||||
|
claim_id: string,
|
||||||
|
normalized_name: string,
|
||||||
|
nout: number,
|
||||||
|
txid: string,
|
||||||
|
type: string,
|
||||||
|
value_type: string,
|
||||||
|
timestamp: number,
|
||||||
|
is_my_output: boolean,
|
||||||
|
is_my_input: boolean,
|
||||||
|
is_spent: boolean,
|
||||||
|
signing_channel?: {
|
||||||
|
channel_id: string,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type TxoListParams = {
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
type: string,
|
||||||
|
is_my_input?: boolean,
|
||||||
|
is_my_output?: boolean,
|
||||||
|
is_not_my_input?: boolean,
|
||||||
|
is_not_my_output?: boolean,
|
||||||
|
is_spent?: boolean,
|
||||||
|
};
|
2
flow-typed/i18n.js
vendored
Normal file
2
flow-typed/i18n.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// @flow
|
||||||
|
declare function __(a: string, b?: {}): string;
|
21
flow-typed/lbryURI.js
vendored
Normal file
21
flow-typed/lbryURI.js
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// @flow
|
||||||
|
declare type LbryUrlObj = {
|
||||||
|
// Path and channel will always exist when calling parseURI
|
||||||
|
// But they may not exist when code calls buildURI
|
||||||
|
isChannel?: boolean,
|
||||||
|
path?: string,
|
||||||
|
streamName?: string,
|
||||||
|
streamClaimId?: string,
|
||||||
|
channelName?: string,
|
||||||
|
channelClaimId?: string,
|
||||||
|
primaryClaimSequence?: number,
|
||||||
|
secondaryClaimSequence?: number,
|
||||||
|
primaryBidPosition?: number,
|
||||||
|
secondaryBidPosition?: number,
|
||||||
|
startTime?: number,
|
||||||
|
|
||||||
|
// Below are considered deprecated and should not be used due to unreliableness with claim.canonical_url
|
||||||
|
claimName?: string,
|
||||||
|
claimId?: string,
|
||||||
|
contentName?: string,
|
||||||
|
};
|
2
flow-typed/publish.js
vendored
2
flow-typed/publish.js
vendored
|
@ -25,7 +25,7 @@ declare type UpdatePublishFormData = {
|
||||||
licenseType?: string,
|
licenseType?: string,
|
||||||
uri?: string,
|
uri?: string,
|
||||||
nsfw: boolean,
|
nsfw: boolean,
|
||||||
isMarkdownPost: boolean,
|
isMarkdownPost?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type PublishParams = {
|
declare type PublishParams = {
|
||||||
|
|
|
@ -49,6 +49,8 @@
|
||||||
"postinstall:warning": "echo '\n\nWARNING\n\nNot all node modules were installed because NODE_ENV is set to \"production\".\nThis should only be set after installing dependencies with \"yarn\". The app will not work.\n\n'"
|
"postinstall:warning": "echo '\n\nWARNING\n\nNot all node modules were installed because NODE_ENV is set to \"production\".\nThis should only be set after installing dependencies with \"yarn\". The app will not work.\n\n'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ungap/from-entries": "^0.2.1",
|
||||||
|
"proxy-polyfill": "0.1.6",
|
||||||
"auto-launch": "^5.0.5",
|
"auto-launch": "^5.0.5",
|
||||||
"electron-dl": "^1.11.0",
|
"electron-dl": "^1.11.0",
|
||||||
"electron-log": "^2.2.12",
|
"electron-log": "^2.2.12",
|
||||||
|
@ -157,8 +159,6 @@
|
||||||
"imagesloaded": "^4.1.4",
|
"imagesloaded": "^4.1.4",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||||
"lbry-redux": "lbryio/lbry-redux#0f930c4a7bfc7f164e6b3c6044050c1bc73f6ab8",
|
|
||||||
"lbryinc": "lbryio/lbryinc#0b4e41ef90d6347819dd3453f2f9398a5c1b4f36",
|
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
"lodash-es": "^4.17.14",
|
"lodash-es": "^4.17.14",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import ChannelThumbnail from 'component/channelThumbnail';
|
import ChannelThumbnail from 'component/channelThumbnail';
|
||||||
import { parseURI } from 'lbry-redux';
|
import { parseURI } from 'util/lbryURI';
|
||||||
import ChannelBlockButton from 'component/channelBlockButton';
|
import ChannelBlockButton from 'component/channelBlockButton';
|
||||||
import ChannelMuteButton from 'component/channelMuteButton';
|
import ChannelMuteButton from 'component/channelMuteButton';
|
||||||
import SubscribeButton from 'component/subscribeButton';
|
import SubscribeButton from 'component/subscribeButton';
|
||||||
|
|
|
@ -5,13 +5,9 @@ import { selectGetSyncErrorMessage, selectSyncFatalError } from 'redux/selectors
|
||||||
import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user';
|
import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user';
|
||||||
import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import { selectUnclaimedRewards } from 'redux/selectors/rewards';
|
import { selectUnclaimedRewards } from 'redux/selectors/rewards';
|
||||||
import {
|
import { doFetchChannelListMine, doFetchCollectionListMine, doResolveUris } from 'redux/actions/claims';
|
||||||
doFetchChannelListMine,
|
import { selectMyChannelUrls } from 'redux/selectors/claims';
|
||||||
doFetchCollectionListMine,
|
import * as SETTINGS from 'constants/settings';
|
||||||
SETTINGS,
|
|
||||||
selectMyChannelUrls,
|
|
||||||
doResolveUris,
|
|
||||||
} from 'lbry-redux';
|
|
||||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import {
|
import {
|
||||||
makeSelectClientSetting,
|
makeSelectClientSetting,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import React, { useEffect, useRef, useState, useLayoutEffect } from 'react';
|
||||||
import { lazyImport } from 'util/lazyImport';
|
import { lazyImport } from 'util/lazyImport';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import { buildURI, parseURI } from 'lbry-redux';
|
import { buildURI, parseURI } from 'util/lbryURI';
|
||||||
import { SIMPLE_SITE } from 'config';
|
import { SIMPLE_SITE } from 'config';
|
||||||
import Router from 'component/router/index';
|
import Router from 'component/router/index';
|
||||||
import ReactModal from 'react-modal';
|
import ReactModal from 'react-modal';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri } from 'lbry-redux';
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import AutoplayCountdown from './view';
|
import AutoplayCountdown from './view';
|
||||||
import { selectModal } from 'redux/selectors/app';
|
import { selectModal } from 'redux/selectors/app';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectMetadataItemForUri, makeSelectClaimForUri } from 'lbry-redux';
|
import { makeSelectMetadataItemForUri, makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
import ChannelAbout from './view';
|
import ChannelAbout from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimIdForUri } from 'lbry-redux';
|
import { makeSelectClaimIdForUri } from 'redux/selectors/claims';
|
||||||
import {
|
import {
|
||||||
doCommentModUnBlock,
|
doCommentModUnBlock,
|
||||||
doCommentModBlock,
|
doCommentModBlock,
|
||||||
|
|
|
@ -6,9 +6,9 @@ import {
|
||||||
makeSelectClaimIsMine,
|
makeSelectClaimIsMine,
|
||||||
makeSelectTotalPagesInChannelSearch,
|
makeSelectTotalPagesInChannelSearch,
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
doResolveUris,
|
} from 'redux/selectors/claims';
|
||||||
SETTINGS,
|
import { doResolveUris } from 'redux/actions/claims';
|
||||||
} from 'lbry-redux';
|
import * as SETTINGS from 'constants/settings';
|
||||||
import { makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
import { makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { DISABLE_COMMENTS_TAG } from 'constants/tags';
|
import { DISABLE_COMMENTS_TAG } from 'constants/tags';
|
||||||
import ChannelDiscussion from './view';
|
import ChannelDiscussion from './view';
|
||||||
import { makeSelectTagInClaimOrChannelForUri } from 'lbry-redux';
|
import { makeSelectTagInClaimOrChannelForUri } from 'redux/selectors/claims';
|
||||||
|
|
||||||
const select = (state, props) => {
|
const select = (state, props) => {
|
||||||
const { search } = props.location;
|
const { search } = props.location;
|
||||||
|
|
|
@ -4,17 +4,15 @@ import {
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
makeSelectCoverForUri,
|
makeSelectCoverForUri,
|
||||||
makeSelectMetadataItemForUri,
|
makeSelectMetadataItemForUri,
|
||||||
doUpdateChannel,
|
|
||||||
doCreateChannel,
|
|
||||||
makeSelectAmountForUri,
|
makeSelectAmountForUri,
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
selectUpdateChannelError,
|
selectUpdateChannelError,
|
||||||
selectUpdatingChannel,
|
selectUpdatingChannel,
|
||||||
selectCreateChannelError,
|
selectCreateChannelError,
|
||||||
selectCreatingChannel,
|
selectCreatingChannel,
|
||||||
selectBalance,
|
} from 'redux/selectors/claims';
|
||||||
doClearChannelErrors,
|
import { selectBalance } from 'redux/selectors/wallet';
|
||||||
} from 'lbry-redux';
|
import { doUpdateChannel, doCreateChannel, doClearChannelErrors } from 'redux/actions/claims';
|
||||||
import { doOpenModal } from 'redux/actions/app';
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
import { doUpdateBlockListForPublishedChannel } from 'redux/actions/comments';
|
import { doUpdateBlockListForPublishedChannel } from 'redux/actions/comments';
|
||||||
import { doClaimInitialRewards } from 'redux/actions/rewards';
|
import { doClaimInitialRewards } from 'redux/actions/rewards';
|
||||||
|
|
|
@ -9,7 +9,7 @@ import TagsSearch from 'component/tagsSearch';
|
||||||
import { FF_MAX_CHARS_IN_DESCRIPTION } from 'constants/form-field';
|
import { FF_MAX_CHARS_IN_DESCRIPTION } from 'constants/form-field';
|
||||||
import ErrorText from 'component/common/error-text';
|
import ErrorText from 'component/common/error-text';
|
||||||
import ChannelThumbnail from 'component/channelThumbnail';
|
import ChannelThumbnail from 'component/channelThumbnail';
|
||||||
import { isNameValid, parseURI } from 'lbry-redux';
|
import { isNameValid, parseURI } from 'util/lbryURI';
|
||||||
import ClaimAbandonButton from 'component/claimAbandonButton';
|
import ClaimAbandonButton from 'component/claimAbandonButton';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { MINIMUM_PUBLISH_BID, INVALID_NAME_ERROR, ESTIMATED_FEE } from 'constants/claim';
|
import { MINIMUM_PUBLISH_BID, INVALID_NAME_ERROR, ESTIMATED_FEE } from 'constants/claim';
|
||||||
|
@ -253,7 +253,7 @@ function ChannelForm(props: Props) {
|
||||||
let nameError;
|
let nameError;
|
||||||
if (!name && name !== undefined) {
|
if (!name && name !== undefined) {
|
||||||
nameError = __('A name is required for your url');
|
nameError = __('A name is required for your url');
|
||||||
} else if (!isNameValid(name, false)) {
|
} else if (!isNameValid(name)) {
|
||||||
nameError = INVALID_NAME_ERROR;
|
nameError = INVALID_NAME_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri, makeSelectIsUriResolving } from 'lbry-redux';
|
import { makeSelectClaimForUri, makeSelectIsUriResolving } from 'redux/selectors/claims';
|
||||||
import ChannelMentionSuggestion from './view';
|
import ChannelMentionSuggestion from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
|
@ -2,7 +2,8 @@ import { connect } from 'react-redux';
|
||||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { doResolveUris, makeSelectClaimForUri } from 'lbry-redux';
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
|
import { doResolveUris } from 'redux/actions/claims';
|
||||||
import { makeSelectTopLevelCommentsForUri } from 'redux/selectors/comments';
|
import { makeSelectTopLevelCommentsForUri } from 'redux/selectors/comments';
|
||||||
import ChannelMentionSuggestions from './view';
|
import ChannelMentionSuggestions from './view';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { Combobox, ComboboxInput, ComboboxPopover, ComboboxList } from '@reach/combobox';
|
import { Combobox, ComboboxInput, ComboboxPopover, ComboboxList } from '@reach/combobox';
|
||||||
import { Form } from 'component/common/form';
|
import { Form } from 'component/common/form';
|
||||||
import { parseURI, regexInvalidURI } from 'lbry-redux';
|
import { parseURI, regexInvalidURI } from 'util/lbryURI';
|
||||||
import { SEARCH_OPTIONS } from 'constants/search';
|
import { SEARCH_OPTIONS } from 'constants/search';
|
||||||
import * as KEYCODES from 'constants/keycodes';
|
import * as KEYCODES from 'constants/keycodes';
|
||||||
import ChannelMentionSuggestion from 'component/channelMentionSuggestion';
|
import ChannelMentionSuggestion from 'component/channelMentionSuggestion';
|
||||||
|
@ -69,8 +69,9 @@ export default function ChannelMentionSuggestions(props: Props) {
|
||||||
const allShownCanonical = [canonicalCreator, ...canonicalSubscriptions, ...canonicalCommentors];
|
const allShownCanonical = [canonicalCreator, ...canonicalSubscriptions, ...canonicalCommentors];
|
||||||
const possibleMatches = allShownUris.filter((uri) => {
|
const possibleMatches = allShownUris.filter((uri) => {
|
||||||
try {
|
try {
|
||||||
|
// yuck a try catch in a filter?
|
||||||
const { channelName } = parseURI(uri);
|
const { channelName } = parseURI(uri);
|
||||||
return channelName.toLowerCase().includes(termToMatch);
|
return channelName && channelName.toLowerCase().includes(termToMatch);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectIsUriResolving, doResolveUri } from 'lbry-redux';
|
import { makeSelectIsUriResolving } from 'redux/selectors/claims';
|
||||||
|
import { doResolveUri } from 'redux/actions/claims';
|
||||||
import { makeSelectWinningUriForQuery } from 'redux/selectors/search';
|
import { makeSelectWinningUriForQuery } from 'redux/selectors/search';
|
||||||
import ChannelMentionTopSuggestion from './view';
|
import ChannelMentionTopSuggestion from './view';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectMyChannelClaims } from 'lbry-redux';
|
import { selectMyChannelClaims } from 'redux/selectors/claims';
|
||||||
import { selectActiveChannelClaim, selectIncognito } from 'redux/selectors/app';
|
import { selectActiveChannelClaim, selectIncognito } from 'redux/selectors/app';
|
||||||
import { doSetActiveChannel, doSetIncognito } from 'redux/actions/app';
|
import { doSetActiveChannel, doSetIncognito } from 'redux/actions/app';
|
||||||
import ChannelSelector from './view';
|
import ChannelSelector from './view';
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectStakedLevelForChannelUri,
|
makeSelectStakedLevelForChannelUri,
|
||||||
makeSelectTotalStakedAmountForChannelUri,
|
makeSelectTotalStakedAmountForChannelUri,
|
||||||
} from 'lbry-redux';
|
} from 'redux/selectors/claims';
|
||||||
import ChannelStakedIndicator from './view';
|
import ChannelStakedIndicator from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectThumbnailForUri, doResolveUri, makeSelectClaimForUri, makeSelectIsUriResolving } from 'lbry-redux';
|
import { makeSelectThumbnailForUri, makeSelectClaimForUri, makeSelectIsUriResolving } from 'redux/selectors/claims';
|
||||||
|
import { doResolveUri } from 'redux/actions/claims';
|
||||||
import ChannelThumbnail from './view';
|
import ChannelThumbnail from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { parseURI } from 'lbry-redux';
|
import { parseURI } from 'util/lbryURI';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Gerbil from './gerbil.png';
|
import Gerbil from './gerbil.png';
|
||||||
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
|
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
|
||||||
|
@ -10,7 +10,7 @@ import { AVATAR_DEFAULT } from 'config';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
thumbnail: ?string,
|
thumbnail: ?string,
|
||||||
uri: ?string,
|
uri: string,
|
||||||
className?: string,
|
className?: string,
|
||||||
thumbnailPreview: ?string,
|
thumbnailPreview: ?string,
|
||||||
obscure?: boolean,
|
obscure?: boolean,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri, makeSelectTitleForUri } from 'lbry-redux';
|
import { makeSelectClaimForUri, makeSelectTitleForUri } from 'redux/selectors/claims';
|
||||||
import ChannelTitle from './view';
|
import ChannelTitle from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
||||||
import { doOpenModal } from 'redux/actions/app';
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
import ClaimAbandonButton from './view';
|
import ClaimAbandonButton from './view';
|
||||||
|
|
||||||
import { makeSelectClaimForUri } from 'lbry-redux';
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: props.uri && makeSelectClaimForUri(props.uri)(state),
|
claim: props.uri && makeSelectClaimForUri(props.uri)(state),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectChannelForClaimUri } from 'lbry-redux';
|
import { makeSelectChannelForClaimUri } from 'redux/selectors/claims';
|
||||||
import ClaimAuthor from './view';
|
import ClaimAuthor from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { connect } from 'react-redux';
|
||||||
import ClaimCollectionAdd from './view';
|
import ClaimCollectionAdd from './view';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import {
|
import {
|
||||||
makeSelectClaimForUri,
|
|
||||||
doLocalCollectionCreate,
|
|
||||||
selectBuiltinCollections,
|
selectBuiltinCollections,
|
||||||
selectMyPublishedCollections,
|
selectMyPublishedCollections,
|
||||||
selectMyUnpublishedCollections,
|
selectMyUnpublishedCollections,
|
||||||
} from 'lbry-redux';
|
} from 'redux/selectors/collections';
|
||||||
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
|
import { doLocalCollectionCreate } from 'redux/actions/collections';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doOpenModal } from 'redux/actions/app';
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
import CollectionAddButton from './view';
|
import CollectionAddButton from './view';
|
||||||
import { makeSelectClaimForUri, makeSelectClaimUrlInCollection } from 'lbry-redux';
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
|
import { makeSelectClaimUrlInCollection } from 'redux/selectors/collections';
|
||||||
|
|
||||||
const select = (state, props) => {
|
const select = (state, props) => {
|
||||||
const claim = makeSelectClaimForUri(props.uri)(state);
|
const claim = makeSelectClaimForUri(props.uri)(state);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri } from 'lbry-redux';
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
import ClaimEffectiveAmount from './view';
|
import ClaimEffectiveAmount from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectInsufficientCreditsForUri } from 'redux/selectors/content';
|
import { makeSelectInsufficientCreditsForUri } from 'redux/selectors/content';
|
||||||
import { makeSelectClaimWasPurchased } from 'lbry-redux';
|
import { makeSelectClaimWasPurchased } from 'redux/selectors/claims';
|
||||||
import ClaimInsufficientCredits from './view';
|
import ClaimInsufficientCredits from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doResolveUri, makeSelectClaimForUri, makeSelectIsUriResolving } from 'lbry-redux';
|
import { makeSelectClaimForUri, makeSelectIsUriResolving } from 'redux/selectors/claims';
|
||||||
|
import { doResolveUri } from 'redux/actions/claims';
|
||||||
import { doSetPlayingUri } from 'redux/actions/content';
|
import { doSetPlayingUri } from 'redux/actions/content';
|
||||||
import { punctuationMarks } from 'util/remark-lbry';
|
import { punctuationMarks } from 'util/remark-lbry';
|
||||||
import { selectBlackListedOutpoints } from 'lbryinc';
|
import { selectBlackListedOutpoints } from 'lbryinc';
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ClaimList from './view';
|
import ClaimList from './view';
|
||||||
import { SETTINGS } from 'lbry-redux';
|
import * as SETTINGS from 'constants/settings';
|
||||||
|
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
doClaimSearch,
|
|
||||||
selectClaimsByUri,
|
selectClaimsByUri,
|
||||||
selectClaimSearchByQuery,
|
selectClaimSearchByQuery,
|
||||||
selectClaimSearchByQueryLastPageReached,
|
selectClaimSearchByQueryLastPageReached,
|
||||||
selectFetchingClaimSearch,
|
selectFetchingClaimSearch,
|
||||||
SETTINGS,
|
} from 'redux/selectors/claims';
|
||||||
} from 'lbry-redux';
|
import { doClaimSearch } from 'redux/actions/claims';
|
||||||
|
import * as SETTINGS from 'constants/settings';
|
||||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||||
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
||||||
|
|
|
@ -5,7 +5,9 @@ import * as CS from 'constants/claim_search';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import usePersistedState from 'effects/use-persisted-state';
|
import usePersistedState from 'effects/use-persisted-state';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { createNormalizedClaimSearchKey, MATURE_TAGS, splitBySeparator } from 'lbry-redux';
|
import { MATURE_TAGS } from 'constants/tags';
|
||||||
|
import { createNormalizedClaimSearchKey } from 'util/claim';
|
||||||
|
import { splitBySeparator } from 'util/lbryURI';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import ClaimList from 'component/claimList';
|
import ClaimList from 'component/claimList';
|
||||||
|
@ -450,7 +452,9 @@ function ClaimListDiscover(props: Props) {
|
||||||
<p>
|
<p>
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
tokens={{
|
tokens={{
|
||||||
contact_support: <Button button="link" label={__('contact support')} href="https://odysee.com/@OdyseeHelp:b?view=about" />,
|
contact_support: (
|
||||||
|
<Button button="link" label={__('contact support')} href="https://odysee.com/@OdyseeHelp:b?view=about" />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
If you continue to have issues, please %contact_support%.
|
If you continue to have issues, please %contact_support%.
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectFetchingClaimSearch, SETTINGS } from 'lbry-redux';
|
import { selectFetchingClaimSearch } from 'redux/selectors/claims';
|
||||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
||||||
import { makeSelectClientSetting, selectShowMatureContent, selectLanguage } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectShowMatureContent, selectLanguage } from 'redux/selectors/settings';
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
|
import * as SETTINGS from 'constants/settings';
|
||||||
import ClaimListHeader from './view';
|
import ClaimListHeader from './view';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as CS from 'constants/claim_search';
|
import * as CS from 'constants/claim_search';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
|
import * as SETTINGS from 'constants/settings';
|
||||||
import type { Node } from 'react';
|
import type { Node } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import usePersistedState from 'effects/use-persisted-state';
|
import usePersistedState from 'effects/use-persisted-state';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import { SETTINGS } from 'lbry-redux';
|
|
||||||
import { FormField } from 'component/common/form';
|
import { FormField } from 'component/common/form';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import { toCapitalCase } from 'util/string';
|
import { toCapitalCase } from 'util/string';
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { makeSelectClaimForUri, makeSelectClaimIsMine } from 'redux/selectors/claims';
|
||||||
|
import { doCollectionEdit, doFetchItemsInCollection } from 'redux/actions/collections';
|
||||||
|
import { doPrepareEdit } from 'redux/actions/publish';
|
||||||
import {
|
import {
|
||||||
doCollectionEdit,
|
|
||||||
makeSelectClaimForUri,
|
|
||||||
makeSelectFileInfoForUri,
|
|
||||||
doPrepareEdit,
|
|
||||||
makeSelectCollectionForIdHasClaimUrl,
|
makeSelectCollectionForIdHasClaimUrl,
|
||||||
makeSelectCollectionIsMine,
|
makeSelectCollectionIsMine,
|
||||||
COLLECTIONS_CONSTS,
|
|
||||||
makeSelectEditedCollectionForId,
|
makeSelectEditedCollectionForId,
|
||||||
makeSelectClaimIsMine,
|
|
||||||
doFetchItemsInCollection,
|
|
||||||
makeSelectUrlsForCollectionId,
|
makeSelectUrlsForCollectionId,
|
||||||
} from 'lbry-redux';
|
} from 'redux/selectors/collections';
|
||||||
|
import { makeSelectFileInfoForUri } from 'redux/selectors/file_info';
|
||||||
|
import * as COLLECTIONS_CONSTS from 'constants/collections';
|
||||||
import { makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
import { makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
||||||
import { doChannelMute, doChannelUnmute } from 'redux/actions/blocked';
|
import { doChannelMute, doChannelUnmute } from 'redux/actions/blocked';
|
||||||
import { doSetActiveChannel, doSetIncognito, doOpenModal } from 'redux/actions/app';
|
import { doSetActiveChannel, doSetIncognito, doOpenModal } from 'redux/actions/app';
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { URL, SHARE_DOMAIN_URL } from 'config';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import * as MODALS from 'constants/modal_types';
|
import * as MODALS from 'constants/modal_types';
|
||||||
|
import * as COLLECTIONS_CONSTS from 'constants/collections';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Menu, MenuButton, MenuList, MenuItem } from '@reach/menu-button';
|
import { Menu, MenuButton, MenuList, MenuItem } from '@reach/menu-button';
|
||||||
|
@ -15,7 +16,7 @@ import {
|
||||||
generateListSearchUrlParams,
|
generateListSearchUrlParams,
|
||||||
} from 'util/url';
|
} from 'util/url';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import { buildURI, parseURI, COLLECTIONS_CONSTS } from 'lbry-redux';
|
import { buildURI, parseURI } from 'util/lbryURI';
|
||||||
|
|
||||||
const SHARE_DOMAIN = SHARE_DOMAIN_URL || URL;
|
const SHARE_DOMAIN = SHARE_DOMAIN_URL || URL;
|
||||||
const PAGE_VIEW_QUERY = 'view';
|
const PAGE_VIEW_QUERY = 'view';
|
||||||
|
@ -174,13 +175,14 @@ function ClaimMenuList(props: Props) {
|
||||||
|
|
||||||
function handleFollow() {
|
function handleFollow() {
|
||||||
const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe;
|
const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe;
|
||||||
|
if (channelName) {
|
||||||
subscriptionHandler({
|
subscriptionHandler({
|
||||||
channelName: '@' + channelName,
|
channelName: '@' + channelName,
|
||||||
uri: contentChannelUri,
|
uri: contentChannelUri,
|
||||||
notificationsDisabled: true,
|
notificationsDisabled: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleToggleMute() {
|
function handleToggleMute() {
|
||||||
if (channelIsMuted) {
|
if (channelIsMuted) {
|
||||||
|
@ -202,7 +204,7 @@ function ClaimMenuList(props: Props) {
|
||||||
if (!isChannel) {
|
if (!isChannel) {
|
||||||
const signingChannelName = contentSigningChannel && contentSigningChannel.name;
|
const signingChannelName = contentSigningChannel && contentSigningChannel.name;
|
||||||
|
|
||||||
const uriObject: { streamName: string, streamClaimId: string, channelName?: string } = {
|
const uriObject: LbryUrlObj = {
|
||||||
streamName: claim.name,
|
streamName: claim.name,
|
||||||
streamClaimId: claim.claim_id,
|
streamClaimId: claim.claim_id,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
doResolveUri,
|
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectIsUriResolving,
|
makeSelectIsUriResolving,
|
||||||
makeSelectClaimIsMine,
|
makeSelectClaimIsMine,
|
||||||
|
@ -8,15 +7,20 @@ import {
|
||||||
makeSelectClaimIsNsfw,
|
makeSelectClaimIsNsfw,
|
||||||
makeSelectReflectingClaimForUri,
|
makeSelectReflectingClaimForUri,
|
||||||
makeSelectClaimWasPurchased,
|
makeSelectClaimWasPurchased,
|
||||||
makeSelectStreamingUrlForUri,
|
|
||||||
makeSelectClaimIsStreamPlaceholder,
|
makeSelectClaimIsStreamPlaceholder,
|
||||||
makeSelectCollectionIsMine,
|
|
||||||
doCollectionEdit,
|
|
||||||
makeSelectUrlsForCollectionId,
|
|
||||||
makeSelectIndexForUrlInCollection,
|
|
||||||
makeSelectTitleForUri,
|
makeSelectTitleForUri,
|
||||||
makeSelectDateForUri,
|
makeSelectDateForUri,
|
||||||
} from 'lbry-redux';
|
} from 'redux/selectors/claims';
|
||||||
|
import { makeSelectStreamingUrlForUri } from 'redux/selectors/file_info';
|
||||||
|
import {
|
||||||
|
makeSelectCollectionIsMine,
|
||||||
|
makeSelectUrlsForCollectionId,
|
||||||
|
makeSelectIndexForUrlInCollection,
|
||||||
|
} from 'redux/selectors/collections';
|
||||||
|
|
||||||
|
import { doResolveUri } from 'redux/actions/claims';
|
||||||
|
import { doCollectionEdit } from 'redux/actions/collections';
|
||||||
|
import { doFileGet } from 'redux/actions/file';
|
||||||
import { selectMutedChannels, makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
import { selectMutedChannels, makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
||||||
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||||
import { makeSelectIsActiveLivestream } from 'redux/selectors/livestream';
|
import { makeSelectIsActiveLivestream } from 'redux/selectors/livestream';
|
||||||
|
@ -24,7 +28,6 @@ import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
|
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
|
||||||
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
||||||
import { selectModerationBlockList } from 'redux/selectors/comments';
|
import { selectModerationBlockList } from 'redux/selectors/comments';
|
||||||
import { doFileGet } from 'redux/actions/file';
|
|
||||||
import ClaimPreview from './view';
|
import ClaimPreview from './view';
|
||||||
import formatMediaDuration from 'util/formatMediaDuration';
|
import formatMediaDuration from 'util/formatMediaDuration';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { NavLink, withRouter } from 'react-router-dom';
|
||||||
import { isEmpty } from 'util/object';
|
import { isEmpty } from 'util/object';
|
||||||
import { lazyImport } from 'util/lazyImport';
|
import { lazyImport } from 'util/lazyImport';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { parseURI, COLLECTIONS_CONSTS, isURIEqual } from 'lbry-redux';
|
import { isURIEqual, isURIValid } from 'util/lbryURI';
|
||||||
|
import * as COLLECTIONS_CONSTS from 'constants/collections';
|
||||||
import { formatLbryUrlForWeb } from 'util/url';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
|
import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
|
||||||
import FileThumbnail from 'component/fileThumbnail';
|
import FileThumbnail from 'component/fileThumbnail';
|
||||||
|
@ -179,15 +180,8 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}, [channelSubCount]);
|
}, [channelSubCount]);
|
||||||
let isValid = false;
|
const isValid = uri && isURIValid(uri);
|
||||||
if (uri) {
|
|
||||||
try {
|
|
||||||
parseURI(uri);
|
|
||||||
isValid = true;
|
|
||||||
} catch (e) {
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
const isPlayable =
|
const isPlayable =
|
||||||
claim &&
|
claim &&
|
||||||
|
|
|
@ -3,10 +3,9 @@ import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectClaimIsPending,
|
makeSelectClaimIsPending,
|
||||||
doClearPublish,
|
|
||||||
doPrepareEdit,
|
|
||||||
makeSelectClaimIsStreamPlaceholder,
|
makeSelectClaimIsStreamPlaceholder,
|
||||||
} from 'lbry-redux';
|
} from 'redux/selectors/claims';
|
||||||
|
import { doClearPublish, doPrepareEdit } from 'redux/actions/publish';
|
||||||
import { push } from 'connected-react-router';
|
import { push } from 'connected-react-router';
|
||||||
import ClaimPreviewSubtitle from './view';
|
import ClaimPreviewSubtitle from './view';
|
||||||
import { doFetchSubCount, makeSelectSubCountForUri } from 'lbryinc';
|
import { doFetchSubCount, makeSelectSubCountForUri } from 'lbryinc';
|
||||||
|
|
|
@ -5,14 +5,14 @@ import UriIndicator from 'component/uriIndicator';
|
||||||
import DateTime from 'component/dateTime';
|
import DateTime from 'component/dateTime';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import FileViewCountInline from 'component/fileViewCountInline';
|
import FileViewCountInline from 'component/fileViewCountInline';
|
||||||
import { parseURI } from 'lbry-redux';
|
import { parseURI } from 'util/lbryURI';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
claim: ?Claim,
|
claim: ?Claim,
|
||||||
pending?: boolean,
|
pending?: boolean,
|
||||||
type: string,
|
type: string,
|
||||||
beginPublish: (string) => void,
|
beginPublish: (?string) => void,
|
||||||
isLivestream: boolean,
|
isLivestream: boolean,
|
||||||
fetchSubCount: (string) => void,
|
fetchSubCount: (string) => void,
|
||||||
subCount: number,
|
subCount: number,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
doResolveUri,
|
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectIsUriResolving,
|
makeSelectIsUriResolving,
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
|
@ -9,11 +8,12 @@ import {
|
||||||
makeSelectClaimIsNsfw,
|
makeSelectClaimIsNsfw,
|
||||||
makeSelectClaimIsStreamPlaceholder,
|
makeSelectClaimIsStreamPlaceholder,
|
||||||
makeSelectDateForUri,
|
makeSelectDateForUri,
|
||||||
} from 'lbry-redux';
|
} from 'redux/selectors/claims';
|
||||||
|
import { doFileGet } from 'redux/actions/file';
|
||||||
|
import { doResolveUri } from 'redux/actions/claims';
|
||||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||||
import { makeSelectViewCountForUri, selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
import { makeSelectViewCountForUri, selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||||
import { makeSelectIsActiveLivestream } from 'redux/selectors/livestream';
|
import { makeSelectIsActiveLivestream } from 'redux/selectors/livestream';
|
||||||
import { doFileGet } from 'redux/actions/file';
|
|
||||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import ClaimPreviewTile from './view';
|
import ClaimPreviewTile from './view';
|
||||||
import formatMediaDuration from 'util/formatMediaDuration';
|
import formatMediaDuration from 'util/formatMediaDuration';
|
||||||
|
|
|
@ -12,7 +12,7 @@ import SubscribeButton from 'component/subscribeButton';
|
||||||
import useGetThumbnail from 'effects/use-get-thumbnail';
|
import useGetThumbnail from 'effects/use-get-thumbnail';
|
||||||
import { formatLbryUrlForWeb, generateListSearchUrlParams } from 'util/url';
|
import { formatLbryUrlForWeb, generateListSearchUrlParams } from 'util/url';
|
||||||
import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
|
import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
|
||||||
import { parseURI, isURIEqual } from 'lbry-redux';
|
import { parseURI, isURIEqual } from 'util/lbryURI';
|
||||||
import PreviewOverlayProperties from 'component/previewOverlayProperties';
|
import PreviewOverlayProperties from 'component/previewOverlayProperties';
|
||||||
import FileDownloadLink from 'component/fileDownloadLink';
|
import FileDownloadLink from 'component/fileDownloadLink';
|
||||||
import FileWatchLaterLink from 'component/fileWatchLaterLink';
|
import FileWatchLaterLink from 'component/fileWatchLaterLink';
|
||||||
|
@ -191,9 +191,11 @@ function ClaimPreviewTile(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
<div className="placeholder__wrapper">
|
<div className="placeholder__wrapper">
|
||||||
<div className="placeholder claim-tile__title" />
|
<div className="placeholder claim-tile__title" />
|
||||||
<div className={classnames('claim-tile__info placeholder', {
|
<div
|
||||||
'contains_view_count': shouldShowViewCount,
|
className={classnames('claim-tile__info placeholder', {
|
||||||
})} />
|
contains_view_count: shouldShowViewCount,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
@ -253,9 +255,11 @@ function ClaimPreviewTile(props: Props) {
|
||||||
<ClaimMenuList uri={uri} collectionId={listId} channelUri={channelUri} />
|
<ClaimMenuList uri={uri} collectionId={listId} channelUri={channelUri} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={classnames('claim-tile__info', {
|
<div
|
||||||
'contains_view_count': shouldShowViewCount,
|
className={classnames('claim-tile__info', {
|
||||||
})}>
|
contains_view_count: shouldShowViewCount,
|
||||||
|
})}
|
||||||
|
>
|
||||||
{isChannel ? (
|
{isChannel ? (
|
||||||
<div className="claim-tile__about--channel">
|
<div className="claim-tile__about--channel">
|
||||||
<SubscribeButton uri={repostedChannelUri || uri} />
|
<SubscribeButton uri={repostedChannelUri || uri} />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri, makeSelectTitleForUri } from 'lbry-redux';
|
import { makeSelectClaimForUri, makeSelectTitleForUri } from 'redux/selectors/claims';
|
||||||
import ClaimPreviewTitle from './view';
|
import ClaimPreviewTitle from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimIsMine, makeSelectClaimForUri } from 'lbry-redux';
|
import { makeSelectClaimIsMine, makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
||||||
import ClaimProperties from './view';
|
import ClaimProperties from './view';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri } from 'lbry-redux';
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
import ClaimRepostAuthor from './view';
|
import ClaimRepostAuthor from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue