Skip to content

Commit aa734b9

Browse files
tzarebczanmiko
andauthored
release recommendations to all users (#3116)
* feat: release recommendations to all users * fix: missed one spot * fix: homepage section * fix: Insert FYP after Following if placement not defined * Show modal when FYP becomes available on account * Minor string change * fix messaging on FYP info page --------- Co-authored-by: miko <sauce47@posteo.net>
1 parent d92f456 commit aa734b9

File tree

15 files changed

+127
-67
lines changed

15 files changed

+127
-67
lines changed

static/app-strings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@
190190
"This file has been shared with you by other people.": "This file has been shared with you by other people.",
191191
"Odysee is not responsible for its content, click continue to proceed at your own risk.": "Odysee is not responsible for its content, click continue to proceed at your own risk.",
192192
"Yes": "Yes",
193+
"Yes!": "Yes!",
193194
"No": "No",
194195
"These search results are provided by Odysee.": "These search results are provided by Odysee.",
195196
"Files": "Files",
@@ -2980,6 +2981,8 @@
29802981
"Failed to apply membership tiers. Access to the content may still be restricted.": "Failed to apply membership tiers. Access to the content may still be restricted.",
29812982
"If the issue persists, please reach out to help@odysee.com": "If the issue persists, please reach out to help@odysee.com",
29822983
"Creator only": "Creator only",
2984+
"Would you like to enable them? Homepage recommendations placement can be configured from the homepage customization.": "Would you like to enable them? Homepage recommendations placement can be configured from the homepage customization.",
2985+
"Homepage recommendations available": "Homepage recommendations available",
29832986

29842987
"--end--": "--end--"
29852988
}

ui/component/app/index.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { hot } from 'react-hot-loader/root';
22
import { connect } from 'react-redux';
3+
import * as SETTINGS from 'constants/settings';
34
import {
45
selectGetSyncErrorMessage,
56
selectPrefsReady,
@@ -17,12 +18,20 @@ import {
1718
selectThemePath,
1819
selectDefaultChannelClaim,
1920
selectHomepageAnnouncement,
21+
selectClientSetting,
2022
} from 'redux/selectors/settings';
2123
import { selectModal, selectActiveChannelClaim } from 'redux/selectors/app';
2224
import { selectUploadCount } from 'redux/selectors/publish';
23-
import { doOpenAnnouncements, doSetLanguage, doSetDefaultChannel, doFetchLanguage } from 'redux/actions/settings';
25+
import { selectPersonalRecommendations } from 'redux/selectors/search';
26+
import {
27+
doOpenAnnouncements,
28+
doSetLanguage,
29+
doSetDefaultChannel,
30+
doFetchLanguage,
31+
doSetClientSetting,
32+
} from 'redux/actions/settings';
2433
import { doSyncLoop } from 'redux/actions/sync';
25-
import { doSignIn, doSetIncognito, doSetAssignedLbrynetServer } from 'redux/actions/app';
34+
import { doSignIn, doSetIncognito, doSetAssignedLbrynetServer, doOpenModal } from 'redux/actions/app';
2635
import { doFetchModBlockedList, doFetchCommentModAmIList } from 'redux/actions/comments';
2736
import App from './view';
2837

@@ -45,6 +54,9 @@ const select = (state) => ({
4554
myChannelClaimIds: selectMyChannelClaimIds(state),
4655
defaultChannelClaim: selectDefaultChannelClaim(state),
4756
announcement: selectHomepageAnnouncement(state),
57+
homepageOrder: selectClientSetting(state, SETTINGS.HOMEPAGE_ORDER),
58+
isFypModalShown: selectClientSetting(state, SETTINGS.FYP_MODAL_SHOWN),
59+
personalRecommendations: selectPersonalRecommendations(state),
4860
});
4961

5062
const perform = {
@@ -60,6 +72,8 @@ const perform = {
6072
doSetLastViewedAnnouncement,
6173
doSetDefaultChannel,
6274
doSetAssignedLbrynetServer,
75+
doOpenModal,
76+
doSetClientSetting,
6377
};
6478

6579
export default hot(connect(select, perform)(App));

ui/component/app/view.jsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// @flow
22
import * as PAGES from 'constants/pages';
3+
import * as MODALS from 'constants/modal_types';
4+
import * as SETTINGS from 'constants/settings';
35
import React, { useEffect, useState } from 'react';
46
import { lazyImport } from 'util/lazyImport';
57
import { tusUnlockAndNotify, tusHandleTabUpdates } from 'util/tus';
@@ -57,6 +59,8 @@ const LATEST_PATH = `/$/${PAGES.LATEST}/`;
5759
const LIVE_PATH = `/$/${PAGES.LIVE_NOW}/`;
5860
const EMBED_PATH = `/$/${PAGES.EMBED}/`;
5961

62+
type HomepageOrder = { active: ?Array<string>, hidden: ?Array<string> };
63+
6064
type Props = {
6165
language: string,
6266
languages: Array<string>,
@@ -89,10 +93,15 @@ type Props = {
8993
defaultChannelClaim: ?any,
9094
nagsShown: boolean,
9195
announcement: string,
96+
homepageOrder: HomepageOrder,
97+
isFypModalShown: boolean,
98+
personalRecommendations: { gid: string, uris: Array<string>, fetched: boolean },
9299
doOpenAnnouncements: () => void,
93100
doSetLastViewedAnnouncement: (hash: string) => void,
94101
doSetDefaultChannel: (claimId: string) => void,
95102
doSetAssignedLbrynetServer: (server: string) => void,
103+
doOpenModal: (id: string, ?{}) => void,
104+
doSetClientSetting: (string, boolean, ?boolean) => void,
96105
};
97106

98107
export const AppContext = React.createContext<any>();
@@ -127,10 +136,15 @@ function App(props: Props) {
127136
fetchModAmIList,
128137
defaultChannelClaim,
129138
announcement,
139+
homepageOrder,
140+
isFypModalShown,
141+
personalRecommendations,
130142
doOpenAnnouncements,
131143
doSetLastViewedAnnouncement,
132144
doSetDefaultChannel,
133145
doSetAssignedLbrynetServer,
146+
doOpenModal,
147+
doSetClientSetting,
134148
} = props;
135149

136150
const isMobile = useIsMobile();
@@ -335,6 +349,51 @@ function App(props: Props) {
335349
}
336350
}, [activeChannelClaim, defaultChannelClaim, doSetDefaultChannel, hasMyChannels, prefsReady]);
337351

352+
useEffect(() => {
353+
if (
354+
isFypModalShown ||
355+
!prefsReady ||
356+
// $FlowIgnore
357+
homepageOrder.active?.includes('FYP') ||
358+
// $FlowIgnore
359+
homepageOrder.hidden?.includes('FYP') ||
360+
!personalRecommendations.uris.length
361+
) {
362+
return;
363+
}
364+
365+
doOpenModal(MODALS.CONFIRM, {
366+
title: __('Homepage recommendations available'),
367+
subtitle: __(
368+
'Would you like to enable them? Homepage recommendations placement can be configured from the homepage customization.'
369+
),
370+
labelOk: __('Yes!'),
371+
labelCancel: __('Customize'),
372+
onConfirm: (closeModal) => {
373+
closeModal();
374+
375+
const newHomePageOrder = {
376+
...homepageOrder,
377+
active: homepageOrder?.active ? ['FYP', ...homepageOrder.active] : ['FYP'],
378+
};
379+
doSetClientSetting(SETTINGS.HOMEPAGE_ORDER, newHomePageOrder, true);
380+
doSetClientSetting(SETTINGS.FYP_MODAL_SHOWN, true, true);
381+
},
382+
onCancel: (closeModal) => {
383+
closeModal();
384+
385+
const newHomePageOrder = {
386+
...homepageOrder,
387+
hidden: homepageOrder?.hidden ? ['FYP', ...homepageOrder.hidden] : ['FYP'],
388+
};
389+
doSetClientSetting(SETTINGS.HOMEPAGE_ORDER, newHomePageOrder, true);
390+
doSetClientSetting(SETTINGS.FYP_MODAL_SHOWN, true, true);
391+
392+
doOpenModal(MODALS.CUSTOMIZE_HOMEPAGE);
393+
},
394+
});
395+
}, [isFypModalShown, prefsReady, homepageOrder, personalRecommendations, doSetClientSetting, doOpenModal]);
396+
338397
useEffect(() => {
339398
// $FlowFixMe
340399
document.documentElement.setAttribute('lang', language);

ui/component/homepageSort/index.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ import { connect } from 'react-redux';
22
import HomepageSort from './view';
33
import * as SETTINGS from 'constants/settings';
44
import { selectClientSetting, selectHomepageData } from 'redux/selectors/settings';
5-
import { hasLegacyOdyseePremium } from '../../redux/selectors/user';
65

76
const select = (state) => ({
87
homepageData: selectHomepageData(state) || {},
98
homepageOrder: selectClientSetting(state, SETTINGS.HOMEPAGE_ORDER),
10-
userHasOdyseeMembership: hasLegacyOdyseePremium(state),
119
});
1210

1311
export default connect(select)(HomepageSort);

ui/component/homepageSort/view.jsx

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const move = (source, destination, droppableSource, droppableDestination) => {
4141
};
4242
};
4343

44-
function getInitialList(listId, savedOrder, homepageSections, userHasOdyseeMembership) {
44+
function getInitialList(listId, savedOrder, homepageSections) {
4545
const savedActiveOrder = savedOrder.active || [];
4646
const savedHiddenOrder = savedOrder.hidden || [];
4747
const sectionKeys = Object.keys(homepageSections);
@@ -71,6 +71,9 @@ function getInitialList(listId, savedOrder, homepageSections, userHasOdyseeMembe
7171
let followingIndex = activeOrder.indexOf('FOLLOWING');
7272
if (followingIndex !== -1) activeOrder.splice(followingIndex + 1, 0, key);
7373
else activeOrder.push(key);
74+
} else if (key === 'FYP') {
75+
// Default FYP to hidden
76+
hiddenOrder = [key, ...hiddenOrder];
7477
} else {
7578
activeOrder.push(key);
7679
}
@@ -80,15 +83,6 @@ function getInitialList(listId, savedOrder, homepageSections, userHasOdyseeMembe
8083

8184
activeOrder = activeOrder.filter((x) => !hiddenOrder.includes(x));
8285

83-
if (!userHasOdyseeMembership) {
84-
if (activeOrder.indexOf('FYP') !== -1) {
85-
activeOrder.splice(activeOrder.indexOf('FYP'), 1);
86-
}
87-
if (hiddenOrder.indexOf('FYP') !== -1) {
88-
hiddenOrder.splice(hiddenOrder.indexOf('FYP'), 1);
89-
}
90-
}
91-
9286
return listId === 'ACTIVE' ? activeOrder : hiddenOrder;
9387
}
9488

@@ -103,20 +97,15 @@ type Props = {
10397
// --- redux:
10498
homepageData: any,
10599
homepageOrder: HomepageOrder,
106-
userHasOdyseeMembership: boolean,
107100
};
108101

109102
export default function HomepageSort(props: Props) {
110-
const { onUpdate, homepageData, homepageOrder, userHasOdyseeMembership } = props;
103+
const { onUpdate, homepageData, homepageOrder } = props;
111104
const { categories } = homepageData;
112105

113106
const SECTIONS = { ...NON_CATEGORY, ...categories };
114-
const [listActive, setListActive] = useState(() =>
115-
getInitialList('ACTIVE', homepageOrder, SECTIONS, userHasOdyseeMembership)
116-
);
117-
const [listHidden, setListHidden] = useState(() =>
118-
getInitialList('HIDDEN', homepageOrder, SECTIONS, userHasOdyseeMembership)
119-
);
107+
const [listActive, setListActive] = useState(() => getInitialList('ACTIVE', homepageOrder, SECTIONS));
108+
const [listHidden, setListHidden] = useState(() => getInitialList('HIDDEN', homepageOrder, SECTIONS));
120109

121110
const BINS = {
122111
ACTIVE: { id: 'ACTIVE', title: 'Active', list: listActive, setList: setListActive },

ui/component/recommendedPersonal/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { connect } from 'react-redux';
22
import { doFetchPersonalRecommendations } from 'redux/actions/search';
33
import { selectPersonalRecommendations } from 'redux/selectors/search';
4-
import { hasLegacyOdyseePremium, selectUser } from 'redux/selectors/user';
4+
import { selectUser } from 'redux/selectors/user';
55

66
import RecommendedPersonal from './view';
77

@@ -10,7 +10,6 @@ const select = (state) => {
1010
return {
1111
userId: user && user.id,
1212
personalRecommendations: selectPersonalRecommendations(state),
13-
userHasOdyseeMembership: hasLegacyOdyseePremium(state),
1413
};
1514
};
1615

ui/component/recommendedPersonal/view.jsx

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,11 @@ type Props = {
2929
// --- redux ---
3030
userId: ?string,
3131
personalRecommendations: { gid: string, uris: Array<string>, fetched: boolean },
32-
userHasOdyseeMembership: ?boolean,
3332
doFetchPersonalRecommendations: () => void,
3433
};
3534

3635
export default function RecommendedPersonal(props: Props) {
37-
const { header, onLoad, userId, personalRecommendations, userHasOdyseeMembership, doFetchPersonalRecommendations } =
38-
props;
36+
const { header, onLoad, userId, personalRecommendations, doFetchPersonalRecommendations } = props;
3937

4038
const ref = React.useRef();
4139
const [markedGid, setMarkedGid] = React.useState('');
@@ -100,15 +98,13 @@ export default function RecommendedPersonal(props: Props) {
10098

10199
React.useEffect(() => {
102100
// -- Fetch FYP
103-
if (userHasOdyseeMembership) {
104-
doFetchPersonalRecommendations();
105-
}
106-
}, [userHasOdyseeMembership, doFetchPersonalRecommendations]);
101+
doFetchPersonalRecommendations();
102+
}, [doFetchPersonalRecommendations]);
107103

108104
// **************************************************************************
109105
// **************************************************************************
110106

111-
if (userHasOdyseeMembership === undefined || !personalRecommendations.fetched) {
107+
if (!personalRecommendations.fetched) {
112108
return (
113109
<>
114110
{header}
@@ -128,21 +124,6 @@ export default function RecommendedPersonal(props: Props) {
128124
);
129125
}
130126

131-
if (!userHasOdyseeMembership) {
132-
return (
133-
<div>
134-
{header}
135-
<div className="empty empty--centered-tight">
136-
<I18nMessage
137-
tokens={{ learn_more: <Button button="link" navigate={`/$/${PAGES.FYP}`} label={__('learn more')} /> }}
138-
>
139-
Premium membership required. Become a member, or %learn_more%.
140-
</I18nMessage>
141-
</div>
142-
</div>
143-
);
144-
}
145-
146127
if (count < 1) {
147128
return (
148129
<div>
@@ -151,7 +132,8 @@ export default function RecommendedPersonal(props: Props) {
151132
<I18nMessage
152133
tokens={{ learn_more: <Button button="link" navigate={`/$/${PAGES.FYP}`} label={__('Learn More')} /> }}
153134
>
154-
No recommendations available at the moment. %learn_more%
135+
No recommendations available at the moment. Typically this means you need to view more content first.
136+
%learn_more%
155137
</I18nMessage>
156138
</div>
157139
</div>

ui/constants/settings.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const SHORTS_ASPECT_RATIO_LIMIT = '1';
5858
export const SHORTS_ASPECT_RATIO_LTE = '0.95';
5959
export const SHORTS_DURATION_LTE = '180';
6060
export const DISABLE_SHORTS_VIEW = 'disable_shorts_view';
61+
export const FYP_MODAL_SHOWN = 'fyp_modal_shown';
6162

6263
export const SETTINGS_GRP = {
6364
APPEARANCE: 'appearance',

ui/constants/shared_preferences.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ export const CLIENT_SYNC_KEYS = [
4343
SETTINGS.UPLOAD_PAGE_FILTERING,
4444
SETTINGS.CRYPTO_DISCLAIMERS,
4545
SETTINGS.DISABLE_SHORTS_VIEW,
46+
SETTINGS.FYP_MODAL_SHOWN,
4647
];

ui/modal/modalConfirm/view.jsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,25 @@ type Props = {
1818
busyMsg?: string,
1919
checkboxLabel?: string,
2020
onConfirm: (closeModal: () => void, setIsBusy: (boolean) => void) => void,
21+
onCancel: (closeModal: () => void, setIsBusy: (boolean) => void) => void,
2122
// --- perform ---
2223
doHideModal: () => void,
2324
};
2425

2526
export default function ModalConfirm(props: Props) {
26-
const { title, subtitle, body, labelOk, labelCancel, hideCancel, busyMsg, checkboxLabel, onConfirm, doHideModal } =
27-
props;
27+
const {
28+
title,
29+
subtitle,
30+
body,
31+
labelOk,
32+
labelCancel,
33+
hideCancel,
34+
busyMsg,
35+
checkboxLabel,
36+
onConfirm,
37+
onCancel,
38+
doHideModal,
39+
} = props;
2840

2941
const [isBusy, setIsBusy] = React.useState(false);
3042
const [isChecked, setIsChecked] = React.useState(!checkboxLabel);
@@ -35,6 +47,14 @@ export default function ModalConfirm(props: Props) {
3547
}
3648
}
3749

50+
function handleOnCancel() {
51+
if (onCancel) {
52+
onCancel(doHideModal, setIsBusy);
53+
} else {
54+
doHideModal();
55+
}
56+
}
57+
3858
return (
3959
<Modal isOpen type="custom" width="wide">
4060
<Card
@@ -67,7 +87,7 @@ export default function ModalConfirm(props: Props) {
6787
)}
6888

6989
{!hideCancel && !(isBusy && busyMsg) && (
70-
<Button button="link" label={labelCancel || __('Cancel')} disabled={isBusy} onClick={doHideModal} />
90+
<Button button="link" label={labelCancel || __('Cancel')} disabled={isBusy} onClick={handleOnCancel} />
7191
)}
7292
</div>
7393
</>

0 commit comments

Comments
 (0)