Skip to content
This repository has been archived by the owner on Apr 25, 2022. It is now read-only.

Show addons on detail page #128

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions pages/apps/details/[appDetails].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import {
fetchAppstream,
fetchAppStats,
fetchSummary,
fetchAddons,
} from '../../../src/fetchers'
import { APPSTREAM_URL } from '../../../src/env'
import { NextSeo } from 'next-seo'
import {
Appstream,
AddonAppstream,
DesktopAppstream,
pickScreenshot,
Screenshot,
} from '../../../src/types/Appstream'
Expand All @@ -21,15 +23,17 @@ export default function Details({
data,
summary,
stats,
addons,
}: {
data: Appstream
data: DesktopAppstream
summary: Summary
stats: AppStats
addons: AddonAppstream[]
}) {
const screenshots = data.screenshots
? data.screenshots.filter(pickScreenshot).map((screenshot: Screenshot) => ({
url: pickScreenshot(screenshot).url,
}))
url: pickScreenshot(screenshot).url,
}))
: []

return (
Expand All @@ -46,7 +50,7 @@ export default function Details({
],
}}
/>
<ApplicationDetails data={data} summary={summary} stats={stats} />
<ApplicationDetails data={data} summary={summary} stats={stats} addons={addons} />
</Main>
)
}
Expand All @@ -58,12 +62,14 @@ export const getStaticProps: GetStaticProps = async ({
const data = await fetchAppstream(appDetails as string)
const summary = await fetchSummary(appDetails as string)
const stats = await fetchAppStats(appDetails as string)
const addons = await fetchAddons(appDetails as string)

return {
props: {
data,
summary,
stats,
addons
},
}
}
Expand Down
64 changes: 64 additions & 0 deletions src/components/application/Addons.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.addons {
background-color: var(--bg-color-secondary);
border-radius: var(--border-radius);

.addon {
width: 100%;
padding: 0px 24px;
border: 1px solid var(--border);
border-bottom-width: 0;

display: flex;
flex-direction: column;

&:first-child {
border-top-right-radius: 8px;
border-top-left-radius: 8px;
}

&:last-child {
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
border-bottom-width: 1px;
}

.time {
align-self: center;
text-align: right;
font-size: 0.9rem;
}

.externalLink {
filter: opacity(0.6);
align-self: center;

&:hover {
filter: opacity(1);
}
}
}
}

.tooltip {
position: relative;
align-self: center;
}

.tooltip .tooltiptext {
visibility: hidden;
width: 200px;
background-color: var(--bg-color-secondary);
color: var(--text-primary);
text-align: center;
border-radius: var(--border-radius);
padding: 4px;
position: absolute;
z-index: 1;
top: -40px;
right: 110%;
border: 1px solid var(--border);
}

.tooltip:hover .tooltiptext {
visibility: visible;
}
91 changes: 91 additions & 0 deletions src/components/application/Addons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { useMatomo } from '@datapunt/matomo-tracker-react'
import { formatDistance } from 'date-fns'
import { FunctionComponent, useState } from 'react'
import { MdInfo, MdOpenInNew } from 'react-icons/md'

import { AddonAppstream } from '../../types/Appstream'
import styles from './Addons.module.scss'

interface Props {
addons: AddonAppstream[]
}

const Addons: FunctionComponent<Props> = ({ addons }) => {
const { trackEvent } = useMatomo()

const [isInfoVisible, setInfoVisible] = useState(false)

function toggleInfoVisible() {
setInfoVisible(!isInfoVisible)
}

return (
<>
{addons && addons.length > 0 && (
<>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<h3>Available add-ons</h3>
<>
<span className={styles.tooltip} onClick={toggleInfoVisible}>
<MdInfo />
<div
className={styles.tooltiptext}
style={isInfoVisible ? { visibility: 'visible' } : { display: 'hidden' }}
>
Please use the flatpak manager supplied by your distribution to install add-ons.
</div>
</span>
</>
</div>
<div className={styles.addons}>
{addons.map(addon => {
const latestRelease = addon.releases ? addon.releases[0] : null
const linkClicked = () => {
trackEvent({
category: 'App',
action: 'AddonHomepage',
name: addon.id ?? 'unknownAddon',
})
}
return (
<div className={styles.addon} key={addon.id}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<h3>{addon.name}</h3>
{latestRelease && latestRelease.timestamp && (
<div className={styles.time}>
updated {formatDistance(
new Date(latestRelease.timestamp * 1000),
new Date(),
{ addSuffix: true }
)}
</div>)
}
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<p>{addon.summary}</p>
{addon.urls.homepage && (
<div className={styles.externalLink}>
<a
href={addon.urls.homepage}
target='_blank'
rel='noreferrer'
onClick={linkClicked}
title='Open in new tab'
>
<MdOpenInNew />
</a>
</div>
)}
</div>
</div>
)
})}
</div>
</>
)
}
</>
)
}

export default Addons
23 changes: 20 additions & 3 deletions src/components/application/Details.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { useMatomo } from '@datapunt/matomo-tracker-react'
import { FunctionComponent, useState } from 'react'
import { Carousel } from 'react-responsive-carousel'
import { Appstream, pickScreenshot } from '../../types/Appstream'
import {
AddonAppstream,
DesktopAppstream,
pickScreenshot,
} from '../../types/Appstream'

import { Summary } from '../../types/Summary'

Expand All @@ -18,11 +22,13 @@ import { SoftwareAppJsonLd, VideoGameJsonLd } from 'next-seo'
import Lightbox from 'react-image-lightbox'

import 'react-image-lightbox/style.css' // This only needs to be imported once in your app
import Addons from './Addons'

interface Props {
data: Appstream
data: DesktopAppstream
summary: Summary
stats: AppStats
addons: AddonAppstream[]
}

function categoryToSeoCategories(categories: string[]) {
Expand Down Expand Up @@ -58,7 +64,12 @@ function categoryToSeoCategory(category) {
}
}

const Details: FunctionComponent<Props> = ({ data, summary, stats }) => {
const Details: FunctionComponent<Props> = ({
data,
summary,
stats,
addons,
}) => {
const [showLightbox, setShowLightbox] = useState(false)
const [currentScreenshot, setCurrentScreenshot] = useState(0)

Expand Down Expand Up @@ -206,6 +217,12 @@ const Details: FunctionComponent<Props> = ({ data, summary, stats }) => {

<Releases releases={data.releases}></Releases>

{addons.length > 0 && (
<div>
<Addons addons={addons}></Addons>
</div>
)}

<AdditionalInfo
data={data}
summary={summary}
Expand Down
4 changes: 4 additions & 0 deletions src/components/application/ListBox.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
.externalLink {
filter: opacity(0.6);
text-align: end;

&:hover {
filter: opacity(1);
}
}
}
}
2 changes: 1 addition & 1 deletion src/components/application/Releases.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
border-radius: var(--border-radius);

.releaseDetails {
padding: 8px 24px;
padding: 0px 24px;

header {
display: flex;
Expand Down
2 changes: 1 addition & 1 deletion src/components/application/Releases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const Releases: FunctionComponent<Props> = ({ releases }) => {
<header>
<h3>Changes in version {latestRelease.version}</h3>
<div>
{latestRelease.timestamp &&
updated {latestRelease.timestamp &&
formatDistance(
new Date(latestRelease.timestamp * 1000),
new Date(),
Expand Down
2 changes: 2 additions & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export const EDITORS_PICKS_APPS_URL: string = `${BASE_URI}/picks/apps`
export const RECENTLY_UPDATED_URL: string = `${BASE_URI}/collection/recently-updated`
export const CATEGORY_URL = (category: keyof typeof Category): string =>
`${BASE_URI}/category/${category}`
export const ADDONS_URL = (appid: string): string =>
`${BASE_URI}/addon/${appid}`
export const FEED_RECENTLY_UPDATED_URL: string = `${BASE_URI}/feed/recently-updated`
export const FEED_NEW_URL: string = `${BASE_URI}/feed/new`

Expand Down
18 changes: 15 additions & 3 deletions src/fetchers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Appstream } from './types/Appstream'
import { AddonAppstream, Appstream, DesktopAppstream } from './types/Appstream'
import { Collection, Collections } from './types/Collection'
import { Category } from './types/Category'

Expand All @@ -13,13 +13,14 @@ import {
SUMMARY_DETAILS,
STATS_DETAILS,
STATS,
ADDONS_URL,
} from './env'
import { Summary } from './types/Summary'
import { AppStats } from './types/AppStats'
import { Stats } from './types/Stats'

export async function fetchAppstream(appId: string): Promise<Appstream> {
let entryJson: Appstream
export async function fetchAppstream(appId: string): Promise<DesktopAppstream> {
let entryJson: DesktopAppstream
try {
const entryData = await fetch(`${APP_DETAILS(appId)}`)
entryJson = await entryData.json()
Expand Down Expand Up @@ -135,6 +136,17 @@ export async function fetchCategory(category: keyof typeof Category) {
return items.filter((item) => Boolean(item))
}

export async function fetchAddons(appid: string) {
const appListRes = await fetch(ADDONS_URL(appid))
const appList = await appListRes.json()

const items: AddonAppstream[] = await Promise.all(appList.map(fetchAppstream))

console.log('\nAddons for ', appid, ' fetched')

return items.filter((item) => Boolean(item))
}

export async function fetchSearchQuery(query: string) {
const appListRes = await fetch(SEARCH_APP(query))
const appList = await appListRes.json()
Expand Down
18 changes: 17 additions & 1 deletion src/types/Appstream.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export interface Appstream {
export type Appstream = DesktopAppstream | AddonAppstream

export interface DesktopAppstream {
type: "desktop"
description: string
screenshots?: Screenshot[]
releases: Release[]
Expand All @@ -18,6 +21,19 @@ export interface Appstream {
bundle: Bundle
}

export interface AddonAppstream {
type: "addon"
releases: Release[];
urls: Urls;
icon?: any;
id: string;
name: string;
summary: string;
project_license?: string;
extends: string;
bundle: Bundle;
}

interface ContentRating {
type: string
'violence-cartoon': ContentRatingLevel
Expand Down