Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #974 Extra infromation on Profile #1031

Closed
Closed
81 changes: 81 additions & 0 deletions functions/src/Integrations/user-stats.ts
Original file line number Diff line number Diff line change
@@ -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);
}

3 changes: 3 additions & 0 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
2 changes: 1 addition & 1 deletion scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ checkBrowsers(paths.appPath, isInteractive)
function build(previousFileSizes) {
console.log('Creating an optimized production build...')

let compiler = webpack(config)
const compiler = webpack(config)
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages
Expand Down
4 changes: 3 additions & 1 deletion src/components/ElWithBeforeIcon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface IProps {
width?: string
ticked?: boolean
contain?: boolean
mr?: number
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case it will be better to extend the props from rebass BoxProps directly, but I don't think you need to add it here, and I don't see it used anywhere else anyway ?

Copy link
Collaborator Author

@alromh87 alromh87 Sep 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was needed to be able to change the margin of the internal box, which had it fixed to 4, now it will default to 4(avoid braking other displays), but can be overwiten when usng component, used to make howto event display in one line

03b17a9

Maybe your right, but i dont know how to do that 😓

}

export const ElWithBeforeIcon: FunctionComponent<IProps> = ({
Expand All @@ -17,6 +18,7 @@ export const ElWithBeforeIcon: FunctionComponent<IProps> = ({
children,
ticked,
contain,
mr,
}) => {
let after: any
if (ticked) {
Expand All @@ -35,7 +37,7 @@ export const ElWithBeforeIcon: FunctionComponent<IProps> = ({

return (
<Box
mr={4}
mr={mr || 4}
pl="30px"
sx={{
position: 'relative',
Expand Down
6 changes: 6 additions & 0 deletions src/models/user.models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface IUser {
country?: string | null
location?: ILocation | null
year?: ISODateString
_stats?: IUserStats
BenGamma marked this conversation as resolved.
Show resolved Hide resolved
}

interface IExternalLink {
Expand All @@ -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'
115 changes: 73 additions & 42 deletions src/pages/User/content/UserPage/UserPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import Slider from 'react-slick'
import styled from 'styled-components'
import Icon from 'src/components/Icons'
import Flex from 'src/components/Flex'
// import ElWithBeforeIcon from 'src/components/ElWithBeforeIcon'
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'
Expand All @@ -33,9 +34,9 @@ import PPIcon from 'src/assets/images/plastic-types/pp.svg'
import PSIcon from 'src/assets/images/plastic-types/ps.svg'
import PVCIcon from 'src/assets/images/plastic-types/pvc.svg'

// import EventsIcon from 'src/assets/icons/icon-events.svg'
import EventsIcon from 'src/assets/icons/icon-events.svg'
// import ExpertIcon from 'src/assets/icons/icon-expert.svg'
// import HowToCountIcon from 'src/assets/icons/icon-how-to.svg'
import HowToCountIcon from 'src/assets/icons/icon-how-to.svg'
// import V4MemberIcon from 'src/assets/icons/icon-v4-member.svg'

import { IUploadedFileMeta } from 'src/stores/storage'
Expand Down Expand Up @@ -114,21 +115,22 @@ const MobileBadge = styled.div`
}
`

// const CommitmentBox = styled.div`
// border: 2px solid black;
// border-radius: 10px;
// padding: 20px;
// background-color: ${theme.colors.background};
// margin-bottom: 20px;
// `
const UserStatsBox = styled.div`
margin-top: 15px;
border: 2px solid black;
border-radius: 10px;
padding: 10px;
background-color: ${theme.colors.background};
margin-bottom: 20px;
`

// const CommitmentBoxItem = styled.div`
// margin-top: 15px;
const UserStatsBoxItem = styled.div`
margin-top: 15px;

// &:first-child {
// margin-top: 0;
// }
// `
&:first-child {
margin-top: 0;
}
`

const ProfileWrapper = styled(Box)`
/* margin-top: 40px;
Expand Down Expand Up @@ -225,32 +227,52 @@ 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 (
// <CommitmentBox>
// {isExpert && (
// <CommitmentBoxItem>
// <ElWithBeforeIcon IconUrl={ExpertIcon} height="25px">
// Expert
// </ElWithBeforeIcon>
// </CommitmentBoxItem>
// )}
// {isV4Member && (
// <CommitmentBoxItem>
// <ElWithBeforeIcon IconUrl={V4MemberIcon}>
// V4 Member
// </ElWithBeforeIcon>
// </CommitmentBoxItem>
// )}
// <CommitmentBoxItem>
// <ElWithBeforeIcon IconUrl={EventsIcon}>0</ElWithBeforeIcon>
// </CommitmentBoxItem>
// <CommitmentBoxItem>
// <ElWithBeforeIcon IconUrl={HowToCountIcon}>0</ElWithBeforeIcon>
// </CommitmentBoxItem>
// </CommitmentBox>
// )
// }
public renderUserStatsBox(user: IUserPP) {
if (!user._stats) {
user._stats = { howToCount: 0, eventCount: 0 }
}

return (
<UserStatsBox>
{/* {isExpert && (
<UserStatsBoxItem>
<ElWithBeforeIcon IconUrl={ExpertIcon} height="25px">
Expert
</ElWithBeforeIcon>
</UserStatsBoxItem>
)} */}
{user.location && (
<Link color={'black'} to={'/map/#' + user.userName}>
<UserStatsBoxItem>
<Icon glyph="location-on"></Icon>
<span> {user.location?.country}</span>
</UserStatsBoxItem>
</Link>
)}
{/* {isV4Member && (
<UserStatsBoxItem>
<ElWithBeforeIcon IconUrl={V4MemberIcon}>
V4 Member
</ElWithBeforeIcon>
</UserStatsBoxItem>
)} */}
{user._stats.howToCount > 0 && (
<UserStatsBoxItem>
<ElWithBeforeIcon IconUrl={HowToCountIcon} mr={2}>
How-to: {user._stats.howToCount}
</ElWithBeforeIcon>
</UserStatsBoxItem>
)}
{user._stats.eventCount > 0 && (
<UserStatsBoxItem>
<ElWithBeforeIcon IconUrl={EventsIcon} mr={2}>
Events: {user._stats.eventCount}
</ElWithBeforeIcon>
</UserStatsBoxItem>
)}
</UserStatsBox>
)
}

public renderPlasticTypes(plasticTypes: Array<PlasticTypeLabel>) {
function renderIcon(type: string) {
Expand Down Expand Up @@ -373,6 +395,13 @@ export class UserPage extends React.Component<
},
)
}
// TODO check if user exist and have created how-to or events
const shouldRenderUserStatsBox =
user &&
(user.location ||
(user._stats && (user._stats.howToCount || user._stats.eventCount)))
? true
: false

return (
<ProfileWrapper mt={4} mb={6}>
Expand Down Expand Up @@ -448,6 +477,8 @@ export class UserPage extends React.Component<
>
<MobileBadge>
<Image src={workspaceBadgeSrc} />

{shouldRenderUserStatsBox && this.renderUserStatsBox(user)}
</MobileBadge>
</Box>
</ProfileContentWrapper>
Expand Down