From 220737f5c8e6f9788774cb0d25eb0796be0f19f5 Mon Sep 17 00:00:00 2001 From: AlfonsoGhislieri Date: Sat, 21 Jan 2023 13:05:09 +0000 Subject: [PATCH 1/9] feat: view incrementation implemented using viewcounter --- .../src/ViewsCounter/ViewsCounter.tsx | 34 ++ packages/components/src/index.ts | 1 + src/models/howto.models.tsx | 1 + src/models/research.models.tsx | 2 + .../HowtoDescription/HowtoDescription.tsx | 512 +++++++++--------- .../Research/Content/ResearchDescription.tsx | 22 +- src/stores/Howto/howto.store.tsx | 17 + src/stores/Research/research.store.tsx | 25 + 8 files changed, 355 insertions(+), 259 deletions(-) create mode 100644 packages/components/src/ViewsCounter/ViewsCounter.tsx diff --git a/packages/components/src/ViewsCounter/ViewsCounter.tsx b/packages/components/src/ViewsCounter/ViewsCounter.tsx new file mode 100644 index 0000000000..3109e8ef35 --- /dev/null +++ b/packages/components/src/ViewsCounter/ViewsCounter.tsx @@ -0,0 +1,34 @@ +import { Text } from 'theme-ui' +import { Button } from '..' + +export interface IProps { + viewsCount: number +} + +export const ViewsCounter = (props: IProps) => { + + return ( + <> + + + ) +} diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index f32ad1384b..8d9925b138 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -23,6 +23,7 @@ export { ImageGallery } from './ImageGallery/ImageGallery' export { Loader } from './Loader/Loader' export { LinkifyText } from './LinkifyText/LinkifyText' export { UsefulStatsButton } from './UsefulStatsButton/UsefulStatsButton' +export { ViewsCounter} from './ViewsCounter/ViewsCounter' export { CategoryTag } from './CategoryTag/CategoryTag' export { FileInformation } from './FileInformation/FileInformation' export { OsmGeocoding } from './OsmGeocoding/OsmGeocoding' diff --git a/src/models/howto.models.tsx b/src/models/howto.models.tsx index 7c3a10daad..54c2479f8d 100644 --- a/src/models/howto.models.tsx +++ b/src/models/howto.models.tsx @@ -22,6 +22,7 @@ export interface IHowto extends IHowtoFormInput, IModerable { // Comments were added in V2, old howto's may not have the property comments?: IComment[] total_downloads?: number + total_views?: number mentions: UserMention[] previousSlugs?: string[] } diff --git a/src/models/research.models.tsx b/src/models/research.models.tsx index 4cd26bc372..9dd4c8d9e6 100644 --- a/src/models/research.models.tsx +++ b/src/models/research.models.tsx @@ -18,6 +18,7 @@ export namespace IResearch { export interface Item extends FormInput { updates: Update[] _createdBy: string + total_views?: number } /** A research item update */ @@ -27,6 +28,7 @@ export namespace IResearch { images: Array videoUrl?: string comments?: IComment[] + total_views?: number } export interface FormInput extends IModerable { diff --git a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx index aea5977161..aa40e81bea 100644 --- a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx @@ -1,5 +1,5 @@ -import { PureComponent } from 'react' import { format } from 'date-fns' +import { useState, useEffect } from 'react'; import type { IHowtoDB } from 'src/models/howto.models' import { Heading, Text, Box, Flex, Image, AspectImage } from 'theme-ui' import StepsIcon from 'src/assets/icons/icon-steps.svg' @@ -13,6 +13,7 @@ import { CategoryTag, FileInformation, Username, + ViewsCounter } from 'oa-components' import type { IUser } from 'src/models/user.models' import { @@ -24,8 +25,7 @@ import theme from 'src/themes/styled.theme' import ArrowIcon from 'src/assets/icons/icon-arrow-select.svg' import { DownloadExternal } from 'src/pages/Howto/DownloadExternal/DownloadExternal' import { Link } from 'react-router-dom' -import type { HowtoStore } from 'src/stores/Howto/howto.store' -import { inject, observer } from 'mobx-react' +import { useCommonStores } from 'src/index' import { retrieveHowtoDownloadCooldown, isHowtoDownloadCooldownExpired, @@ -44,59 +44,47 @@ interface IProps { onUsefulClick: () => void } -interface IInjected extends IProps { - howtoStore: HowtoStore -} +const HowtoDescription: React.FC = ({ + howto, + ...props +}) => { -interface IState { - fileDownloadCount: number | undefined -} + const [fileDownloadCount, setFileDownloadCount] = useState(howto.total_downloads) + const [viewCount, setViewCount] = useState(howto.total_views) + const { loggedInUser } = props + const { stores } = useCommonStores() -@inject('howtoStore') -@observer -export default class HowtoDescription extends PureComponent { - private setFileDownloadCount = (val: number) => { - this.setState({ - fileDownloadCount: val, - }) - } - private incrementDownloadCount = async () => { + const incrementDownloadCount = async () => { const updatedDownloadCount = - await this.injected.howtoStore.incrementDownloadCount( - this.props.howto._id, + await stores.howtoStore.incrementDownloadCount( + howto._id ) - this.setFileDownloadCount(updatedDownloadCount!) + setFileDownloadCount(updatedDownloadCount!) + } + + const incrementViewCount = async () => { + const updatedViewCount = await stores.howtoStore.incrementViewCount(howto._id) + setViewCount(updatedViewCount) } - private handleClick = async () => { + + const handleClick = async () => { const howtoDownloadCooldown = retrieveHowtoDownloadCooldown( - this.props.howto._id, + howto._id ) if ( howtoDownloadCooldown && isHowtoDownloadCooldownExpired(howtoDownloadCooldown) ) { - updateHowtoDownloadCooldown(this.props.howto._id) - this.incrementDownloadCount() + updateHowtoDownloadCooldown(howto._id) + incrementDownloadCount() } else if (!howtoDownloadCooldown) { - addHowtoDownloadCooldown(this.props.howto._id) - this.incrementDownloadCount() - } - } - // eslint-disable-next-line - constructor(props: IProps) { - super(props) - this.state = { - fileDownloadCount: this.props.howto.total_downloads || 0, + addHowtoDownloadCooldown(howto._id) + incrementDownloadCount() } - this.handleClick = this.handleClick.bind(this) - } - - get injected() { - return this.props as IInjected } - private dateLastEditText(howto: IHowtoDB): string { + const dateLastEditText = (howto: IHowtoDB): string => { const lastModifiedDate = format(new Date(howto._modified), 'DD-MM-YYYY') const creationDate = format(new Date(howto._created), 'DD-MM-YYYY') if (lastModifiedDate !== creationDate) { @@ -106,239 +94,247 @@ export default class HowtoDescription extends PureComponent { } } - public render() { - const { howto, loggedInUser } = this.props + // useEffect(() => { + // incrementViewCount() + // }, []) + + useEffect(() => { + setFileDownloadCount(howto.total_downloads) + setViewCount(howto.total_views) + incrementViewCount() + }, [howto]) - const iconFlexDirection = - emStringToPx(theme.breakpoints[0]) > window.innerWidth ? 'column' : 'row' - return ( + const iconFlexDirection = + emStringToPx(theme.breakpoints[0]) > window.innerWidth ? 'column' : 'row' + + return ( + - - - - - - {this.props.votedUsefulCount !== undefined && ( - - + + - + Back - )} - {/* Check if logged in user is the creator of the how-to OR a super-admin */} - {loggedInUser && isAllowToEditContent(howto, loggedInUser) && ( - - - - )} - - - - - {this.dateLastEditText(howto)} - - - {/* HACK 2021-07-16 - new howtos auto capitalize title but not older */} - {capitalizeFirstLetter(howto.title)} - - - {howto.description} - - - - - - + + {props.votedUsefulCount !== undefined && ( + + - {howto.steps.length} steps - - - + )} + + - {howto.time} - - - + {/* Check if pin should be moderated */} + {props.needsModeration && ( + + + + )} - - + - {howto.moderation !== 'accepted' && ( - - )} + + {dateLastEditText(howto)} + + + {/* HACK 2021-07-16 - new howtos auto capitalize title but not older */} + {capitalizeFirstLetter(howto.title)} + + + {howto.description} + + + + + + {howto.steps.length} steps + + + + {howto.time} + + + + {howto.difficulty_level} + + + + {howto.taglist && + howto.taglist.map((tag, idx) => ( + + ))} + + {((howto.files && howto.files.length > 0) || howto.fileLink) && ( + + {howto.fileLink && ( + + )} + {howto.files + .filter(Boolean) + .map( + (file, index) => + file && ( + + ), + )} + {typeof fileDownloadCount === 'number' && ( + + {fileDownloadCount} + {fileDownloadCount !== 1 + ? ' downloads' + : ' download'} + + )} + + )} - ) - } + + + {howto.moderation !== 'accepted' && ( + + )} + + + ) } + +export default HowtoDescription diff --git a/src/pages/Research/Content/ResearchDescription.tsx b/src/pages/Research/Content/ResearchDescription.tsx index 3d11c1681b..d64975246c 100644 --- a/src/pages/Research/Content/ResearchDescription.tsx +++ b/src/pages/Research/Content/ResearchDescription.tsx @@ -8,12 +8,14 @@ import { ModerationStatus, UsefulStatsButton, Username, + ViewsCounter } from 'oa-components' import type { IResearch } from 'src/models/research.models' import theme from 'src/themes/styled.theme' import type { IUser } from 'src/models/user.models' import { Link } from 'react-router-dom' import { isUserVerified } from 'src/common/isUserVerified' +import { useResearchStore } from 'src/stores/Research/research.store' interface IProps { research: IResearch.ItemDB @@ -40,6 +42,19 @@ const ResearchDescription: React.FC = ({ return '' } } + const store = useResearchStore() + + const [viewCount, setViewCount] = React.useState(research.total_views) + + const incrementViewCount = async () => { + const updatedViewCount = await store.incrementViewCount(research._id) + setViewCount(updatedViewCount) + } + + React.useEffect(() => { + setViewCount(research.total_views) + incrementViewCount() + }, [research]) return ( = ({ {props.votedUsefulCount !== undefined && ( - + = ({ /> )} + + + {/* Check if research should be moderated */} {props.needsModeration && ( diff --git a/src/stores/Howto/howto.store.tsx b/src/stores/Howto/howto.store.tsx index 3f4088cb7b..0a96c6480a 100644 --- a/src/stores/Howto/howto.store.tsx +++ b/src/stores/Howto/howto.store.tsx @@ -190,6 +190,23 @@ export class HowtoStore extends ModuleStore { } } + public async incrementViewCount(howToID: string) { + const dbRef = this.db.collection(COLLECTION_NAME).doc(howToID) + const howToData = await toJS(dbRef.get()) + const totalViews = howToData?.total_views || 0 + + if (howToData) { + const updatedHowto: IHowto = { + ...howToData, + total_views: totalViews! + 1, + } + + await this.updateHowtoItem(updatedHowto) + + return updatedHowto.total_views + } + } + public updateSearchValue(query: string) { this.searchValue = query } diff --git a/src/stores/Research/research.store.tsx b/src/stores/Research/research.store.tsx index 6629cdd39c..7d6dfaafae 100644 --- a/src/stores/Research/research.store.tsx +++ b/src/stores/Research/research.store.tsx @@ -146,6 +146,31 @@ export class ResearchStore extends ModuleStore { } } + public async incrementViewCount(id: string) { + const dbRef = this.db.collection(COLLECTION_NAME).doc(id) + const researchData = await toJS(dbRef.get()) + const totalViews = researchData?.total_views || 0 + + if (researchData) { + const updatedResearch: IResearchDB = { + ...researchData, + total_views: totalViews! + 1, + } + + // remove this comment + // console.log(updatedResearch) + + dbRef.set({ + ... updatedResearch + }) + + // remove this comment + // console.log(await toJS(this.db.collection(COLLECTION_NAME).doc(updatedResearch._id)).get()) + + return updatedResearch.total_views + } + } + public deleteResearchItem(id: string) { this.db.collection('research').doc(id).delete() } From 0807984f5b9993e8f5f322c8d952a0bbff14a0f4 Mon Sep 17 00:00:00 2001 From: AlfonsoGhislieri Date: Mon, 23 Jan 2023 14:49:24 +0000 Subject: [PATCH 2/9] chore: cleaned up props --- packages/components/src/ViewsCounter/ViewsCounter.tsx | 4 ++-- .../Content/Howto/HowtoDescription/HowtoDescription.tsx | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/components/src/ViewsCounter/ViewsCounter.tsx b/packages/components/src/ViewsCounter/ViewsCounter.tsx index 3109e8ef35..570e625e1b 100644 --- a/packages/components/src/ViewsCounter/ViewsCounter.tsx +++ b/packages/components/src/ViewsCounter/ViewsCounter.tsx @@ -25,8 +25,8 @@ export const ViewsCounter = (props: IProps) => { icon={'star'} > - {props.viewsCount ? props.viewsCount : 0 } - {props.viewsCount!== 1 ? ' views' : ' view'} + {props.viewsCount} + {props.viewsCount !== 1 ? ' views' : ' view'} diff --git a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx index aa40e81bea..63fab8c28c 100644 --- a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx @@ -46,12 +46,12 @@ interface IProps { const HowtoDescription: React.FC = ({ howto, + loggedInUser, ...props }) => { const [fileDownloadCount, setFileDownloadCount] = useState(howto.total_downloads) const [viewCount, setViewCount] = useState(howto.total_views) - const { loggedInUser } = props const { stores } = useCommonStores() const incrementDownloadCount = async () => { @@ -93,10 +93,6 @@ const HowtoDescription: React.FC = ({ return '' } } - - // useEffect(() => { - // incrementViewCount() - // }, []) useEffect(() => { setFileDownloadCount(howto.total_downloads) @@ -157,7 +153,7 @@ const HowtoDescription: React.FC = ({ From 8a9d5c8a58c2137303aae491ede10eb6449c0b5a Mon Sep 17 00:00:00 2001 From: AlfonsoGhislieri Date: Thu, 2 Feb 2023 14:36:59 +0000 Subject: [PATCH 3/9] fix: views incremented without _modified field being changed --- src/stores/Howto/howto.store.tsx | 11 ++++++++--- src/stores/Research/research.store.tsx | 8 +------- src/stores/databaseV2/DocReference.tsx | 12 ++++++++---- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/stores/Howto/howto.store.tsx b/src/stores/Howto/howto.store.tsx index 0a96c6480a..db12298a0b 100644 --- a/src/stores/Howto/howto.store.tsx +++ b/src/stores/Howto/howto.store.tsx @@ -185,7 +185,10 @@ export class HowtoStore extends ModuleStore { total_downloads: totalDownloads! + 1, } - await this.updateHowtoItem(updatedHowto) + dbRef.set({ + ... updatedHowto + }, {keep_modified_timestamp: true}) + return updatedHowto.total_downloads } } @@ -201,8 +204,10 @@ export class HowtoStore extends ModuleStore { total_views: totalViews! + 1, } - await this.updateHowtoItem(updatedHowto) - + dbRef.set({ + ... updatedHowto + }, {keep_modified_timestamp: true}) + return updatedHowto.total_views } } diff --git a/src/stores/Research/research.store.tsx b/src/stores/Research/research.store.tsx index 7d6dfaafae..f9350a4386 100644 --- a/src/stores/Research/research.store.tsx +++ b/src/stores/Research/research.store.tsx @@ -157,15 +157,9 @@ export class ResearchStore extends ModuleStore { total_views: totalViews! + 1, } - // remove this comment - // console.log(updatedResearch) - dbRef.set({ ... updatedResearch - }) - - // remove this comment - // console.log(await toJS(this.db.collection(COLLECTION_NAME).doc(updatedResearch._id)).get()) + }, {keep_modified_timestamp: true}) return updatedResearch.total_views } diff --git a/src/stores/databaseV2/DocReference.tsx b/src/stores/databaseV2/DocReference.tsx index 6198f444a6..9556a60c43 100644 --- a/src/stores/databaseV2/DocReference.tsx +++ b/src/stores/databaseV2/DocReference.tsx @@ -59,9 +59,9 @@ export class DocReference { * If contains metadata fields (e.g. `_id`) * then this will be used instead of generated id */ - async set(data: T) { + async set(data: T, options?:{keep_modified_timestamp:boolean}) { const { serverDB, cacheDB } = this.clients - const dbDoc: DBDoc = this._setDocMeta(data) + const dbDoc: DBDoc = this._setDocMeta(data, options) await serverDB.setDoc(this.endpoint, dbDoc) await cacheDB.setDoc(this.endpoint, dbDoc) } @@ -90,14 +90,18 @@ export class DocReference { return this._setDocMeta(data) } - private _setDocMeta(data: any = {}): DBDoc { + private _setDocMeta(data: any = {}, options: any = {}): DBDoc { const d = data + const o = options + const modifiedTimestamp = + o.keep_modified_timestamp ? d._modified : new Date().toISOString() + return { ...d, _created: d._created ? d._created : new Date().toISOString(), _deleted: d._deleted ? d._deleted : false, _id: this.id, - _modified: new Date().toISOString(), + _modified: modifiedTimestamp, } } From 646ba5321ef604df9f7a797f8e77cf6de29a13dd Mon Sep 17 00:00:00 2001 From: AlfonsoGhislieri Date: Thu, 2 Feb 2023 17:02:26 +0000 Subject: [PATCH 4/9] feat: session storage added to limit views from increasing --- .../HowtoDescription/HowtoDescription.tsx | 14 +++++++++++--- .../Research/Content/ResearchDescription.tsx | 19 +++++++++++++------ src/utils/sessionStorage.ts | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 src/utils/sessionStorage.ts diff --git a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx index 63fab8c28c..7a323511ed 100644 --- a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx @@ -32,6 +32,8 @@ import { addHowtoDownloadCooldown, updateHowtoDownloadCooldown, } from './downloadCooldown' +import {retrieveSessionStorageArray, addIDToSessionStorageArray} from'src/utils/sessionStorage' + interface IProps { howto: IHowtoDB & { taglist: any } @@ -63,8 +65,14 @@ const HowtoDescription: React.FC = ({ } const incrementViewCount = async () => { - const updatedViewCount = await stores.howtoStore.incrementViewCount(howto._id) - setViewCount(updatedViewCount) + const sessionStorageArray = retrieveSessionStorageArray('howto') + console.log(sessionStorageArray) + + if (!(sessionStorageArray.includes(howto._id))) { + const updatedViewCount = await stores.howtoStore.incrementViewCount(howto._id) + addIDToSessionStorageArray('howto',howto._id) + setViewCount(updatedViewCount) + } } const handleClick = async () => { @@ -98,7 +106,7 @@ const HowtoDescription: React.FC = ({ setFileDownloadCount(howto.total_downloads) setViewCount(howto.total_views) incrementViewCount() - }, [howto]) + }, [howto._id]) const iconFlexDirection = emStringToPx(theme.breakpoints[0]) > window.innerWidth ? 'column' : 'row' diff --git a/src/pages/Research/Content/ResearchDescription.tsx b/src/pages/Research/Content/ResearchDescription.tsx index d64975246c..9ce97ab127 100644 --- a/src/pages/Research/Content/ResearchDescription.tsx +++ b/src/pages/Research/Content/ResearchDescription.tsx @@ -1,5 +1,6 @@ import { format } from 'date-fns' import * as React from 'react' +import {useEffect, useState} from 'react' import { Box, Flex, Image, Text, Heading } from 'theme-ui' import ArrowIcon from 'src/assets/icons/icon-arrow-select.svg' import { @@ -16,6 +17,7 @@ import type { IUser } from 'src/models/user.models' import { Link } from 'react-router-dom' import { isUserVerified } from 'src/common/isUserVerified' import { useResearchStore } from 'src/stores/Research/research.store' +import {retrieveSessionStorageArray, addIDToSessionStorageArray} from'src/utils/sessionStorage' interface IProps { research: IResearch.ItemDB @@ -44,17 +46,22 @@ const ResearchDescription: React.FC = ({ } const store = useResearchStore() - const [viewCount, setViewCount] = React.useState(research.total_views) + const [viewCount, setViewCount] = useState(research.total_views) const incrementViewCount = async () => { - const updatedViewCount = await store.incrementViewCount(research._id) - setViewCount(updatedViewCount) + const sessionStorageArray = retrieveSessionStorageArray('research') + + if (!(sessionStorageArray.includes(research._id))) { + const updatedViewCount = await store.incrementViewCount(research._id) + setViewCount(updatedViewCount) + addIDToSessionStorageArray('research',research._id) + } } - - React.useEffect(() => { + + useEffect(() => { setViewCount(research.total_views) incrementViewCount() - }, [research]) + }, [research._id]) return ( { + const viewsArray: string | null = sessionStorage.getItem(key) + if (typeof viewsArray === 'string') { + return JSON.parse(viewsArray) + } + else { + return [] + } +} + +export const addIDToSessionStorageArray = (key: string, value: string) => { + const sessionStorageArray = retrieveSessionStorageArray(key) + sessionStorageArray.push(value) + sessionStorage.setItem(key, JSON.stringify(sessionStorageArray)) +} \ No newline at end of file From 6a745afc1fc727ab478e53a2ed4144b36c1f1a02 Mon Sep 17 00:00:00 2001 From: AlfonsoGhislieri Date: Thu, 2 Feb 2023 18:29:41 +0000 Subject: [PATCH 5/9] chore: fixed linting and left margin --- .../src/ViewsCounter/ViewsCounter.tsx | 11 ++-- packages/components/src/index.ts | 2 +- .../HowtoDescription/HowtoDescription.tsx | 60 +++++++++---------- .../Research/Content/ResearchDescription.tsx | 19 +++--- src/stores/Howto/howto.store.tsx | 22 ++++--- src/stores/Research/research.store.tsx | 11 ++-- src/stores/databaseV2/DocReference.tsx | 7 ++- src/utils/sessionStorage.ts | 6 +- 8 files changed, 71 insertions(+), 67 deletions(-) diff --git a/packages/components/src/ViewsCounter/ViewsCounter.tsx b/packages/components/src/ViewsCounter/ViewsCounter.tsx index 570e625e1b..06999eb63d 100644 --- a/packages/components/src/ViewsCounter/ViewsCounter.tsx +++ b/packages/components/src/ViewsCounter/ViewsCounter.tsx @@ -2,11 +2,10 @@ import { Text } from 'theme-ui' import { Button } from '..' export interface IProps { - viewsCount: number + viewsCount: number } export const ViewsCounter = (props: IProps) => { - return ( <> diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 8d9925b138..500b7b84a4 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -23,7 +23,7 @@ export { ImageGallery } from './ImageGallery/ImageGallery' export { Loader } from './Loader/Loader' export { LinkifyText } from './LinkifyText/LinkifyText' export { UsefulStatsButton } from './UsefulStatsButton/UsefulStatsButton' -export { ViewsCounter} from './ViewsCounter/ViewsCounter' +export { ViewsCounter } from './ViewsCounter/ViewsCounter' export { CategoryTag } from './CategoryTag/CategoryTag' export { FileInformation } from './FileInformation/FileInformation' export { OsmGeocoding } from './OsmGeocoding/OsmGeocoding' diff --git a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx index 7a323511ed..4971149409 100644 --- a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx @@ -1,5 +1,5 @@ import { format } from 'date-fns' -import { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react' import type { IHowtoDB } from 'src/models/howto.models' import { Heading, Text, Box, Flex, Image, AspectImage } from 'theme-ui' import StepsIcon from 'src/assets/icons/icon-steps.svg' @@ -13,7 +13,7 @@ import { CategoryTag, FileInformation, Username, - ViewsCounter + ViewsCounter, } from 'oa-components' import type { IUser } from 'src/models/user.models' import { @@ -32,8 +32,10 @@ import { addHowtoDownloadCooldown, updateHowtoDownloadCooldown, } from './downloadCooldown' -import {retrieveSessionStorageArray, addIDToSessionStorageArray} from'src/utils/sessionStorage' - +import { + retrieveSessionStorageArray, + addIDToSessionStorageArray, +} from 'src/utils/sessionStorage' interface IProps { howto: IHowtoDB & { taglist: any } @@ -51,16 +53,16 @@ const HowtoDescription: React.FC = ({ loggedInUser, ...props }) => { - - const [fileDownloadCount, setFileDownloadCount] = useState(howto.total_downloads) + const [fileDownloadCount, setFileDownloadCount] = useState( + howto.total_downloads, + ) const [viewCount, setViewCount] = useState(howto.total_views) const { stores } = useCommonStores() const incrementDownloadCount = async () => { - const updatedDownloadCount = - await stores.howtoStore.incrementDownloadCount( - howto._id - ) + const updatedDownloadCount = await stores.howtoStore.incrementDownloadCount( + howto._id, + ) setFileDownloadCount(updatedDownloadCount!) } @@ -68,17 +70,17 @@ const HowtoDescription: React.FC = ({ const sessionStorageArray = retrieveSessionStorageArray('howto') console.log(sessionStorageArray) - if (!(sessionStorageArray.includes(howto._id))) { - const updatedViewCount = await stores.howtoStore.incrementViewCount(howto._id) - addIDToSessionStorageArray('howto',howto._id) + if (!sessionStorageArray.includes(howto._id)) { + const updatedViewCount = await stores.howtoStore.incrementViewCount( + howto._id, + ) + addIDToSessionStorageArray('howto', howto._id) setViewCount(updatedViewCount) } } const handleClick = async () => { - const howtoDownloadCooldown = retrieveHowtoDownloadCooldown( - howto._id - ) + const howtoDownloadCooldown = retrieveHowtoDownloadCooldown(howto._id) if ( howtoDownloadCooldown && @@ -101,17 +103,17 @@ const HowtoDescription: React.FC = ({ return '' } } - + useEffect(() => { setFileDownloadCount(howto.total_downloads) setViewCount(howto.total_views) incrementViewCount() - }, [howto._id]) + }, [howto._id]) const iconFlexDirection = emStringToPx(theme.breakpoints[0]) > window.innerWidth ? 'column' : 'row' - - return ( + + return ( = ({ {props.votedUsefulCount !== undefined && ( - + = ({ /> )} - - - + + + {/* Check if pin should be moderated */} {props.needsModeration && ( @@ -221,9 +221,7 @@ const HowtoDescription: React.FC = ({ {/* HACK 2021-07-16 - new howtos auto capitalize title but not older */} {capitalizeFirstLetter(howto.title)} - + {howto.description} @@ -304,9 +302,7 @@ const HowtoDescription: React.FC = ({ }} > {fileDownloadCount} - {fileDownloadCount !== 1 - ? ' downloads' - : ' download'} + {fileDownloadCount !== 1 ? ' downloads' : ' download'} )} diff --git a/src/pages/Research/Content/ResearchDescription.tsx b/src/pages/Research/Content/ResearchDescription.tsx index 9ce97ab127..32b4afddf5 100644 --- a/src/pages/Research/Content/ResearchDescription.tsx +++ b/src/pages/Research/Content/ResearchDescription.tsx @@ -1,6 +1,6 @@ import { format } from 'date-fns' import * as React from 'react' -import {useEffect, useState} from 'react' +import { useEffect, useState } from 'react' import { Box, Flex, Image, Text, Heading } from 'theme-ui' import ArrowIcon from 'src/assets/icons/icon-arrow-select.svg' import { @@ -9,7 +9,7 @@ import { ModerationStatus, UsefulStatsButton, Username, - ViewsCounter + ViewsCounter, } from 'oa-components' import type { IResearch } from 'src/models/research.models' import theme from 'src/themes/styled.theme' @@ -17,7 +17,10 @@ import type { IUser } from 'src/models/user.models' import { Link } from 'react-router-dom' import { isUserVerified } from 'src/common/isUserVerified' import { useResearchStore } from 'src/stores/Research/research.store' -import {retrieveSessionStorageArray, addIDToSessionStorageArray} from'src/utils/sessionStorage' +import { + retrieveSessionStorageArray, + addIDToSessionStorageArray, +} from 'src/utils/sessionStorage' interface IProps { research: IResearch.ItemDB @@ -51,17 +54,17 @@ const ResearchDescription: React.FC = ({ const incrementViewCount = async () => { const sessionStorageArray = retrieveSessionStorageArray('research') - if (!(sessionStorageArray.includes(research._id))) { + if (!sessionStorageArray.includes(research._id)) { const updatedViewCount = await store.incrementViewCount(research._id) setViewCount(updatedViewCount) - addIDToSessionStorageArray('research',research._id) + addIDToSessionStorageArray('research', research._id) } } useEffect(() => { setViewCount(research.total_views) incrementViewCount() - }, [research._id]) + }, [research._id]) return ( = ({ )} - + {/* Check if research should be moderated */} {props.needsModeration && ( diff --git a/src/stores/Howto/howto.store.tsx b/src/stores/Howto/howto.store.tsx index db12298a0b..3dda98a461 100644 --- a/src/stores/Howto/howto.store.tsx +++ b/src/stores/Howto/howto.store.tsx @@ -185,10 +185,13 @@ export class HowtoStore extends ModuleStore { total_downloads: totalDownloads! + 1, } - dbRef.set({ - ... updatedHowto - }, {keep_modified_timestamp: true}) - + dbRef.set( + { + ...updatedHowto, + }, + { keep_modified_timestamp: true }, + ) + return updatedHowto.total_downloads } } @@ -204,10 +207,13 @@ export class HowtoStore extends ModuleStore { total_views: totalViews! + 1, } - dbRef.set({ - ... updatedHowto - }, {keep_modified_timestamp: true}) - + dbRef.set( + { + ...updatedHowto, + }, + { keep_modified_timestamp: true }, + ) + return updatedHowto.total_views } } diff --git a/src/stores/Research/research.store.tsx b/src/stores/Research/research.store.tsx index f9350a4386..2bcf46ec24 100644 --- a/src/stores/Research/research.store.tsx +++ b/src/stores/Research/research.store.tsx @@ -157,11 +157,14 @@ export class ResearchStore extends ModuleStore { total_views: totalViews! + 1, } - dbRef.set({ - ... updatedResearch - }, {keep_modified_timestamp: true}) + dbRef.set( + { + ...updatedResearch, + }, + { keep_modified_timestamp: true }, + ) - return updatedResearch.total_views + return updatedResearch.total_views } } diff --git a/src/stores/databaseV2/DocReference.tsx b/src/stores/databaseV2/DocReference.tsx index 9556a60c43..4773372968 100644 --- a/src/stores/databaseV2/DocReference.tsx +++ b/src/stores/databaseV2/DocReference.tsx @@ -59,7 +59,7 @@ export class DocReference { * If contains metadata fields (e.g. `_id`) * then this will be used instead of generated id */ - async set(data: T, options?:{keep_modified_timestamp:boolean}) { + async set(data: T, options?: { keep_modified_timestamp: boolean }) { const { serverDB, cacheDB } = this.clients const dbDoc: DBDoc = this._setDocMeta(data, options) await serverDB.setDoc(this.endpoint, dbDoc) @@ -93,8 +93,9 @@ export class DocReference { private _setDocMeta(data: any = {}, options: any = {}): DBDoc { const d = data const o = options - const modifiedTimestamp = - o.keep_modified_timestamp ? d._modified : new Date().toISOString() + const modifiedTimestamp = o.keep_modified_timestamp + ? d._modified + : new Date().toISOString() return { ...d, diff --git a/src/utils/sessionStorage.ts b/src/utils/sessionStorage.ts index dbcbce831d..ee22b050a7 100644 --- a/src/utils/sessionStorage.ts +++ b/src/utils/sessionStorage.ts @@ -1,10 +1,8 @@ - export const retrieveSessionStorageArray = (key: string) => { const viewsArray: string | null = sessionStorage.getItem(key) if (typeof viewsArray === 'string') { return JSON.parse(viewsArray) - } - else { + } else { return [] } } @@ -13,4 +11,4 @@ export const addIDToSessionStorageArray = (key: string, value: string) => { const sessionStorageArray = retrieveSessionStorageArray(key) sessionStorageArray.push(value) sessionStorage.setItem(key, JSON.stringify(sessionStorageArray)) -} \ No newline at end of file +} From 49fb5b5e07bb5c14a8fa2e4722ffbe5880f1fd25 Mon Sep 17 00:00:00 2001 From: AlfonsoGhislieri Date: Sat, 4 Feb 2023 15:32:37 +0000 Subject: [PATCH 6/9] chore: removed useeffect state initialization --- src/pages/Howto/Content/Howto/Howto.tsx | 1 + .../Content/Howto/HowtoDescription/HowtoDescription.tsx | 8 +------- src/pages/Research/Content/ResearchArticle.tsx | 1 + src/pages/Research/Content/ResearchDescription.tsx | 3 +-- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/pages/Howto/Content/Howto/Howto.tsx b/src/pages/Howto/Content/Howto/Howto.tsx index cf3174a741..c639b28b32 100644 --- a/src/pages/Howto/Content/Howto/Howto.tsx +++ b/src/pages/Howto/Content/Howto/Howto.tsx @@ -191,6 +191,7 @@ export class Howto extends React.Component< <> void } -const HowtoDescription: React.FC = ({ - howto, - loggedInUser, - ...props -}) => { +const HowtoDescription = ({ howto, loggedInUser, ...props }: IProps) => { const [fileDownloadCount, setFileDownloadCount] = useState( howto.total_downloads, ) @@ -105,8 +101,6 @@ const HowtoDescription: React.FC = ({ } useEffect(() => { - setFileDownloadCount(howto.total_downloads) - setViewCount(howto.total_views) incrementViewCount() }, [howto._id]) diff --git a/src/pages/Research/Content/ResearchArticle.tsx b/src/pages/Research/Content/ResearchArticle.tsx index 05a7f8cc51..2aa2290861 100644 --- a/src/pages/Research/Content/ResearchArticle.tsx +++ b/src/pages/Research/Content/ResearchArticle.tsx @@ -119,6 +119,7 @@ const ResearchArticle = observer((props: IProps) => { = ({ research, isEditable, ...props -}) => { +}: IProps) => { const dateLastUpdateText = (research: IResearch.ItemDB): string => { const lastModifiedDate = format(new Date(research._modified), 'DD-MM-YYYY') const creationDate = format(new Date(research._created), 'DD-MM-YYYY') @@ -62,7 +62,6 @@ const ResearchDescription: React.FC = ({ } useEffect(() => { - setViewCount(research.total_views) incrementViewCount() }, [research._id]) From bf57b474ce62a96eeb9a17abeb642c59441a2e0d Mon Sep 17 00:00:00 2001 From: AlfonsoGhislieri Date: Sat, 4 Feb 2023 17:55:30 +0000 Subject: [PATCH 7/9] feat: aadded views svg and modified viewscounter --- .../components/assets/icons/icon-views.svg | 1 + packages/components/src/Icon/Icon.tsx | 1 + packages/components/src/Icon/svgs.tsx | 2 + packages/components/src/Icon/types.ts | 1 + .../src/ViewsCounter/ViewsCounter.tsx | 46 +++++++++---------- .../HowtoDescription/HowtoDescription.tsx | 12 ++--- .../Research/Content/ResearchDescription.tsx | 6 +-- 7 files changed, 36 insertions(+), 33 deletions(-) create mode 100644 packages/components/assets/icons/icon-views.svg diff --git a/packages/components/assets/icons/icon-views.svg b/packages/components/assets/icons/icon-views.svg new file mode 100644 index 0000000000..da95992128 --- /dev/null +++ b/packages/components/assets/icons/icon-views.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/Icon/Icon.tsx b/packages/components/src/Icon/Icon.tsx index 089ea31a17..86325533f8 100644 --- a/packages/components/src/Icon/Icon.tsx +++ b/packages/components/src/Icon/Icon.tsx @@ -95,6 +95,7 @@ export const glyphs: IGlyphs = { useful: iconMap.useful, thunderbolt: , filter: , + view: iconMap.view, } type WrapperProps = IProps & VerticalAlignProps & SpaceProps diff --git a/packages/components/src/Icon/svgs.tsx b/packages/components/src/Icon/svgs.tsx index 4d6fa9a629..5c6ddff39a 100644 --- a/packages/components/src/Icon/svgs.tsx +++ b/packages/components/src/Icon/svgs.tsx @@ -4,6 +4,7 @@ import starActiveSVG from '../../assets/icons/icon-star-active.svg' import verifiedSVG from '../../assets/icons/icon-verified-badge.svg' import usefulSVG from '../../assets/icons/icon-useful.svg' import commentSVG from '../../assets/icons/icon-comment.svg' +import viewSVG from '../../assets/icons/icon-views.svg' const imgStyle = { maxWidth: '100%', @@ -16,4 +17,5 @@ export const iconMap = { verified: icon, useful: icon, comment: icon, + view: icon, } diff --git a/packages/components/src/Icon/types.ts b/packages/components/src/Icon/types.ts index b25a73f19f..24db17b822 100644 --- a/packages/components/src/Icon/types.ts +++ b/packages/components/src/Icon/types.ts @@ -40,5 +40,6 @@ export type availableGlyphs = | 'useful' | 'verified' | 'filter' + | 'view' export type IGlyphs = { [k in availableGlyphs]: JSX.Element } diff --git a/packages/components/src/ViewsCounter/ViewsCounter.tsx b/packages/components/src/ViewsCounter/ViewsCounter.tsx index 06999eb63d..9df37e4eed 100644 --- a/packages/components/src/ViewsCounter/ViewsCounter.tsx +++ b/packages/components/src/ViewsCounter/ViewsCounter.tsx @@ -1,33 +1,31 @@ -import { Text } from 'theme-ui' -import { Button } from '..' +import { Text, Flex } from 'theme-ui' +import { Icon } from '..' +import { useTheme } from '@emotion/react' export interface IProps { viewsCount: number } export const ViewsCounter = (props: IProps) => { + const theme: any = useTheme() + return ( - <> - - + + + + {props.viewsCount} + {props.viewsCount !== 1 ? ' views' : ' view'} + + ) } diff --git a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx index 8b346fd583..824129e9a3 100644 --- a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx @@ -37,6 +37,9 @@ import { addIDToSessionStorageArray, } from 'src/utils/sessionStorage' +const iconFlexDirection = + emStringToPx(theme.breakpoints[0]) > window.innerWidth ? 'column' : 'row' + interface IProps { howto: IHowtoDB & { taglist: any } loggedInUser: IUser | undefined @@ -104,9 +107,6 @@ const HowtoDescription = ({ howto, loggedInUser, ...props }: IProps) => { incrementViewCount() }, [howto._id]) - const iconFlexDirection = - emStringToPx(theme.breakpoints[0]) > window.innerWidth ? 'column' : 'row' - return ( { width: ['100%', '100%', `${(1 / 2) * 100}%`], }} > - + {props.votedUsefulCount !== undefined && ( - + { /> )} - + {/* Check if pin should be moderated */} diff --git a/src/pages/Research/Content/ResearchDescription.tsx b/src/pages/Research/Content/ResearchDescription.tsx index 5997ecae3c..2d234c8788 100644 --- a/src/pages/Research/Content/ResearchDescription.tsx +++ b/src/pages/Research/Content/ResearchDescription.tsx @@ -82,7 +82,7 @@ const ResearchDescription: React.FC = ({ }} > - + {props.votedUsefulCount !== undefined && ( - + = ({ /> )} - + {/* Check if research should be moderated */} From b6d7895f3a377a9c850fb68157655ff60e02ad17 Mon Sep 17 00:00:00 2001 From: AlfonsoGhislieri Date: Sat, 4 Feb 2023 18:00:37 +0000 Subject: [PATCH 8/9] feat: viewscounter only viewable for beta-testers --- .../Content/Howto/HowtoDescription/HowtoDescription.tsx | 9 ++++++--- src/pages/Research/Content/ResearchDescription.tsx | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx index 824129e9a3..39db23fa6e 100644 --- a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx @@ -36,6 +36,7 @@ import { retrieveSessionStorageArray, addIDToSessionStorageArray, } from 'src/utils/sessionStorage' +import { AuthWrapper } from 'src/common/AuthWrapper' const iconFlexDirection = emStringToPx(theme.breakpoints[0]) > window.innerWidth ? 'column' : 'row' @@ -162,9 +163,11 @@ const HowtoDescription = ({ howto, loggedInUser, ...props }: IProps) => { /> )} - - - + + + + + {/* Check if pin should be moderated */} {props.needsModeration && ( diff --git a/src/pages/Research/Content/ResearchDescription.tsx b/src/pages/Research/Content/ResearchDescription.tsx index 2d234c8788..be10e54dc8 100644 --- a/src/pages/Research/Content/ResearchDescription.tsx +++ b/src/pages/Research/Content/ResearchDescription.tsx @@ -21,6 +21,7 @@ import { retrieveSessionStorageArray, addIDToSessionStorageArray, } from 'src/utils/sessionStorage' +import { AuthWrapper } from 'src/common/AuthWrapper' interface IProps { research: IResearch.ItemDB @@ -113,9 +114,11 @@ const ResearchDescription: React.FC = ({ /> )} - - - + + + + + {/* Check if research should be moderated */} {props.needsModeration && ( From 765a49c14297d76b9689d6a0238d4c9fb3a0876c Mon Sep 17 00:00:00 2001 From: AlfonsoGhislieri Date: Sun, 5 Feb 2023 14:08:40 +0000 Subject: [PATCH 9/9] chore: removed react fc from research desc --- .../Content/Howto/HowtoDescription/HowtoDescription.tsx | 1 - src/pages/Research/Content/ResearchDescription.tsx | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx index 39db23fa6e..d15a8d137a 100644 --- a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx @@ -68,7 +68,6 @@ const HowtoDescription = ({ howto, loggedInUser, ...props }: IProps) => { const incrementViewCount = async () => { const sessionStorageArray = retrieveSessionStorageArray('howto') - console.log(sessionStorageArray) if (!sessionStorageArray.includes(howto._id)) { const updatedViewCount = await stores.howtoStore.incrementViewCount( diff --git a/src/pages/Research/Content/ResearchDescription.tsx b/src/pages/Research/Content/ResearchDescription.tsx index be10e54dc8..af618b24c7 100644 --- a/src/pages/Research/Content/ResearchDescription.tsx +++ b/src/pages/Research/Content/ResearchDescription.tsx @@ -34,11 +34,7 @@ interface IProps { onUsefulClick: () => void } -const ResearchDescription: React.FC = ({ - research, - isEditable, - ...props -}: IProps) => { +const ResearchDescription = ({ research, isEditable, ...props }: IProps) => { const dateLastUpdateText = (research: IResearch.ItemDB): string => { const lastModifiedDate = format(new Date(research._modified), 'DD-MM-YYYY') const creationDate = format(new Date(research._created), 'DD-MM-YYYY')