From ca879d156df084cfa846a4eed25cdf8dc011d698 Mon Sep 17 00:00:00 2001 From: benj Date: Tue, 21 Jul 2020 13:56:04 +0100 Subject: [PATCH 1/6] display user stat box and user location on profile --- src/pages/User/content/UserPage/UserPage.tsx | 70 ++++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/pages/User/content/UserPage/UserPage.tsx b/src/pages/User/content/UserPage/UserPage.tsx index d4b1983951..3db4cbfe7a 100644 --- a/src/pages/User/content/UserPage/UserPage.tsx +++ b/src/pages/User/content/UserPage/UserPage.tsx @@ -19,6 +19,7 @@ import ElWithBeforeIcon from 'src/components/ElWithBeforeIcon' import { zIndex } from 'src/themes/styled.theme' import Workspace from 'src/pages/User/workspace/Workspace' import { Text } from 'src/components/Text' +import { Link } from 'src/components/Links' import theme from 'src/themes/styled.theme' import { replaceDashesWithSpaces } from 'src/utils/helpers' @@ -114,7 +115,8 @@ const MobileBadge = styled.div` } ` -const CommitmentBox = styled.div` +const UserStatsBox = styled.div` + margin-top: 15px; border: 2px solid black; border-radius: 10px; padding: 20px; @@ -122,7 +124,7 @@ const CommitmentBox = styled.div` margin-bottom: 20px; ` -const CommitmentBoxItem = styled.div` +const UserStatsBoxItem = styled.div` margin-top: 15px; &:first-child { @@ -225,32 +227,40 @@ export class UserPage extends React.Component< }) } // Comment on 6.05.20 by BG : renderCommitmentBox commented for now, will be reused with #974 - // public renderCommitmentBox(isExpert?: boolean, isV4Member?: boolean) { - // return ( - // - // {isExpert && ( - // - // - // Expert - // - // - // )} - // {isV4Member && ( - // - // - // V4 Member - // - // - // )} - // - // 0 - // - // - // 0 - // - // - // ) - // } + public renderUserStatsBox(user: IUserPP) { + console.log('user.location', user.location) + + return ( + + {/* {isExpert && ( + + + Expert + + + )} */} + + + + {user.location?.country} + + + {/* {isV4Member && ( + + + V4 Member + + + )} */} + + 0 + + + 0 + + + ) + } public renderPlasticTypes(plasticTypes: Array) { function renderIcon(type: string) { @@ -373,6 +383,8 @@ 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 return ( @@ -448,6 +460,8 @@ export class UserPage extends React.Component< > + + {shouldRenderUserStatsBox && this.renderUserStatsBox(user)} From 40b7deb8073677b2518906acb444bca19081df44 Mon Sep 17 00:00:00 2001 From: Alejandro Romero Herrera Date: Sat, 5 Sep 2020 18:21:18 +0300 Subject: [PATCH 2/6] 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 ( From 8b6e0c7050e46ad10d25c2920b9e0e855a06fa51 Mon Sep 17 00:00:00 2001 From: Alejandro Romero Herrera Date: Sun, 6 Sep 2020 15:59:32 +0300 Subject: [PATCH 3/6] Added missing text for User Stats (How-to: , Events:) --- src/pages/User/content/UserPage/UserPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/User/content/UserPage/UserPage.tsx b/src/pages/User/content/UserPage/UserPage.tsx index 83d2823214..5f1e4c5cdf 100644 --- a/src/pages/User/content/UserPage/UserPage.tsx +++ b/src/pages/User/content/UserPage/UserPage.tsx @@ -119,7 +119,7 @@ const UserStatsBox = styled.div` margin-top: 15px; border: 2px solid black; border-radius: 10px; - padding: 20px; + padding: 10px; background-color: ${theme.colors.background}; margin-bottom: 20px; ` @@ -259,14 +259,14 @@ export class UserPage extends React.Component< {user._stats.eventCount > 0 && ( - {user._stats.eventCount} + How-to: {user._stats.eventCount} )} {user._stats.howToCount > 0 && ( - {user._stats.howToCount} + Events: {user._stats.howToCount} )} From 03b17a9bf0e1ea72710412d6d16900bb343df4ed Mon Sep 17 00:00:00 2001 From: Alejandro Romero Herrera Date: Sun, 6 Sep 2020 18:51:26 +0300 Subject: [PATCH 4/6] Profile extra info in correct order and in single line --- src/components/ElWithBeforeIcon/index.tsx | 4 +++- src/pages/User/content/UserPage/UserPage.tsx | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/ElWithBeforeIcon/index.tsx b/src/components/ElWithBeforeIcon/index.tsx index 2069ddeaf2..80f9da8c2c 100644 --- a/src/components/ElWithBeforeIcon/index.tsx +++ b/src/components/ElWithBeforeIcon/index.tsx @@ -8,6 +8,7 @@ interface IProps { width?: string ticked?: boolean contain?: boolean + mr?: number } export const ElWithBeforeIcon: FunctionComponent = ({ @@ -17,6 +18,7 @@ export const ElWithBeforeIcon: FunctionComponent = ({ children, ticked, contain, + mr, }) => { let after: any if (ticked) { @@ -35,7 +37,7 @@ export const ElWithBeforeIcon: FunctionComponent = ({ return ( )} */} - {user._stats.eventCount > 0 && ( + {user._stats.howToCount > 0 && ( - - How-to: {user._stats.eventCount} + + How-to: {user._stats.howToCount} )} - {user._stats.howToCount > 0 && ( + {user._stats.eventCount > 0 && ( - - Events: {user._stats.howToCount} + + Events: {user._stats.eventCount} )} From 4152e7cca7254b26c5475f0f029f6ad2f2c14ebe Mon Sep 17 00:00:00 2001 From: Alejandro Romero Herrera Date: Sun, 6 Sep 2020 18:57:55 +0300 Subject: [PATCH 5/6] Right color for pin link in profile extras --- src/pages/User/content/UserPage/UserPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/User/content/UserPage/UserPage.tsx b/src/pages/User/content/UserPage/UserPage.tsx index f8bd81634c..7ad6e01667 100644 --- a/src/pages/User/content/UserPage/UserPage.tsx +++ b/src/pages/User/content/UserPage/UserPage.tsx @@ -242,7 +242,7 @@ export class UserPage extends React.Component< )} */} {user.location && ( - + {user.location?.country} From 3a85e05eab25b2a1c66f1129320eacea02fdc7f2 Mon Sep 17 00:00:00 2001 From: Alejandro Romero Herrera Date: Thu, 10 Sep 2020 16:07:15 +0300 Subject: [PATCH 6/6] User _stats renamed to stats for #974 https://github.com/ONEARMY/community-platform/pull/1031 --- functions/src/Integrations/user-stats.ts | 6 +++--- src/models/user.models.tsx | 2 +- src/pages/User/content/UserPage/UserPage.tsx | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/functions/src/Integrations/user-stats.ts b/functions/src/Integrations/user-stats.ts index 31ef8fb817..375e79e29d 100644 --- a/functions/src/Integrations/user-stats.ts +++ b/functions/src/Integrations/user-stats.ts @@ -36,7 +36,7 @@ function updateStats(change, collection, target){ console.log('Update ', collection, ' delta: ', delta ) - return userStatsRef.doc(info._createdBy).update({'_stats':{ + return userStatsRef.doc(info._createdBy).update({'stats':{ [target]: admin.firestore.FieldValue.increment(delta) }}).catch(e => { console.log(e); @@ -58,7 +58,7 @@ function computeUserStats(owner){ count++; }); console.log('Accepted howtos for', owner ,',count: ', count); - userStatsRef.doc(owner).set({'_stats':{ + userStatsRef.doc(owner).set({'stats':{ howToCount: count }}, {merge: true}); return null @@ -72,7 +72,7 @@ function computeUserStats(owner){ count++; }); console.log('Accepted events for', owner ,',count: ', count); - userStatsRef.doc(owner).set({'_stats':{ + userStatsRef.doc(owner).set({'stats':{ eventCount: count }}, {merge: true}); return null diff --git a/src/models/user.models.tsx b/src/models/user.models.tsx index 836cb556b7..666f0415db 100644 --- a/src/models/user.models.tsx +++ b/src/models/user.models.tsx @@ -36,7 +36,7 @@ export interface IUser { country?: string | null location?: ILocation | null year?: ISODateString - _stats?: IUserStats + stats?: IUserStats } interface IExternalLink { diff --git a/src/pages/User/content/UserPage/UserPage.tsx b/src/pages/User/content/UserPage/UserPage.tsx index 7ad6e01667..35ff0dd110 100644 --- a/src/pages/User/content/UserPage/UserPage.tsx +++ b/src/pages/User/content/UserPage/UserPage.tsx @@ -228,8 +228,8 @@ 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) { - if (!user._stats) { - user._stats = { howToCount: 0, eventCount: 0 } + if (!user.stats) { + user.stats = { howToCount: 0, eventCount: 0 } } return ( @@ -256,17 +256,17 @@ export class UserPage extends React.Component< )} */} - {user._stats.howToCount > 0 && ( + {user.stats.howToCount > 0 && ( - How-to: {user._stats.howToCount} + How-to: {user.stats.howToCount} )} - {user._stats.eventCount > 0 && ( + {user.stats.eventCount > 0 && ( - Events: {user._stats.eventCount} + Events: {user.stats.eventCount} )} @@ -399,7 +399,7 @@ export class UserPage extends React.Component< const shouldRenderUserStatsBox = user && (user.location || - (user._stats && (user._stats.howToCount || user._stats.eventCount))) + (user.stats && (user.stats.howToCount || user.stats.eventCount))) ? true : false