From 40b7deb8073677b2518906acb444bca19081df44 Mon Sep 17 00:00:00 2001 From: Alejandro Romero Herrera Date: Sat, 5 Sep 2020 18:21:18 +0300 Subject: [PATCH] Fix #974 Extra infromation on Profile Shows information on user profile - [x] Location with link to map pin - [x] Accepted HowTos - [x] Accepted Events User stats are computed via firebase function triggered by database changes --- functions/src/Integrations/user-stats.ts | 81 ++++++++++++++++++++ functions/src/index.ts | 3 + src/models/user.models.tsx | 6 ++ src/pages/User/content/UserPage/UserPage.tsx | 45 +++++++---- 4 files changed, 121 insertions(+), 14 deletions(-) create mode 100644 functions/src/Integrations/user-stats.ts diff --git a/functions/src/Integrations/user-stats.ts b/functions/src/Integrations/user-stats.ts new file mode 100644 index 0000000000..31ef8fb817 --- /dev/null +++ b/functions/src/Integrations/user-stats.ts @@ -0,0 +1,81 @@ +import * as functions from 'firebase-functions' +import { db } from '../Firebase/firestoreDB' + + +export const countHowTos = functions.firestore + .document('v3_howtos/{id}') + .onWrite(async (change, context) => { + + updateStats(change, 'v3_howtos', 'howToCount') + + }) + +export const countEvents = functions.firestore + .document('v3_events/{id}') + .onWrite(async (change, context) => { + + updateStats(change, 'v3_events', 'eventCount') + + }) + +function updateStats(change, collection, target){ + + const info = change.after.exists ? change.after.data() : null + const prevInfo = change.before.exists ? change.before.data() : null + const userStatsRef = db.collection('v3_users/'); + + let delta = 0 + + if (info !== null && info.moderation === 'accepted' && prevInfo !== null && prevInfo.Moderation !== 'accepted') { // Increment if now accepted and previously different + delta = 1 + } else if (prevInfo !== null && prevInfo.moderation === 'accepted' && (info === null || info.moderation !== 'accepted')) { // Decrement if previously accepted and now erased or moderation changed + delta = -1 + } else { + return null + } + + console.log('Update ', collection, ' delta: ', delta ) + + return userStatsRef.doc(info._createdBy).update({'_stats':{ + [target]: admin.firestore.FieldValue.increment(delta) + }}).catch(e => { + console.log(e); + // In case stats for user are inexistent we compute from all his records, Only triggers if no user exist (new Collection) + // computeUserStats(info._createdBy) + }) +} + +// Compute one user stats +function computeUserStats(owner){ + + const userStatsRef = db.collection('v3_users/') + + db.collection('v3_howtos').where("_createdBy", "==", owner).where("moderation", "==", "accepted") + .get() + .then(querySnapshot => { + let count = 0; + querySnapshot.forEach(doc => { + count++; + }); + console.log('Accepted howtos for', owner ,',count: ', count); + userStatsRef.doc(owner).set({'_stats':{ + howToCount: count + }}, {merge: true}); + return null + }).catch(() => null); + + db.collection('v3_events').where("_createdBy", "==", owner).where("moderation", "==", "accepted") + .get() + .then(querySnapshot => { + let count = 0; + querySnapshot.forEach(doc => { + count++; + }); + console.log('Accepted events for', owner ,',count: ', count); + userStatsRef.doc(owner).set({'_stats':{ + eventCount: count + }}, {merge: true}); + return null + }).catch(() => null); +} + diff --git a/functions/src/index.ts b/functions/src/index.ts index fa9c8b086e..44ae4dd0ad 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -5,6 +5,7 @@ import * as IntegrationsSlack from './Integrations/firebase-slack' import * as IntegrationsDiscord from './Integrations/firebase-discord' import { FirebaseUserBackup } from './Integrations/firebase-userBackup' import * as IntegrationsEmail from './Integrations/firebase-email' +import * as UserStats from './Integrations/user-stats' import * as Admin from './admin' // the following endpoints are exposed for use by various triggers @@ -23,6 +24,8 @@ exports.notifyHowToAccepted = IntegrationsDiscord.notifyHowToAccepted exports.notifyEventAccepted = IntegrationsDiscord.notifyEventAccepted exports.firebaseUserBackup = FirebaseUserBackup exports.emailNotificationDemo = IntegrationsEmail.notifyEmailDemo +exports.countEvents = UserStats.countEvents +exports.countHowTos = UserStats.countHowTos // CC Note, 2020-04-40 // folder-based naming conventions should be encourage from now on exports.adminGetUserEmail = Admin.getUserEmail diff --git a/src/models/user.models.tsx b/src/models/user.models.tsx index 446487ddf5..836cb556b7 100644 --- a/src/models/user.models.tsx +++ b/src/models/user.models.tsx @@ -36,6 +36,7 @@ export interface IUser { country?: string | null location?: ILocation | null year?: ISODateString + _stats?: IUserStats } interface IExternalLink { @@ -51,6 +52,11 @@ interface IExternalLink { | 'instagram' } +interface IUserStats { + howToCount: number + eventCount: number +} + export type IUserDB = IUser & DBDoc export type UserRole = 'super-admin' | 'subscriber' | 'admin' diff --git a/src/pages/User/content/UserPage/UserPage.tsx b/src/pages/User/content/UserPage/UserPage.tsx index e3a5d92877..83d2823214 100644 --- a/src/pages/User/content/UserPage/UserPage.tsx +++ b/src/pages/User/content/UserPage/UserPage.tsx @@ -228,7 +228,9 @@ export class UserPage extends React.Component< } // Comment on 6.05.20 by BG : renderCommitmentBox commented for now, will be reused with #974 public renderUserStatsBox(user: IUserPP) { - console.log('user.location', user.location) + if (!user._stats) { + user._stats = { howToCount: 0, eventCount: 0 } + } return ( @@ -239,12 +241,14 @@ export class UserPage extends React.Component< )} */} - - - - {user.location?.country} - - + {user.location && ( + + + + {user.location?.country} + + + )} {/* {isV4Member && ( @@ -252,12 +256,20 @@ export class UserPage extends React.Component< )} */} - - 0 - - - 0 - + {user._stats.eventCount > 0 && ( + + + {user._stats.eventCount} + + + )} + {user._stats.howToCount > 0 && ( + + + {user._stats.howToCount} + + + )} ) } @@ -384,7 +396,12 @@ export class UserPage extends React.Component< ) } // TODO check if user exist and have created how-to or events - const shouldRenderUserStatsBox = user && user.location ? true : false + const shouldRenderUserStatsBox = + user && + (user.location || + (user._stats && (user._stats.howToCount || user._stats.eventCount))) + ? true + : false return (