Debug and fix courses watching progress resolution
This commit is contained in:
107
Views/Courses.js
107
Views/Courses.js
@@ -8,6 +8,11 @@ import GlobalState from '../contexts/GlobalState.js';
|
|||||||
import i18n from "../i18nMessages.js";
|
import i18n from "../i18nMessages.js";
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
|
||||||
|
const COURSES_LOG_PREFIX = '[Courses]';
|
||||||
|
const logCourses = (...args) => {
|
||||||
|
if (__DEV__) console.log(COURSES_LOG_PREFIX, ...args);
|
||||||
|
};
|
||||||
|
|
||||||
const emptyCoursesData = {
|
const emptyCoursesData = {
|
||||||
courses: [],
|
courses: [],
|
||||||
popular: [],
|
popular: [],
|
||||||
@@ -17,15 +22,42 @@ const emptyCoursesData = {
|
|||||||
const getCourses = async (profileObj = {}) => {
|
const getCourses = async (profileObj = {}) => {
|
||||||
let courses = [];
|
let courses = [];
|
||||||
let popular = [];
|
let popular = [];
|
||||||
|
logCourses('getCourses:start', {
|
||||||
|
viewerId: profileObj?._id || null,
|
||||||
|
hasViewerData: !!profileObj?.data,
|
||||||
|
});
|
||||||
await API.getCourses().then((data) => {
|
await API.getCourses().then((data) => {
|
||||||
const groups = Array.isArray(data?.groups) ? data.groups : [];
|
const groups = Array.isArray(data?.groups) ? data.groups : [];
|
||||||
courses = groups;
|
courses = groups;
|
||||||
popular = [...groups].sort((a, b) => {
|
popular = [...groups].sort((a, b) => {
|
||||||
return Object.keys(b.subscribed).length - Object.keys(a.subscribed).length;
|
return Object.keys(b.subscribed).length - Object.keys(a.subscribed).length;
|
||||||
});
|
});
|
||||||
|
logCourses('getCourses:groups', {
|
||||||
|
groups: groups.length,
|
||||||
|
popular: popular.length,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
let watching = {};
|
let watching = {};
|
||||||
let watchingProms = [];
|
let watchingProms = [];
|
||||||
|
let watchingStats = {
|
||||||
|
rawEntries: 0,
|
||||||
|
explicitProfileId: 0,
|
||||||
|
fallbackByPost: 0,
|
||||||
|
skippedNoProgress: 0,
|
||||||
|
skippedNoProfileId: 0,
|
||||||
|
courseProfilesResolved: 0,
|
||||||
|
};
|
||||||
|
const pushWatchingProgress = (profile, progress, profileId) => {
|
||||||
|
if (!profile?.isCourse) return;
|
||||||
|
if (!watching[profileId]) {
|
||||||
|
watching[profileId] = { profile, progress: [], mostRecent: 0 };
|
||||||
|
}
|
||||||
|
if (watching[profileId].mostRecent < (progress?.ts || 0)) {
|
||||||
|
watching[profileId].mostRecent = progress.ts || 0;
|
||||||
|
}
|
||||||
|
watching[profileId].progress.push(progress);
|
||||||
|
watchingStats.courseProfilesResolved += 1;
|
||||||
|
};
|
||||||
let viewerData = {};
|
let viewerData = {};
|
||||||
try {
|
try {
|
||||||
// Detach potential proxy/non-extensible objects before iterating keys.
|
// Detach potential proxy/non-extensible objects before iterating keys.
|
||||||
@@ -33,20 +65,44 @@ const getCourses = async (profileObj = {}) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
viewerData = {};
|
viewerData = {};
|
||||||
}
|
}
|
||||||
|
const viewerKeys = Object.keys(viewerData);
|
||||||
|
logCourses('watching:viewerData', {
|
||||||
|
entries: viewerKeys.length,
|
||||||
|
sampleKeys: viewerKeys.slice(0, 5),
|
||||||
|
});
|
||||||
Object.keys(viewerData).forEach((videoId) => {
|
Object.keys(viewerData).forEach((videoId) => {
|
||||||
|
watchingStats.rawEntries += 1;
|
||||||
const progress = viewerData[videoId];
|
const progress = viewerData[videoId];
|
||||||
if (progress?.profileId) {
|
if (!progress) {
|
||||||
let profileId = progress.profileId;
|
watchingStats.skippedNoProgress += 1;
|
||||||
watchingProms.push(API.getUserProfile(profileId).then(profile => {
|
return;
|
||||||
if (!profile.isCourse)
|
|
||||||
return 0;
|
|
||||||
if (!watching[profileId])
|
|
||||||
watching[profileId] = { profile, progress: [], mostRecent: 0 };
|
|
||||||
if (watching[profileId].mostRecent < progress.ts)
|
|
||||||
watching[profileId].mostRecent = progress.ts;
|
|
||||||
watching[profileId].progress.push(progress);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const explicitProfileId = progress.profileId || progress.profileid;
|
||||||
|
if (explicitProfileId) {
|
||||||
|
watchingStats.explicitProfileId += 1;
|
||||||
|
watchingProms.push(API.getUserProfile(explicitProfileId).then((profile) => {
|
||||||
|
pushWatchingProgress(profile, progress, explicitProfileId);
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backward compatibility: old progress entries may not include profileId.
|
||||||
|
const postId = progress.postId || videoId;
|
||||||
|
if (!postId) {
|
||||||
|
watchingStats.skippedNoProfileId += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
watchingStats.fallbackByPost += 1;
|
||||||
|
watchingProms.push(
|
||||||
|
API.getPost(postId).then((post) => {
|
||||||
|
const fallbackProfileId = post?.profileid;
|
||||||
|
if (!fallbackProfileId) return 0;
|
||||||
|
return API.getUserProfile(fallbackProfileId).then((profile) => {
|
||||||
|
pushWatchingProgress(profile, progress, fallbackProfileId);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
let watchingArray = [];
|
let watchingArray = [];
|
||||||
await Promise.all(watchingProms).then(() => {
|
await Promise.all(watchingProms).then(() => {
|
||||||
@@ -57,6 +113,10 @@ const getCourses = async (profileObj = {}) => {
|
|||||||
return b.mostRecent - a.mostRecent;
|
return b.mostRecent - a.mostRecent;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
logCourses('watching:resolved', {
|
||||||
|
...watchingStats,
|
||||||
|
uniqueCourses: watchingArray.length,
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
courses: courses.slice(0, 10),
|
courses: courses.slice(0, 10),
|
||||||
popular: popular.slice(0, 10),
|
popular: popular.slice(0, 10),
|
||||||
@@ -97,14 +157,22 @@ const Courses = () => {
|
|||||||
let subscribed = true;
|
let subscribed = true;
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const cached = await getCoursesCache();
|
const cached = await getCoursesCache();
|
||||||
console.log("Courses Cache");
|
logCourses('cache:read', {
|
||||||
|
courses: Array.isArray(cached?.courses) ? cached.courses.length : 0,
|
||||||
|
popular: Array.isArray(cached?.popular) ? cached.popular.length : 0,
|
||||||
|
watching: Array.isArray(cached?.watching) ? cached.watching.length : 0,
|
||||||
|
});
|
||||||
if (subscribed) {
|
if (subscribed) {
|
||||||
setGroups(Array.isArray(cached?.courses) ? cached.courses : []);
|
setGroups(Array.isArray(cached?.courses) ? cached.courses : []);
|
||||||
setPopular(Array.isArray(cached?.popular) ? cached.popular : []);
|
setPopular(Array.isArray(cached?.popular) ? cached.popular : []);
|
||||||
setWatching(Array.isArray(cached?.watching) ? cached.watching : []);
|
setWatching(Array.isArray(cached?.watching) ? cached.watching : []);
|
||||||
}
|
}
|
||||||
let r = await getCourses(viewer);
|
let r = await getCourses(viewer);
|
||||||
console.log("Courses Live");
|
logCourses('live:resolved', {
|
||||||
|
courses: Array.isArray(r?.courses) ? r.courses.length : 0,
|
||||||
|
popular: Array.isArray(r?.popular) ? r.popular.length : 0,
|
||||||
|
watching: Array.isArray(r?.watching) ? r.watching.length : 0,
|
||||||
|
});
|
||||||
if(subscribed){
|
if(subscribed){
|
||||||
setGroups(r.courses || []);
|
setGroups(r.courses || []);
|
||||||
setPopular(r.popular || []);
|
setPopular(r.popular || []);
|
||||||
@@ -116,7 +184,7 @@ const Courses = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
subscribed = false;
|
subscribed = false;
|
||||||
}
|
}
|
||||||
}, [])
|
}, [viewer])
|
||||||
|
|
||||||
const onChangeSearch = query => {
|
const onChangeSearch = query => {
|
||||||
setSearchQuery(query);
|
setSearchQuery(query);
|
||||||
@@ -151,6 +219,17 @@ const Courses = () => {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
logCourses('render', {
|
||||||
|
searchQueryLength: searchQuery.length,
|
||||||
|
groups: groups.length,
|
||||||
|
popular: popular.length,
|
||||||
|
watching: watching.length,
|
||||||
|
showWatching: searchQuery.length === 0 && watching.length > 0,
|
||||||
|
});
|
||||||
|
}, [searchQuery, groups.length, popular.length, watching.length]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
<Searchbar
|
<Searchbar
|
||||||
|
|||||||
@@ -106,7 +106,13 @@ let Media = (props) => {
|
|||||||
const renderVideo = () => {
|
const renderVideo = () => {
|
||||||
if (videosFiles.length && !props.skiptVideo) {
|
if (videosFiles.length && !props.skiptVideo) {
|
||||||
return loaded ? (
|
return loaded ? (
|
||||||
<VideoPlayer videosFiles={videosFiles} poster={poster} videoId={videosId[1]} />
|
<VideoPlayer
|
||||||
|
videosFiles={videosFiles}
|
||||||
|
poster={poster}
|
||||||
|
postId={props.postId}
|
||||||
|
profileId={props.post?.profileid}
|
||||||
|
videoId={videosId[1]}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TouchableHighlight onPress={() => setLoaded(true)}>
|
<TouchableHighlight onPress={() => setLoaded(true)}>
|
||||||
<Image source={poster ? { uri: poster } : {}} key={poster} style={styles.poster} cachePolicy="memory-disk" />
|
<Image source={poster ? { uri: poster } : {}} key={poster} style={styles.poster} cachePolicy="memory-disk" />
|
||||||
@@ -123,7 +129,7 @@ let Media = (props) => {
|
|||||||
const renderHlsVideo = () => {
|
const renderHlsVideo = () => {
|
||||||
if (hlsUrl && !props.skiptVideo) {
|
if (hlsUrl && !props.skiptVideo) {
|
||||||
return loaded ? (
|
return loaded ? (
|
||||||
<VideoPlayer videoUrl={hlsUrl} postId={props.postId} />
|
<VideoPlayer videoUrl={hlsUrl} postId={props.postId} profileId={props.post?.profileid} />
|
||||||
) : (
|
) : (
|
||||||
<TouchableHighlight onPress={() => {
|
<TouchableHighlight onPress={() => {
|
||||||
GlobalState.currentMedia = hlsUrl;
|
GlobalState.currentMedia = hlsUrl;
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ const VideoPlayer = ({ videosFiles, postId, profileId, poster, videoUrl, videoSt
|
|||||||
postId: postId,
|
postId: postId,
|
||||||
profileId: profileId,
|
profileId: profileId,
|
||||||
};
|
};
|
||||||
//console.log(postId, newData);
|
if (!GlobalState.me.data) GlobalState.me.data = {};
|
||||||
|
// Keep local viewer progress in sync so Courses can render immediately.
|
||||||
|
GlobalState.me.data[postId] = newData;
|
||||||
API.setDataValue(postId, newData);
|
API.setDataValue(postId, newData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user