Debug and fix courses watching progress resolution

This commit is contained in:
Adolfo Reyna
2026-02-20 20:52:26 -05:00
parent 058b060d03
commit 83604f5eaa
3 changed files with 106 additions and 19 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
} }