From 9d4f7dc642d06f7c7d6b6ddb1c46232059a811c3 Mon Sep 17 00:00:00 2001 From: jessop Date: Mon, 5 Oct 2020 14:31:51 -0400 Subject: [PATCH] sync reducer stuff bring tags into app repo prevent prefset until prefsReady prefs ready on sign up prefsReady-desktop --- static/app-strings.json | 3 + ui/component/app/index.js | 2 +- ui/component/claimListDiscover/index.js | 2 +- ui/component/claimListHeader/index.js | 3 +- ui/component/claimTags/index.js | 8 +- ui/component/sideNavigation/index.js | 3 +- ui/component/syncPassword/index.js | 2 +- ui/component/tagsSearch/index.js | 18 +- ui/component/tagsSelect/index.js | 18 +- ui/component/userChannelFollowIntro/index.js | 7 +- ui/component/userSignUp/index.js | 3 + ui/component/userSignUp/view.jsx | 5 +- ui/component/userTagFollowIntro/index.js | 2 +- ui/constants/action_types.js | 6 + ui/constants/tags.js | 546 +++++++++++++++++++ ui/page/channelsFollowingDiscover/index.js | 2 +- ui/page/discover/index.js | 3 +- ui/page/home/index.js | 2 +- ui/page/tagsFollowing/index.js | 2 +- ui/page/tagsFollowingManage/index.js | 11 +- ui/reducers.js | 3 +- ui/redux/actions/app.js | 24 +- ui/redux/actions/blocked.js | 23 +- ui/redux/actions/publish.js | 2 +- ui/redux/actions/reactions.js | 2 +- ui/redux/actions/settings.js | 33 +- ui/redux/actions/subscriptions.js | 13 + ui/redux/actions/sync.js | 70 ++- ui/redux/actions/syncwrapper.js | 58 -- ui/redux/actions/tags.js | 33 +- ui/redux/reducers/app.js | 7 - ui/redux/reducers/subscriptions.js | 6 + ui/redux/reducers/sync.js | 21 + ui/redux/reducers/tags.js | 82 +++ ui/redux/selectors/app.js | 2 - ui/redux/selectors/sync.js | 4 + ui/redux/selectors/tags.js | 36 ++ ui/store.js | 4 +- 38 files changed, 925 insertions(+), 146 deletions(-) create mode 100644 ui/constants/tags.js delete mode 100644 ui/redux/actions/syncwrapper.js create mode 100644 ui/redux/reducers/tags.js create mode 100644 ui/redux/selectors/tags.js diff --git a/static/app-strings.json b/static/app-strings.json index 2f0c6261d..13e86fb46 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -1348,6 +1348,9 @@ "Create a channel": "Create a channel", "Credit Details": "Credit Details", "LBRY Credits": "LBRY Credits", + "Mark all as read": "Mark all as read", + "Log in to %SITE_NAME% to earn rewards From Inviting Your Friends": "Log in to %SITE_NAME% to earn rewards From Inviting Your Friends", + "Confirming...": "Confirming...", "Pinned by @%channel%": "Pinned by @%channel%", "Pinned by creator": "Pinned by creator", "Claim Your %reward_amount% Credit Invite Reward": "Claim Your %reward_amount% Credit Invite Reward", diff --git a/ui/component/app/index.js b/ui/component/app/index.js index 8bcef8c81..e172b50d4 100644 --- a/ui/component/app/index.js +++ b/ui/component/app/index.js @@ -9,7 +9,7 @@ import { doFetchChannelListMine, SETTINGS } from 'lbry-redux'; import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings'; import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded, selectModal } from 'redux/selectors/app'; import { doGetWalletSyncPreference, doSetLanguage } from 'redux/actions/settings'; -import { doSyncSubscribe } from 'redux/actions/syncwrapper'; +import { doSyncSubscribe } from 'redux/actions/sync'; import { doDownloadUpgradeRequested, doSignIn, diff --git a/ui/component/claimListDiscover/index.js b/ui/component/claimListDiscover/index.js index 3f4f74c52..5fa38b2f0 100644 --- a/ui/component/claimListDiscover/index.js +++ b/ui/component/claimListDiscover/index.js @@ -5,8 +5,8 @@ import { selectClaimSearchByQueryLastPageReached, selectFetchingClaimSearch, SETTINGS, - selectFollowedTags, } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import { selectBlockedChannels } from 'redux/selectors/blocked'; import { doToggleTagFollowDesktop } from 'redux/actions/tags'; import { makeSelectClientSetting } from 'redux/selectors/settings'; diff --git a/ui/component/claimListHeader/index.js b/ui/component/claimListHeader/index.js index d3b2fd0ed..54e319089 100644 --- a/ui/component/claimListHeader/index.js +++ b/ui/component/claimListHeader/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; -import { selectFetchingClaimSearch, SETTINGS, selectFollowedTags } from 'lbry-redux'; +import { selectFetchingClaimSearch, SETTINGS } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import { doToggleTagFollowDesktop } from 'redux/actions/tags'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import { doSetClientSetting } from 'redux/actions/settings'; diff --git a/ui/component/claimTags/index.js b/ui/component/claimTags/index.js index 0f498f2e3..1fbca33d3 100644 --- a/ui/component/claimTags/index.js +++ b/ui/component/claimTags/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; -import { makeSelectTagsForUri, selectFollowedTags } from 'lbry-redux'; +import { makeSelectTagsForUri } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import ClaimTags from './view'; const select = (state, props) => ({ @@ -7,7 +8,4 @@ const select = (state, props) => ({ followedTags: selectFollowedTags(state), }); -export default connect( - select, - null -)(ClaimTags); +export default connect(select, null)(ClaimTags); diff --git a/ui/component/sideNavigation/index.js b/ui/component/sideNavigation/index.js index c7933ed48..f033c2665 100644 --- a/ui/component/sideNavigation/index.js +++ b/ui/component/sideNavigation/index.js @@ -1,6 +1,7 @@ import { connect } from 'react-redux'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; -import { selectFollowedTags, selectPurchaseUriSuccess, doClearPurchasedUriSuccess, SETTINGS } from 'lbry-redux'; +import { selectPurchaseUriSuccess, doClearPurchasedUriSuccess, SETTINGS } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import { doSignOut } from 'redux/actions/app'; diff --git a/ui/component/syncPassword/index.js b/ui/component/syncPassword/index.js index c6e6cb08d..cecddf233 100644 --- a/ui/component/syncPassword/index.js +++ b/ui/component/syncPassword/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { selectGetSyncIsPending, selectSyncApplyPasswordError } from 'redux/selectors/sync'; -import { doGetSyncDesktop } from 'redux/actions/syncwrapper'; +import { doGetSyncDesktop } from 'redux/actions/sync'; import { selectUserEmail } from 'redux/selectors/user'; import { doSetClientSetting } from 'redux/actions/settings'; import { doSignOut, doHandleSyncComplete } from 'redux/actions/app'; diff --git a/ui/component/tagsSearch/index.js b/ui/component/tagsSearch/index.js index a6c71c5bb..9f547c2ef 100644 --- a/ui/component/tagsSearch/index.js +++ b/ui/component/tagsSearch/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; -import { selectUnfollowedTags, selectFollowedTags, doReplaceTags, doAddTag, doDeleteTag } from 'lbry-redux'; -import { doToggleTagFollowDesktop } from 'redux/actions/tags'; +import { selectUnfollowedTags, selectFollowedTags } from 'redux/selectors/tags'; +import { doToggleTagFollowDesktop, doAddTag, doDeleteTag } from 'redux/actions/tags'; import DiscoveryFirstRun from './view'; const select = (state, props) => ({ @@ -8,12 +8,8 @@ const select = (state, props) => ({ followedTags: selectFollowedTags(state), }); -export default connect( - select, - { - doToggleTagFollowDesktop, - doAddTag, - doDeleteTag, - doReplaceTags, - } -)(DiscoveryFirstRun); +export default connect(select, { + doToggleTagFollowDesktop, + doAddTag, + doDeleteTag, +})(DiscoveryFirstRun); diff --git a/ui/component/tagsSelect/index.js b/ui/component/tagsSelect/index.js index a6c71c5bb..9f547c2ef 100644 --- a/ui/component/tagsSelect/index.js +++ b/ui/component/tagsSelect/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; -import { selectUnfollowedTags, selectFollowedTags, doReplaceTags, doAddTag, doDeleteTag } from 'lbry-redux'; -import { doToggleTagFollowDesktop } from 'redux/actions/tags'; +import { selectUnfollowedTags, selectFollowedTags } from 'redux/selectors/tags'; +import { doToggleTagFollowDesktop, doAddTag, doDeleteTag } from 'redux/actions/tags'; import DiscoveryFirstRun from './view'; const select = (state, props) => ({ @@ -8,12 +8,8 @@ const select = (state, props) => ({ followedTags: selectFollowedTags(state), }); -export default connect( - select, - { - doToggleTagFollowDesktop, - doAddTag, - doDeleteTag, - doReplaceTags, - } -)(DiscoveryFirstRun); +export default connect(select, { + doToggleTagFollowDesktop, + doAddTag, + doDeleteTag, +})(DiscoveryFirstRun); diff --git a/ui/component/userChannelFollowIntro/index.js b/ui/component/userChannelFollowIntro/index.js index 9b6e3ec3e..e4838c877 100644 --- a/ui/component/userChannelFollowIntro/index.js +++ b/ui/component/userChannelFollowIntro/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectFollowedTags } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; import { doChannelSubscribe } from 'redux/actions/subscriptions'; import UserChannelFollowIntro from './view'; @@ -13,7 +13,4 @@ const perform = dispatch => ({ channelSubscribe: uri => dispatch(doChannelSubscribe(uri)), }); -export default connect( - select, - perform -)(UserChannelFollowIntro); +export default connect(select, perform)(UserChannelFollowIntro); diff --git a/ui/component/userSignUp/index.js b/ui/component/userSignUp/index.js index ccc921fc1..b3ce229e0 100644 --- a/ui/component/userSignUp/index.js +++ b/ui/component/userSignUp/index.js @@ -22,6 +22,8 @@ import { import { makeSelectClientSetting } from 'redux/selectors/settings'; import { selectInterestedInYoutubeSync } from 'redux/selectors/app'; import { doToggleInterestedInYoutubeSync } from 'redux/actions/app'; +import { doSetPrefsReady } from 'redux/actions/sync'; + import UserSignIn from './view'; const select = state => ({ @@ -63,6 +65,7 @@ const perform = dispatch => ({ ), setClientSetting: (setting, value, pushToPrefs) => dispatch(doSetClientSetting(setting, value, pushToPrefs)), doToggleInterestedInYoutubeSync: () => dispatch(doToggleInterestedInYoutubeSync()), + setPrefsReady: () => dispatch(doSetPrefsReady()), }); export default connect(select, perform)(UserSignIn); diff --git a/ui/component/userSignUp/view.jsx b/ui/component/userSignUp/view.jsx index 2cf6852d7..98f4ca47b 100644 --- a/ui/component/userSignUp/view.jsx +++ b/ui/component/userSignUp/view.jsx @@ -45,6 +45,7 @@ type Props = { rewardsAcknowledged: boolean, interestedInYoutubeSync: boolean, doToggleInterestedInYoutubeSync: () => void, + setPrefsReady: () => void, }; function UserSignUp(props: Props) { @@ -70,6 +71,7 @@ function UserSignUp(props: Props) { setClientSetting, interestedInYoutubeSync, doToggleInterestedInYoutubeSync, + setPrefsReady, } = props; const { location: { search, pathname }, @@ -133,9 +135,10 @@ function UserSignUp(props: Props) { React.useEffect(() => { if (hasVerifiedEmail) { + setPrefsReady(); setSettingAndSync(SETTINGS.FIRST_RUN_STARTED, true); } - }, [hasVerifiedEmail]); + }, [hasVerifiedEmail, setPrefsReady]); React.useEffect(() => { // Don't claim the reward if sync is enabled until after a sync has been completed successfully diff --git a/ui/component/userTagFollowIntro/index.js b/ui/component/userTagFollowIntro/index.js index 4f7f29d42..2ef156393 100644 --- a/ui/component/userTagFollowIntro/index.js +++ b/ui/component/userTagFollowIntro/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectFollowedTags } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import UserTagFollowIntro from './view'; const select = state => ({ diff --git a/ui/constants/action_types.js b/ui/constants/action_types.js index 9278991b9..8410257a0 100644 --- a/ui/constants/action_types.js +++ b/ui/constants/action_types.js @@ -279,6 +279,11 @@ export const COMMENT_SET_CHANNEL = 'COMMENT_SET_CHANNEL'; // Blocked channels export const TOGGLE_BLOCK_CHANNEL = 'TOGGLE_BLOCK_CHANNEL'; +// Tags +export const TOGGLE_TAG_FOLLOW = 'TOGGLE_TAG_FOLLOW'; +export const TAG_ADD = 'TAG_ADD'; +export const TAG_DELETE = 'TAG_DELETE'; + // Notifications export const WS_CONNECT = 'WS_CONNECT'; export const WS_DISCONNECT = 'WS_DISCONNECT'; @@ -290,6 +295,7 @@ 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_PREFS_READY = 'SET_PREFS_READY'; export const SET_DEFAULT_ACCOUNT = 'SET_DEFAULT_ACCOUNT'; export const SYNC_APPLY_STARTED = 'SYNC_APPLY_STARTED'; export const SYNC_APPLY_COMPLETED = 'SYNC_APPLY_COMPLETED'; diff --git a/ui/constants/tags.js b/ui/constants/tags.js new file mode 100644 index 000000000..11a5cedb0 --- /dev/null +++ b/ui/constants/tags.js @@ -0,0 +1,546 @@ +export const DEFAULT_FOLLOWED_TAGS = [ + 'art', + 'automotive', + 'blockchain', + 'comedy', + 'economics', + 'education', + 'gaming', + 'music', + 'news', + 'science', + 'sports', + 'technology', +]; + +export const MATURE_TAGS = [ + 'porn', + 'porno', + 'nsfw', + 'mature', + 'xxx', + 'sex', + 'creampie', + 'blowjob', + 'handjob', + 'vagina', + 'boobs', + 'big boobs', + 'big dick', + 'pussy', + 'cumshot', + 'anal', + 'hard fucking', + 'ass', + 'fuck', + 'hentai', +]; + +const DEFAULT_ENGLISH_KNOWN_TAGS = [ + 'free speech', + 'censorship', + 'gaming', + 'pop culture', + 'entertainment', + 'technology', + 'music', + 'funny', + 'education', + 'learning', + 'news', + 'gameplay', + 'nature', + 'beliefs', + 'comedy', + 'games', + 'film & animation', + 'game', + 'weapons', + 'blockchain', + 'video game', + 'sports', + 'walkthrough', + 'lbrytvpaidbeta', + 'art', + 'pc', + 'minecraft', + 'playthrough', + 'economics', + 'automotive', + 'play', + 'tutorial', + 'twitch', + 'how to', + 'ps4', + 'bitcoin', + 'fortnite', + 'commentary', + 'lets play', + 'fun', + 'politics', + 'travel', + 'food', + 'science', + 'xbox', + 'liberal', + 'democrat', + 'progressive', + 'survival', + 'non-profits', + 'activism', + 'cryptocurrency', + 'playstation', + 'nintendo', + 'government', + 'steam', + 'podcast', + 'gamer', + 'horror', + 'conservative', + 'reaction', + 'trailer', + 'love', + 'cnn', + 'republican', + 'political', + 'hangoutsonair', + 'hoa', + 'msnbc', + 'cbs', + 'anime', + 'donald trump', + 'fiction', + 'fox news', + 'crypto', + 'ethereum', + 'call of duty', + 'android', + 'multiplayer', + 'epic', + 'rpg', + 'adventure', + 'secular talk', + 'btc', + 'atheist', + 'atheism', + 'video games', + 'ps3', + 'cod', + 'online', + 'agnostic', + 'movie', + 'fps', + 'lets', + 'mod', + 'world', + 'reviews', + 'sharefactory', + 'space', + 'pokemon', + 'stream', + 'hilarious', + 'lol', + 'sony', + 'god', + 'dance', + 'pvp', + 'tech', + 'strategy', + 'zombies', + 'fail', + 'film', + 'xbox360', + 'animation', + 'unboxing', + 'money', + 'wwe', + 'mods', + 'indie', + 'pubg', + 'ios', + 'history', + 'rap', + 'mobile', + 'trump', + 'hack', + 'flat earth', + 'trap', + 'humor', + 'vlogging', + 'fox', + 'news radio', + 'facebook', + 'edm', + 'fitness', + 'vaping', + 'hip hop', + 'secular', + 'jesus', + 'song', + 'vape', + 'guitar', + 'remix', + 'mining', + 'daily', + 'diy', + 'pets', + 'videogame', + 'death', + 'funny moments', + 'religion', + 'media', + 'viral', + 'war', + 'nbc', + 'freedom', + 'gold', + 'family', + 'meme', + 'zombie', + 'photography', + 'chill', + 'sniper', + 'computer', + 'iphone', + 'dragon', + 'bible', + 'pro', + 'overwatch', + 'litecoin', + 'gta', + 'house', + 'fire', + 'bass', + 'truth', + 'crash', + 'mario', + 'league of legends', + 'wii', + 'mmorpg', + 'health', + 'marvel', + 'racing', + 'apple', + 'instrumental', + 'earth', + 'destiny', + 'satire', + 'race', + 'training', + 'electronic', + 'boss', + 'roblox', + 'family friendly', + 'california', + 'react', + 'christian', + 'mmo', + 'twitter', + 'help', + 'star', + 'cars', + 'random', + 'top 10', + 'ninja', + 'guns', + 'linux', + 'lessons', + 'vegan', + 'future', + 'dota 2', + 'studio', + 'star wars', + 'shooting', + 'nasa', + 'rock', + 'league', + 'subscribe', + 'water', + 'gta v', + 'car', + 'samsung', + 'music video', + 'skyrim', + 'dog', + 'comics', + 'shooter game', + 'bo3', + 'halloween', + 'liberty', + 'eth', + 'conspiracy', + 'knife', + 'fashion', + 'stories', + 'vapor', + 'nvidia', + 'cute', + 'beat', + 'nintendo switch', + 'fantasy', + 'christmas', + 'world of warcraft', + 'industry', + 'cartoon', + 'garden', + 'animals', + 'windows', + 'happy', + 'magic', + 'memes', + 'design', + 'tactical', + 'fallout 4', + 'puzzle', + 'parody', + 'rv', + 'beats', + 'building', + 'disney', + 'drone', + 'ps2', + 'beach', + 'metal', + 'christianity', + 'business', + 'mix', + 'bo2', + 'cover', + 'senate', + '4k', + 'united states', + 'final', + 'hero', + 'playing', + 'dlc', + 'ubisoft', + 'halo', + 'pc gaming', + 'raw', + 'investing', + 'online learning', + 'software', + 'ark', + 'mojang', + 'console', + 'battle royale', + 'canon', + 'microsoft', + 'camping', + 'ufo', + 'progressive talk', + 'switch', + 'fpv', + 'arcade', + 'school', + 'driving', + 'bodybuilding', + 'drama', + 'retro', + 'science fiction', + 'eggs', + 'australia', + 'modded', + 'rainbow', + 'gamers', + 'resident evil', + 'drawing', + 'brasil', + 'england', + 'hillary clinton', + 'singing', + 'final fantasy', + 'hiphop', + 'video blog', + 'mature', + 'quad', + 'noob', + 'simulation', + 'illuminati', + 'poetry', + 'dayz', + 'manga', + 'howto', + 'insane', + 'press', + 'special', + 'church', + 'ico', + 'weird', + 'libertarian', + 'crafting', + 'level', + 'comic', + 'sandbox', + 'daily vlog', + 'outdoor', + 'black ops', + 'sound', + 'christ', + 'duty', + 'juvenile fiction', + 'pc game', + 'how-to', + 'ww2', + 'creepy', + 'artist', + 'galaxy', + 'destiny 2', + 'new music', + 'quest', + 'lee', + 'pacman', + 'super smash bros', + 'day', + 'survival horror', + 'patreon', + 'bitcoin price', + 'trending', + 'open world', + 'wii u', + 'dope', + 'reaper', + 'sniping', + 'dubstep', + 'truck', + 'planet', + 'dc', + 'amazon', + 'spirituality', + 'universe', + 'video game culture', + 'community', + 'cat', + 'aliens', + 'tourism', + 'altcoins', + 'style', + 'travel trailer', + 'rda', + 'gun', + 'secret', + 'far cry 5', + 'auto', + 'culture', + 'dj', + 'mw2', + 'lord', + 'full time rving', + 'role-playing game', + 'prank', + 'grand theft auto', + 'master', + 'wrestling', + 'sci-fi', + 'workout', + 'ghost', + 'fake news', + 'silly', + 'season', + 'bo4', + 'trading', + 'extreme', + 'economy', + 'combat', + 'plays', + 'muslim', + 'pubg mobile', + 'clips', + 'bo1', + 'paypal', + 'sims', + 'exploration', + 'light', + 'ripple', + 'paranormal', + 'football', + 'capcom', + 'rta', + 'discord', + 'batman', + 'player', + 'server', + 'anarchy', + 'military', + 'playlist', + 'cosplay', + 'rv park', + 'rant', + 'edit', + 'germany', + 'reading', + 'chris', + 'flash', + 'loot', + 'bitcoin gratis', + 'game reviews', + 'movies', + 'stupid', + 'latest news', + 'squad gameplay', + 'guru', + 'timelapse', + 'black ops 3', + 'holiday', + 'soul', + 'motivation', + 'mw3', + 'vacation', + 'sega', + '19th century', + 'pop', + 'sims 4', + 'post', + 'smok', + 'island', + 'scotland', + 'paladins', + 'warrior', + 'creepypasta', + 'role-playing', + 'solar', + 'vr', + 'animal', + 'peace', + 'consciousness', + 'dota', + 'audio', + 'mass effect', + 'humour', + 'first look', + 'videogames', + 'future bass', + 'freestyle', + 'hardcore', + 'portugal', + 'dantdm', + 'teaser', + 'lbry', + 'coronavirus', + '2020protests', + 'covidcuts', + 'covid-19', + 'LBRYFoundationBoardCandidacy', +]; + +const DEFAULT_SPANISH_KNOWN_TAGS = [ + 'español', + 'tecnología', + 'criptomonedas', + 'economía', + 'bitcoin', + 'educación', + 'videojuegos', + 'música', + 'noticias', + 'ciencia', + 'deportes', + 'latinoamérica', + 'latam', + 'conspiración', + 'humor', + 'política', + 'tutoriales', +]; + +export const DEFAULT_KNOWN_TAGS = [...DEFAULT_ENGLISH_KNOWN_TAGS, ...DEFAULT_SPANISH_KNOWN_TAGS]; diff --git a/ui/page/channelsFollowingDiscover/index.js b/ui/page/channelsFollowingDiscover/index.js index 89eb4c60f..90462bebf 100644 --- a/ui/page/channelsFollowingDiscover/index.js +++ b/ui/page/channelsFollowingDiscover/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectFollowedTags } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import { selectBlockedChannels } from 'redux/selectors/blocked'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; import ChannelsFollowingManagePage from './view'; diff --git a/ui/page/discover/index.js b/ui/page/discover/index.js index ad6b4a325..f2d0a0a28 100644 --- a/ui/page/discover/index.js +++ b/ui/page/discover/index.js @@ -1,7 +1,8 @@ import * as CS from 'constants/claim_search'; import { connect } from 'react-redux'; -import { makeSelectClaimForUri, selectFollowedTags, doResolveUri, SETTINGS } from 'lbry-redux'; +import { makeSelectClaimForUri, doResolveUri, SETTINGS } from 'lbry-redux'; import { selectUserVerifiedEmail } from 'redux/selectors/user'; +import { selectFollowedTags } from 'redux/selectors/tags'; import { doToggleTagFollowDesktop } from 'redux/actions/tags'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import Tags from './view'; diff --git a/ui/page/home/index.js b/ui/page/home/index.js index a70c7afd2..72a1853a7 100644 --- a/ui/page/home/index.js +++ b/ui/page/home/index.js @@ -1,6 +1,6 @@ import * as SETTINGS from 'constants/settings'; import { connect } from 'react-redux'; -import { selectFollowedTags } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; import { makeSelectClientSetting } from 'redux/selectors/settings'; diff --git a/ui/page/tagsFollowing/index.js b/ui/page/tagsFollowing/index.js index cc88b8606..42906ccae 100644 --- a/ui/page/tagsFollowing/index.js +++ b/ui/page/tagsFollowing/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectFollowedTags } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; import DiscoverPage from './view'; diff --git a/ui/page/tagsFollowingManage/index.js b/ui/page/tagsFollowingManage/index.js index 69281c1d7..a4e12bf31 100644 --- a/ui/page/tagsFollowingManage/index.js +++ b/ui/page/tagsFollowingManage/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectFollowedTags } from 'lbry-redux'; +import { selectFollowedTags } from 'redux/selectors/tags'; import { selectSubscriptions, selectSuggestedChannels } from 'redux/selectors/subscriptions'; import { doFetchRecommendedSubscriptions } from 'redux/actions/subscriptions'; @@ -11,9 +11,6 @@ const select = state => ({ suggestedSubscriptions: selectSuggestedChannels(state), }); -export default connect( - select, - { - doFetchRecommendedSubscriptions, - } -)(TagsEdit); +export default connect(select, { + doFetchRecommendedSubscriptions, +})(TagsEdit); diff --git a/ui/reducers.js b/ui/reducers.js index 243f0e409..2dc468928 100644 --- a/ui/reducers.js +++ b/ui/reducers.js @@ -1,8 +1,9 @@ import { combineReducers } from 'redux'; import { connectRouter } from 'connected-react-router'; -import { claimsReducer, fileInfoReducer, walletReducer, tagsReducer, publishReducer } from 'lbry-redux'; +import { claimsReducer, fileInfoReducer, walletReducer, publishReducer } from 'lbry-redux'; import { costInfoReducer, blacklistReducer, filteredReducer, homepageReducer, statsReducer, webReducer } from 'lbryinc'; import appReducer from 'redux/reducers/app'; +import tagsReducer from 'redux/reducers/tags'; import contentReducer from 'redux/reducers/content'; import settingsReducer from 'redux/reducers/settings'; import subscriptionsReducer from 'redux/reducers/subscriptions'; diff --git a/ui/redux/actions/app.js b/ui/redux/actions/app.js index e4d58d21b..25fea04b8 100644 --- a/ui/redux/actions/app.js +++ b/ui/redux/actions/app.js @@ -18,12 +18,13 @@ import { doClearPublish, doPreferenceGet, doClearSupport, - selectFollowedTagsList, SHARED_PREFERENCES, DAEMON_SETTINGS, SETTINGS, } from 'lbry-redux'; +import { selectFollowedTagsList } from 'redux/selectors/tags'; import { doToast, doError, doNotificationList } from 'redux/actions/notifications'; + import Native from 'native'; import { doFetchDaemonSettings, @@ -47,7 +48,7 @@ import { import { selectDaemonSettings, makeSelectClientSetting } from 'redux/selectors/settings'; import { selectUser } from 'redux/selectors/user'; // import { selectDaemonSettings } from 'redux/selectors/settings'; -import { doSyncSubscribe } from 'redux/actions/syncwrapper'; +import { doSyncSubscribe, doSetPrefsReady } from 'redux/actions/sync'; import { doAuthenticate } from 'redux/actions/user'; import { lbrySettings as config, version as appVersion } from 'package.json'; import analytics, { SHARE_INTERNAL } from 'analytics'; @@ -90,13 +91,6 @@ export function doUpdateDownloadProgress(percent) { }; } -export function doSetSyncLock(lock) { - return { - type: ACTIONS.SET_SYNC_LOCK, - data: lock, - }; -} - export function doSkipUpgrade() { return { type: ACTIONS.SKIP_UPGRADE, @@ -340,6 +334,16 @@ export function doAlertError(errorList) { }; } +export function doAlertWaitingForSync() { + return dispatch => + dispatch( + doToast({ + message: __('Hold on, we are setting up your account'), + isError: false, + }) + ); +} + export function doDaemonReady() { return (dispatch, getState) => { const state = getState(); @@ -628,6 +632,8 @@ export function doGetAndPopulatePreferences() { }); } // @endif + } else { + dispatch(doSetPrefsReady()); } return true; } diff --git a/ui/redux/actions/blocked.js b/ui/redux/actions/blocked.js index 1e9656307..46d5ed9d5 100644 --- a/ui/redux/actions/blocked.js +++ b/ui/redux/actions/blocked.js @@ -1,9 +1,20 @@ // @flow import * as ACTIONS from 'constants/action_types'; +import { selectPrefsReady } from 'redux/selectors/sync'; +import { doAlertWaitingForSync } from 'redux/actions/app'; -export const doToggleBlockChannel = (uri: string) => ({ - type: ACTIONS.TOGGLE_BLOCK_CHANNEL, - data: { - uri, - }, -}); +export const doToggleBlockChannel = (uri: string) => (dispatch: Dispatch, getState: GetState) => { + const state = getState(); + const ready = selectPrefsReady(state); + + if (!ready) { + return dispatch(doAlertWaitingForSync()); + } + + dispatch({ + type: ACTIONS.TOGGLE_BLOCK_CHANNEL, + data: { + uri, + }, + }); +}; diff --git a/ui/redux/actions/publish.js b/ui/redux/actions/publish.js index ba9f1177c..3688db3b3 100644 --- a/ui/redux/actions/publish.js +++ b/ui/redux/actions/publish.js @@ -13,7 +13,7 @@ import { import { doError } from 'redux/actions/notifications'; import { push } from 'connected-react-router'; import analytics from 'analytics'; -import { doOpenModal } from './app'; +import { doOpenModal } from 'redux/actions/app'; export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispatch: Dispatch, getState: () => {}) => { const publishPreview = previewResponse => { diff --git a/ui/redux/actions/reactions.js b/ui/redux/actions/reactions.js index 5f0123d4a..ae494e5ee 100644 --- a/ui/redux/actions/reactions.js +++ b/ui/redux/actions/reactions.js @@ -2,7 +2,7 @@ import { Lbryio } from 'lbryinc'; import * as ACTIONS from 'constants/action_types'; import * as REACTION_TYPES from 'constants/reactions'; -import { makeSelectMyReactionForUri } from '../selectors/reactions'; +import { makeSelectMyReactionForUri } from 'redux/selectors/reactions'; import { makeSelectClaimForUri } from 'lbry-redux'; export const doFetchReactions = (claimId: string) => (dispatch: Dispatch) => { diff --git a/ui/redux/actions/settings.js b/ui/redux/actions/settings.js index 57c37c499..a174c13bd 100644 --- a/ui/redux/actions/settings.js +++ b/ui/redux/actions/settings.js @@ -5,8 +5,9 @@ import analytics from 'analytics'; import SUPPORTED_LANGUAGES from 'constants/supported_languages'; import { launcher } from 'util/autoLaunch'; import { makeSelectClientSetting } from 'redux/selectors/settings'; -import { doGetSyncDesktop, doSyncUnsubscribe } from 'redux/actions/syncwrapper'; -import { doGetAndPopulatePreferences, doSetSyncLock } from 'redux/actions/app'; +import { doGetSyncDesktop, doSyncUnsubscribe, doSetSyncLock } from 'redux/actions/sync'; +import { doAlertWaitingForSync, doGetAndPopulatePreferences } from 'redux/actions/app'; +import { selectPrefsReady } from 'redux/selectors/sync'; const { DEFAULT_LANGUAGE } = require('config'); const { SDK_SYNC_KEYS } = SHARED_PREFERENCES; @@ -57,10 +58,18 @@ export function doGetDaemonStatus() { } export function doClearDaemonSetting(key) { - return dispatch => { + return (dispatch, getState) => { + const state = getState(); + const ready = selectPrefsReady(state); + + if (!ready) { + return dispatch(doAlertWaitingForSync()); + } + const clearKey = { key, }; + // not if syncLocked Lbry.settings_clear(clearKey).then(defaultSettings => { if (SDK_SYNC_KEYS.includes(key)) { dispatch({ @@ -85,7 +94,14 @@ export function doClearDaemonSetting(key) { } // if doPopulate is applying settings, we don't want to cause a loop; doNotDispatch = true. export function doSetDaemonSetting(key, value, doNotDispatch = false) { - return dispatch => { + return (dispatch, getState) => { + const state = getState(); + const ready = selectPrefsReady(state); + + if (!ready) { + return dispatch(doAlertWaitingForSync()); + } + const newSettings = { key, value: !value && value !== false ? null : value, @@ -123,7 +139,14 @@ export function doSaveCustomWalletServers(servers) { } export function doSetClientSetting(key, value, pushPrefs) { - return dispatch => { + return (dispatch, getState) => { + const state = getState(); + const ready = selectPrefsReady(state); + + if (!ready) { + return dispatch(doAlertWaitingForSync()); + } + dispatch({ type: ACTIONS.CLIENT_SETTING_CHANGED, data: { diff --git a/ui/redux/actions/subscriptions.js b/ui/redux/actions/subscriptions.js index 56cfb6404..1b7acbbc7 100644 --- a/ui/redux/actions/subscriptions.js +++ b/ui/redux/actions/subscriptions.js @@ -5,6 +5,7 @@ import { Lbryio } from 'lbryinc'; import { doClaimRewardType } from 'redux/actions/rewards'; import { selectUnreadByChannel } from 'redux/selectors/subscriptions'; import { parseURI } from 'lbry-redux'; +import { doAlertWaitingForSync } from 'redux/actions/app'; export const doSetViewMode = (viewMode: ViewMode) => (dispatch: Dispatch) => dispatch({ @@ -123,7 +124,13 @@ export const doRemoveUnreadSubscription = (channelUri: string, readUri: string) export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dispatch, getState: GetState) => { const { settings: { daemonSettings }, + sync: { prefsReady: ready }, } = getState(); + + if (!ready) { + return dispatch(doAlertWaitingForSync()); + } + const { share_usage_data: shareSetting } = daemonSettings; const isSharingData = shareSetting || IS_WEB; @@ -153,7 +160,13 @@ export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dis export const doChannelUnsubscribe = (subscription: Subscription) => (dispatch: Dispatch, getState: GetState) => { const { settings: { daemonSettings }, + sync: { prefsReady: ready }, } = getState(); + + if (!ready) { + return dispatch(doAlertWaitingForSync()); + } + const { share_usage_data: shareSetting } = daemonSettings; const isSharingData = shareSetting || IS_WEB; diff --git a/ui/redux/actions/sync.js b/ui/redux/actions/sync.js index a816f1bb6..c4c9d5cac 100644 --- a/ui/redux/actions/sync.js +++ b/ui/redux/actions/sync.js @@ -1,6 +1,14 @@ import * as ACTIONS from 'constants/action_types'; import { Lbryio } from 'lbryinc'; -import { Lbry, doWalletEncrypt, doWalletDecrypt } from 'lbry-redux'; +import { SETTINGS, Lbry, doWalletEncrypt, doWalletDecrypt } from 'lbry-redux'; +import { selectGetSyncIsPending, selectSetSyncIsPending, selectSyncIsLocked } from 'redux/selectors/sync'; +import { makeSelectClientSetting } from 'redux/selectors/settings'; +import { getSavedPassword } from 'util/saved-passwords'; +import { doAnalyticsTagSync, doHandleSyncComplete } from 'redux/actions/app'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; + +let syncTimer = null; +const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes export function doSetDefaultAccount(success, failure) { return dispatch => { @@ -77,6 +85,52 @@ export function doSetSync(oldHash, newHash, data) { }; } +export const doGetSyncDesktop = (cb?, password) => (dispatch, getState) => { + const state = getState(); + const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); + const getSyncPending = selectGetSyncIsPending(state); + const setSyncPending = selectSetSyncIsPending(state); + const syncLocked = selectSyncIsLocked(state); + + return getSavedPassword().then(savedPassword => { + const passwordArgument = password || password === '' ? password : savedPassword === null ? '' : savedPassword; + + if (syncEnabled && !getSyncPending && !setSyncPending && !syncLocked) { + return dispatch(doGetSync(passwordArgument, cb)); + } + }); +}; + +export function doSyncSubscribe() { + return (dispatch, getState) => { + if (syncTimer) clearInterval(syncTimer); + const state = getState(); + const hasVerifiedEmail = selectUserVerifiedEmail(state); + const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); + const syncLocked = selectSyncIsLocked(state); + if (hasVerifiedEmail && syncEnabled && !syncLocked) { + dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData)))); + dispatch(doAnalyticsTagSync()); + syncTimer = setInterval(() => { + const state = getState(); + const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); + if (syncEnabled) { + dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData)))); + dispatch(doAnalyticsTagSync()); + } + }, SYNC_INTERVAL); + } + }; +} + +export function doSyncUnsubscribe() { + return dispatch => { + if (syncTimer) { + clearInterval(syncTimer); + } + }; +} + export function doGetSync(passedPassword, callback) { const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword; @@ -280,3 +334,17 @@ export function doSyncEncryptAndDecrypt(oldPassword, newPassword, encrypt) { .catch(console.error); // eslint-disable-line }; } + +export function doSetSyncLock(lock) { + return { + type: ACTIONS.SET_SYNC_LOCK, + data: lock, + }; +} + +export function doSetPrefsReady() { + return { + type: ACTIONS.SET_PREFS_READY, + data: true, + }; +} diff --git a/ui/redux/actions/syncwrapper.js b/ui/redux/actions/syncwrapper.js deleted file mode 100644 index 92e7c9764..000000000 --- a/ui/redux/actions/syncwrapper.js +++ /dev/null @@ -1,58 +0,0 @@ -// @flow -import { doGetSync } from 'redux/actions/sync'; -import { selectGetSyncIsPending, selectSetSyncIsPending } from 'redux/selectors/sync'; -import { makeSelectClientSetting } from 'redux/selectors/settings'; -import { getSavedPassword } from 'util/saved-passwords'; -import { doAnalyticsTagSync, doHandleSyncComplete } from 'redux/actions/app'; -import { selectSyncIsLocked } from 'redux/selectors/app'; -import { selectUserVerifiedEmail } from 'redux/selectors/user'; -import { SETTINGS } from 'lbry-redux'; - -let syncTimer = null; -const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes - -export const doGetSyncDesktop = (cb?: () => void, password?: string) => (dispatch: Dispatch, getState: GetState) => { - const state = getState(); - const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); - const getSyncPending = selectGetSyncIsPending(state); - const setSyncPending = selectSetSyncIsPending(state); - const syncLocked = selectSyncIsLocked(state); - - return getSavedPassword().then(savedPassword => { - const passwordArgument = password || password === '' ? password : savedPassword === null ? '' : savedPassword; - - if (syncEnabled && !getSyncPending && !setSyncPending && !syncLocked) { - return dispatch(doGetSync(passwordArgument, cb)); - } - }); -}; - -export function doSyncSubscribe() { - return (dispatch: Dispatch, getState: GetState) => { - if (syncTimer) clearInterval(syncTimer); - const state = getState(); - const hasVerifiedEmail = selectUserVerifiedEmail(state); - const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); - const syncLocked = selectSyncIsLocked(state); - if (hasVerifiedEmail && syncEnabled && !syncLocked) { - dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData)))); - dispatch(doAnalyticsTagSync()); - syncTimer = setInterval(() => { - const state = getState(); - const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); - if (syncEnabled) { - dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData)))); - dispatch(doAnalyticsTagSync()); - } - }, SYNC_INTERVAL); - } - }; -} - -export function doSyncUnsubscribe() { - return (dispatch: Dispatch) => { - if (syncTimer) { - clearInterval(syncTimer); - } - }; -} diff --git a/ui/redux/actions/tags.js b/ui/redux/actions/tags.js index e631a97ac..413f6daed 100644 --- a/ui/redux/actions/tags.js +++ b/ui/redux/actions/tags.js @@ -1,15 +1,42 @@ // @flow -import { doToggleTagFollow, selectFollowedTagsList } from 'lbry-redux'; +import { selectFollowedTagsList } from 'redux/selectors/tags'; +import { selectPrefsReady } from 'redux/selectors/sync'; +import * as ACTIONS from 'constants/action_types'; import analytics from 'analytics'; +import { doAlertWaitingForSync } from 'redux/actions/app'; export const doToggleTagFollowDesktop = (name: string) => (dispatch: Dispatch, getState: GetState) => { - dispatch(doToggleTagFollow(name)); - const state = getState(); const tags = selectFollowedTagsList(state); + const ready = selectPrefsReady(state); + if (!ready) { + return dispatch(doAlertWaitingForSync()); + } + + dispatch({ + type: ACTIONS.TOGGLE_TAG_FOLLOW, + data: { + name, + }, + }); + const stringOfTags = tags.join(','); if (stringOfTags) { analytics.apiSyncTags({ content_tags: stringOfTags }); } }; + +export const doAddTag = (name: string) => ({ + type: ACTIONS.TAG_ADD, + data: { + name, + }, +}); + +export const doDeleteTag = (name: string) => ({ + type: ACTIONS.TAG_DELETE, + data: { + name, + }, +}); diff --git a/ui/redux/reducers/app.js b/ui/redux/reducers/app.js index 883dcb297..b2fd8783a 100644 --- a/ui/redux/reducers/app.js +++ b/ui/redux/reducers/app.js @@ -42,7 +42,6 @@ export type AppState = { welcomeVersion: number, allowAnalytics: boolean, hasNavigated: boolean, - syncLocked: boolean, interestedInYoutubeSync: boolean, }; @@ -78,7 +77,6 @@ const defaultState: AppState = { welcomeVersion: 0.0, allowAnalytics: false, hasNavigated: false, - syncLocked: false, interestedInYoutubeSync: false, }; @@ -113,11 +111,6 @@ reducers[ACTIONS.DAEMON_READY] = state => daemonReady: true, }); -reducers[ACTIONS.SET_SYNC_LOCK] = (state, action) => - Object.assign({}, state, { - syncLocked: action.data, - }); - reducers[ACTIONS.PASSWORD_SAVED] = (state, action) => Object.assign({}, state, { isPasswordSaved: action.data, diff --git a/ui/redux/reducers/subscriptions.js b/ui/redux/reducers/subscriptions.js index 5c7a22f02..145676d8f 100644 --- a/ui/redux/reducers/subscriptions.js +++ b/ui/redux/reducers/subscriptions.js @@ -144,6 +144,12 @@ export default handleActions( action: { data: { subscriptions: ?Array } } ) => { const { subscriptions } = action.data; + const incomingSubscriptions = Array.isArray(subscriptions) && subscriptions.length; + if (!incomingSubscriptions) { + return { + ...state, + }; + } let newSubscriptions; if (!subscriptions) { diff --git a/ui/redux/reducers/sync.js b/ui/redux/reducers/sync.js index 2c02c5541..97d14cfc7 100644 --- a/ui/redux/reducers/sync.js +++ b/ui/redux/reducers/sync.js @@ -1,4 +1,5 @@ import * as ACTIONS from 'constants/action_types'; +import { ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux'; const reducers = {}; const defaultState = { @@ -12,15 +13,35 @@ const defaultState = { syncApplyPasswordError: false, getSyncIsPending: false, setSyncIsPending: false, + prefsReady: false, + syncLocked: false, hashChanged: false, }; +reducers[LBRY_REDUX_ACTIONS.USER_STATE_POPULATE] = state => { + const { syncReady } = state; + if (!syncReady) { + return Object.assign({}, state, { + prefsReady: true, + }); + } else { + return Object.assign({}, state); + } +}; + +reducers[ACTIONS.SET_PREFS_READY] = (state, action) => Object.assign({}, state, { prefsReady: action.data }); + reducers[ACTIONS.GET_SYNC_STARTED] = state => Object.assign({}, state, { getSyncIsPending: true, getSyncErrorMessage: null, }); +reducers[ACTIONS.SET_SYNC_LOCK] = (state, action) => + Object.assign({}, state, { + syncLocked: action.data, + }); + reducers[ACTIONS.GET_SYNC_COMPLETED] = (state, action) => Object.assign({}, state, { syncHash: action.data.syncHash, diff --git a/ui/redux/reducers/tags.js b/ui/redux/reducers/tags.js new file mode 100644 index 000000000..0001a3f8c --- /dev/null +++ b/ui/redux/reducers/tags.js @@ -0,0 +1,82 @@ +// @flow +import * as ACTIONS from 'constants/action_types'; +import { ACTIONS as LBRY_REDUX_ACTIONS, DEFAULT_KNOWN_TAGS, DEFAULT_FOLLOWED_TAGS } from 'lbry-redux'; +import { handleActions } from 'util/redux-utils'; + +function getDefaultKnownTags() { + return DEFAULT_FOLLOWED_TAGS.concat(DEFAULT_KNOWN_TAGS).reduce( + (tagsMap, tag) => ({ + ...tagsMap, + [tag]: { name: tag }, + }), + {} + ); +} + +const defaultState: TagState = { + followedTags: [], + knownTags: getDefaultKnownTags(), +}; + +export default handleActions( + { + [ACTIONS.TOGGLE_TAG_FOLLOW]: (state: TagState, action: TagAction): TagState => { + const { followedTags } = state; + const { name } = action.data; + + let newFollowedTags = followedTags.slice(); + + if (newFollowedTags.includes(name)) { + newFollowedTags = newFollowedTags.filter(tag => tag !== name); + } else { + newFollowedTags.push(name); + } + + return { + ...state, + followedTags: newFollowedTags, + }; + }, + + [ACTIONS.TAG_ADD]: (state: TagState, action: TagAction) => { + const { knownTags } = state; + const { name } = action.data; + + let newKnownTags = { ...knownTags }; + newKnownTags[name] = { name }; + + return { + ...state, + knownTags: newKnownTags, + }; + }, + + [ACTIONS.TAG_DELETE]: (state: TagState, action: TagAction) => { + const { knownTags, followedTags } = state; + const { name } = action.data; + + let newKnownTags = { ...knownTags }; + delete newKnownTags[name]; + const newFollowedTags = followedTags.filter(tag => tag !== name); + + return { + ...state, + knownTags: newKnownTags, + followedTags: newFollowedTags, + }; + }, + [LBRY_REDUX_ACTIONS.USER_STATE_POPULATE]: (state: TagState, action: { data: { tags: ?Array } }) => { + const { tags } = action.data; + if (Array.isArray(tags)) { + return { + ...state, + followedTags: tags, + }; + } + return { + ...state, + }; + }, + }, + defaultState +); diff --git a/ui/redux/selectors/app.js b/ui/redux/selectors/app.js index b1b40a9aa..305a6cc08 100644 --- a/ui/redux/selectors/app.js +++ b/ui/redux/selectors/app.js @@ -81,6 +81,4 @@ export const selectScrollStartingPosition = createSelector(selectState, state => export const selectIsPasswordSaved = createSelector(selectState, state => state.isPasswordSaved); -export const selectSyncIsLocked = createSelector(selectState, state => state.syncLocked); - export const selectInterestedInYoutubeSync = createSelector(selectState, state => state.interestedInYoutubeSync); diff --git a/ui/redux/selectors/sync.js b/ui/redux/selectors/sync.js index 111f01472..f05f6a8ba 100644 --- a/ui/redux/selectors/sync.js +++ b/ui/redux/selectors/sync.js @@ -23,3 +23,7 @@ export const selectSyncApplyIsPending = createSelector(selectState, state => sta export const selectSyncApplyErrorMessage = createSelector(selectState, state => state.syncApplyErrorMessage); export const selectSyncApplyPasswordError = createSelector(selectState, state => state.syncApplyPasswordError); + +export const selectSyncIsLocked = createSelector(selectState, state => state.syncLocked); + +export const selectPrefsReady = createSelector(selectState, state => state.prefsReady); diff --git a/ui/redux/selectors/tags.js b/ui/redux/selectors/tags.js new file mode 100644 index 000000000..c75fbaa39 --- /dev/null +++ b/ui/redux/selectors/tags.js @@ -0,0 +1,36 @@ +// @flow +import { createSelector } from 'reselect'; + +const selectState = (state: { tags: TagState }) => state.tags || {}; + +export const selectKnownTagsByName = createSelector(selectState, (state: TagState): KnownTags => state.knownTags); + +export const selectFollowedTagsList = createSelector(selectState, (state: TagState): Array => + state.followedTags.filter(tag => typeof tag === 'string') +); + +export const selectFollowedTags = createSelector(selectFollowedTagsList, (followedTags: Array): Array => + followedTags.map(tag => ({ name: tag.toLowerCase() })).sort((a, b) => a.name.localeCompare(b.name)) +); + +export const selectUnfollowedTags = createSelector( + selectKnownTagsByName, + selectFollowedTagsList, + (tagsByName: KnownTags, followedTags: Array): Array => { + const followedTagsSet = new Set(followedTags); + let tagsToReturn = []; + Object.keys(tagsByName).forEach(key => { + if (!followedTagsSet.has(key)) { + const { name } = tagsByName[key]; + tagsToReturn.push({ name: name.toLowerCase() }); + } + }); + + return tagsToReturn; + } +); + +export const makeSelectIsFollowingTag = (tag: string) => + createSelector(selectFollowedTags, followedTags => { + return followedTags.some(followedTag => followedTag.name === tag.toLowerCase()); + }); diff --git a/ui/store.js b/ui/store.js index 44fe11a35..5917b67b8 100644 --- a/ui/store.js +++ b/ui/store.js @@ -10,7 +10,7 @@ import { createMemoryHistory, createBrowserHistory } from 'history'; import { routerMiddleware } from 'connected-react-router'; import createRootReducer from './reducers'; import { Lbry, buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux'; -import { doSyncSubscribe } from 'redux/actions/syncwrapper'; +import { doSyncSubscribe } from 'redux/actions/sync'; import { getAuthToken } from 'util/saved-passwords'; import { generateInitialUrl } from 'util/url'; import { X_LBRY_AUTH_TOKEN } from 'constants/token'; @@ -120,7 +120,7 @@ const triggerSharedStateActions = [ ACTIONS.CHANNEL_SUBSCRIBE, ACTIONS.CHANNEL_UNSUBSCRIBE, ACTIONS.TOGGLE_BLOCK_CHANNEL, - LBRY_REDUX_ACTIONS.TOGGLE_TAG_FOLLOW, + ACTIONS.TOGGLE_TAG_FOLLOW, LBRY_REDUX_ACTIONS.CREATE_CHANNEL_COMPLETED, ACTIONS.SYNC_CLIENT_SETTINGS, // Disabled until we can overwrite preferences