Skip to content

Commit

Permalink
Show download count on core info pages
Browse files Browse the repository at this point in the history
  • Loading branch information
neil-morrison44 committed Jul 13, 2024
1 parent c2f676b commit ce07c76
Show file tree
Hide file tree
Showing 10 changed files with 358 additions and 23 deletions.
100 changes: 100 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@tauri-apps/api": "^1.5.3",
"@types/three": "0.162.0",
"@zip.js/zip.js": "^2.7.40",
"d3-scale": "^4.0.2",
"date-fns": "^3.5.0",
"fast-fuzzy": "^1.12.0",
"html-react-parser": "^5.1.8",
Expand All @@ -39,6 +40,7 @@
"devDependencies": {
"@octokit/rest": "^20.1.0",
"@tauri-apps/cli": "^1.5.11",
"@types/d3-scale": "^4.0.8",
"@types/jsonlint": "^1.6.3",
"@types/node": "^20.11.28",
"@types/react": "^18.2.66",
Expand Down
169 changes: 169 additions & 0 deletions src/components/cores/info/downloadCounts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { useRecoilValue } from "recoil"
import { useInventoryItem } from "../../../../hooks/useInventoryItem"
import { GithubReleasesSelectorFamily } from "../../../../recoil/github/selectors"
import { GithubRelease, InventoryItem } from "../../../../types"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"

import { parseISO, differenceInMilliseconds } from "date-fns"
import { scaleLinear, scalePow } from "d3-scale"

type DownloadCountProps = {
coreName: string
}

export const DownloadCount = ({ coreName }: DownloadCountProps) => {
const inventoryItem = useInventoryItem(coreName)
if (inventoryItem?.repository.platform !== "github") return null

return <DownloadCountInner inventoryItem={inventoryItem} />
}

const DownloadCountInner = ({
inventoryItem,
}: {
inventoryItem: InventoryItem
}) => {
const { t } = useTranslation("core_info")

const { owner, name: repo } = inventoryItem.repository
const githubReleases = useRecoilValue(
GithubReleasesSelectorFamily({
owner,
repo,
latest: inventoryItem.version,
})
)

const latestCount = useMemo(() => {
const latestRelease = githubReleases.find(
({ draft, prerelease }) =>
(!draft && !owner.startsWith("eric")) || prerelease
)
if (!latestRelease) return 0
const latest = findMostDownloadedAsset(latestRelease)
if (!latest) return 0
return latest.download_count
}, [githubReleases, owner])

const latestTag = inventoryItem.version

const totalCount = useMemo(() => {
return githubReleases
.map((release) => {
const asset = findMostDownloadedAsset(release)
return asset?.download_count || 0
})
.reduce((prev, curr) => prev + curr, 0)
}, [githubReleases])

const perVersionCounts = useMemo(() => {
const currentDate = new Date(Date.now())

return githubReleases.map((release, index) => {
const releaseDate = parseISO(release.published_at)
const asset = findMostDownloadedAsset(release)
const downloadCount = asset?.download_count || 0

const replacedOn =
index === 0
? currentDate
: new Date(githubReleases[index - 1].published_at)

const daysAsLatest =
differenceInMilliseconds(replacedOn, releaseDate) / 1000 / 60 / 60 / 24

const daysAgo =
differenceInMilliseconds(currentDate, releaseDate) / 1000 / 60 / 60 / 24

return {
release: release.tag_name,
downloadCount,
downloadsPerDay: downloadCount / daysAsLatest,
daysAgo,
daysAsLatest,
}
})
}, [githubReleases])

if (githubReleases.length === 0) return null

return (
<div>
<div>{t("download_latest", { count: latestCount, tag: latestTag })}</div>
<div>{t("download_total", { count: totalCount })}</div>

{githubReleases.length > 1 && (
<DownloadGraph perVersions={perVersionCounts} />
)}
</div>
)
}

const findMostDownloadedAsset = (release: GithubRelease) =>
[...release.assets]
.sort((a, b) => b.download_count - a.download_count)
.find(({ name }) => name.endsWith(".zip"))

type DownloadGraphProps = {
perVersions: {
release: string
downloadCount: number
downloadsPerDay: number
daysAsLatest: number
daysAgo: number
}[]
}

const BAR_HEIGHT = 20
const BAR_WIDTH = 150

const DownloadGraph = ({ perVersions }: DownloadGraphProps) => {
const { t } = useTranslation("core_info")

const scaleY = scalePow(
[0, Math.max(...perVersions.map(({ downloadsPerDay }) => downloadsPerDay))],
[0, BAR_HEIGHT * 0.9]
).exponent(0.5)

const scaleX = scaleLinear(
[0, Math.max(...perVersions.map(({ daysAgo }) => daysAgo))],
[BAR_WIDTH, 0]
)

return (
<div
style={{
position: "relative",
width: BAR_WIDTH,
height: BAR_HEIGHT,
display: "block",
}}
>
{perVersions.map(
({
downloadsPerDay,
downloadCount,
release,
daysAsLatest,
daysAgo,
}) => (
<div
key={release}
className="core-info__download-graph-bar"
style={{
left: scaleX(daysAgo),
width: Math.max(2, BAR_WIDTH - scaleX(daysAsLatest)),
height: scaleY(downloadsPerDay),
}}
title={t("download_bar_title", {
release,
count: downloadCount,
days: Math.round(daysAsLatest),
})}
></div>
)
)}
</div>
)
}
23 changes: 22 additions & 1 deletion src/components/cores/info/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
}

.core-info__analogizer{
background-color: rgb(101,156,222);
background-color: rgb(101 156 222);
}

.core-info__firmware-warning{
Expand Down Expand Up @@ -97,6 +97,12 @@
}
}

.core-info__info-row--hide-if-null {
&:has(>:first-child:last-child){
display: none;
}
}

.core-info__info-row--right {
grid-column: 2;
}
Expand All @@ -105,6 +111,7 @@
display: grid;
gap: inherit;
grid-template-columns: 1fr auto;
grid-auto-flow: dense;
}

.core-info__author-tag {
Expand Down Expand Up @@ -157,3 +164,17 @@
.core-info__info-txt {
white-space: pre-wrap;
}


.core-info__download-graph-bar{
display: block;
position: absolute;
background-color: white;
opacity: 0.6;
bottom: 0;
cursor: pointer;

&:hover{
opacity: 1;
}
}
Loading

0 comments on commit ce07c76

Please sign in to comment.