diff --git a/components/Card.tsx b/components/Card.tsx
index 0d7fc532..5c7a68fc 100644
--- a/components/Card.tsx
+++ b/components/Card.tsx
@@ -1,5 +1,5 @@
import {createStyles} from '@mantine/core'
-import {cleanIframe} from '~/lib/helpers'
+import {Post} from '~/lib/types'
const useStyles = createStyles((theme) => ({
card: {
@@ -19,18 +19,19 @@ const useStyles = createStyles((theme) => ({
/**
* Card component.
*/
-export default function Card(props) {
+export default function Card(props: Post) {
const {classes} = useStyles()
return (
)
case 'link':
// Search for .gifv....
@@ -68,11 +102,18 @@ export default function Card(props) {
+ poster={props?.images?.url}
+ preload="metadata"
+ >
+
+
)
} else {
// No media? Return blank.
diff --git a/components/Footer.tsx b/components/Footer.tsx
new file mode 100644
index 00000000..60703ea9
--- /dev/null
+++ b/components/Footer.tsx
@@ -0,0 +1,43 @@
+import {createStyles} from '@mantine/core'
+import {IconBrandGithub} from '@tabler/icons'
+import config from '~/lib/config'
+
+const useStyles = createStyles((theme) => ({
+ footer: {
+ alignItems: 'center',
+ display: 'flex',
+ gap: theme.spacing.md,
+ justifyContent: 'center',
+ textAlign: 'center',
+
+ a: {
+ color: theme.colors.dark[0]
+ }
+ }
+}))
+
+/**
+ * Footer component.
+ */
+export default function Footer() {
+ const {classes} = useStyles()
+ return (
+
+ )
+}
diff --git a/components/Header.tsx b/components/Header.tsx
new file mode 100644
index 00000000..f545f494
--- /dev/null
+++ b/components/Header.tsx
@@ -0,0 +1,31 @@
+import {createStyles} from '@mantine/core'
+import config from '~/lib/config'
+
+const useStyles = createStyles((theme) => ({
+ header: {
+ alignItems: 'center',
+ display: 'flex',
+ justifyContent: 'space-between',
+ [`@media (max-width: ${theme.breakpoints.sm}px)`]: {
+ flexDirection: 'column',
+ textAlign: 'center'
+ }
+ },
+ title: {
+ fontSize: theme.fontSizes.xl,
+ margin: 0,
+ [`@media (max-width: ${theme.breakpoints.sm}px)`]: {
+ lineHeight: 1
+ }
+ }
+}))
+
+export default function Header() {
+ const {classes} = useStyles()
+ return (
+
+ {config.siteTitle}
+ {config.siteDescription}
+
+ )
+}
diff --git a/components/Meta.tsx b/components/Meta.tsx
new file mode 100644
index 00000000..7a86b5dd
--- /dev/null
+++ b/components/Meta.tsx
@@ -0,0 +1,53 @@
+import Head from 'next/head'
+import config from '~/lib/config'
+
+export default function Meta() {
+ return (
+
+ {config?.siteTitle}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/components/NoResults.tsx b/components/NoResults.tsx
index 513ab77d..7e49e37c 100644
--- a/components/NoResults.tsx
+++ b/components/NoResults.tsx
@@ -1,3 +1,4 @@
+import {Stack} from '@mantine/core'
import Image from 'next/future/image'
import notFound from '../public/not-found.webp'
@@ -6,12 +7,12 @@ import notFound from '../public/not-found.webp'
*/
export default function NoResults() {
return (
- <>
+
Either the Reddit API is down or something else is wrong. Please try
your search again.
-
- >
+
+
)
}
diff --git a/components/Results.tsx b/components/Results.tsx
index 1c11dfb0..955dd52e 100644
--- a/components/Results.tsx
+++ b/components/Results.tsx
@@ -1,4 +1,5 @@
import {Button, createStyles} from '@mantine/core'
+import dynamic from 'next/dynamic'
import {useEffect, useState} from 'react'
import {useInView} from 'react-intersection-observer'
import Masonry from 'react-masonry-css'
@@ -6,6 +7,11 @@ import Card from '~/components/Card'
import {useRedditContext} from '~/components/RedditProvider'
import SkeletonWrapper from '~/components/SkeletonWrapper'
import {fetchPosts} from '~/lib/helpers'
+
+const DynamicNoResults = dynamic(() => import('./NoResults'), {
+ ssr: false
+})
+
const breakpointColumnsObj = {
default: 3,
766: 1
@@ -83,10 +89,14 @@ export default function Results() {
}
}, [inView]) // eslint-disable-line react-hooks/exhaustive-deps
- if (loading || !posts) {
+ if (loading) {
return
}
+ if (!posts) {
+ return
+ }
+
return (
<>
({
+ settings: {}
+}))
+
+export default function Settings() {
+ const {classes} = useStyles()
+ const [opened, setOpened] = useState(false)
+ const {colorScheme, toggleColorScheme} = useMantineColorScheme()
+
+ return (
+ <>
+ setOpened(false)} opened={opened} title="Settings">
+ toggleColorScheme()}
+ onLabel="ON"
+ size="lg"
+ />
+
+
+
+
+
+ >
+ )
+}
diff --git a/lib/helpers.ts b/lib/helpers.ts
index 3b325ecd..416a8268 100644
--- a/lib/helpers.ts
+++ b/lib/helpers.ts
@@ -58,25 +58,3 @@ export async function fetchPosts({
}
}
}
-
-interface CleanIframeProps {
- html: string
-}
-
-/**
- * Replace the src attribute with a less terrible version.
- */
-export function cleanIframe({html}: CleanIframeProps) {
- // Grab the src URL.
- const source = html.match(/(src="([^"]+)")/gi)
-
- return ``
-}
diff --git a/lib/types.d.ts b/lib/types.d.ts
index 71a2f7c0..725a0ac6 100644
--- a/lib/types.d.ts
+++ b/lib/types.d.ts
@@ -4,21 +4,49 @@ export interface ChildrenProps {
children: React.ReactNode
}
+export interface Post {
+ id: string
+ images: {
+ height: number
+ url: string
+ width: number
+ }
+ media: {
+ reddit_video: {
+ dash_url: string
+ fallback_url: string
+ height: number
+ scrubber_media_url: string
+ hls_url: string
+ width: number
+ }
+ }
+ over_18: boolean
+ permalink: string
+ post_hint: string
+ score: number
+ subreddit: string
+ thumbnail: string
+ secure_media_embed: {
+ content: string
+ height: number
+ media_domain_url: string
+ scrolling: boolean
+ width: number
+ }
+ video_preview: {
+ dash_url: string
+ fallback_url: string
+ height: number
+ scrubber_media_url: string
+ hls_url: string
+ width: number
+ }
+ title: string
+ url: string
+}
+
export interface Posts {
after: string
- posts: [
- {
- id: string
- images: []
- media: string
- permalink: string
- secure_media: string
- subreddit: string
- thumbnail: string
- title: string
- type: string
- ups: number
- url: string
- }
- ]
+ posts: Post[]
}
diff --git a/package-lock.json b/package-lock.json
index 9d90163e..ba4cc321 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@gregrickaby/reddit-image-viewer",
- "version": "3.0.0",
+ "version": "3.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@gregrickaby/reddit-image-viewer",
- "version": "3.0.0",
+ "version": "3.0.1",
"license": "MIT",
"dependencies": {
"@emotion/react": "^11.10.4",
@@ -1924,9 +1924,9 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.4.271",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.271.tgz",
- "integrity": "sha512-BCPBtK07xR1/uY2HFDtl3wK2De66AW4MSiPlLrnPNxKC/Qhccxd59W73654S3y6Rb/k3hmuGJOBnhjfoutetXA==",
+ "version": "1.4.272",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.272.tgz",
+ "integrity": "sha512-KS6gPPGNrzpVv9HzFVq+Etd0AjZEPr5pvaTBn2yD6KV4+cKW4I0CJoJNgmTG6gUQPAMZ4wIPtcOuoou3qFAZCA==",
"peer": true
},
"node_modules/emoji-regex": {
@@ -4459,9 +4459,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz",
- "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
"funding": [
{
"type": "opencollective",
@@ -5949,9 +5949,9 @@
}
},
"electron-to-chromium": {
- "version": "1.4.271",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.271.tgz",
- "integrity": "sha512-BCPBtK07xR1/uY2HFDtl3wK2De66AW4MSiPlLrnPNxKC/Qhccxd59W73654S3y6Rb/k3hmuGJOBnhjfoutetXA==",
+ "version": "1.4.272",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.272.tgz",
+ "integrity": "sha512-KS6gPPGNrzpVv9HzFVq+Etd0AjZEPr5pvaTBn2yD6KV4+cKW4I0CJoJNgmTG6gUQPAMZ4wIPtcOuoou3qFAZCA==",
"peer": true
},
"emoji-regex": {
@@ -7779,9 +7779,9 @@
}
},
"update-browserslist-db": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz",
- "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
"peer": true,
"requires": {
"escalade": "^3.1.1",
diff --git a/package.json b/package.json
index fbc9c35a..57873817 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "@gregrickaby/reddit-image-viewer",
"private": true,
"description": "Anonymously browse images, videos, gifs, and other media from any subreddit.",
- "version": "3.0.0",
+ "version": "3.0.1",
"repository": {
"type": "git",
"url": "git+https://github.com/gregrickaby/reddit-image-viewer.git"
diff --git a/pages/_app.tsx b/pages/_app.tsx
index c927daa7..a5835f9a 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -1,24 +1,34 @@
+import {ColorScheme, ColorSchemeProvider, MantineProvider} from '@mantine/core'
+import {useColorScheme, useHotkeys, useLocalStorage} from '@mantine/hooks'
import {AppProps} from 'next/app'
-import {MantineProvider} from '@mantine/core'
import RedditProvider from '~/components/RedditProvider'
/**
- * App component.
+ * Custom App component.
*/
-export default function App(props: AppProps) {
- const {Component, pageProps} = props
+export default function App({Component, pageProps}: AppProps) {
+ const preferredColorScheme = useColorScheme()
+ const [colorScheme, setColorScheme] = useLocalStorage({
+ key: 'riv-color-scheme',
+ defaultValue: preferredColorScheme,
+ getInitialValueInEffect: true
+ })
+
+ const toggleColorScheme = (value?: ColorScheme) =>
+ setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark'))
+
+ useHotkeys([['mod+j', () => toggleColorScheme()]])
return (
-
-
-
-
-
+
+
+
+
+
+
)
}
diff --git a/pages/_document.tsx b/pages/_document.tsx
index a2482d92..96250cd4 100644
--- a/pages/_document.tsx
+++ b/pages/_document.tsx
@@ -1,58 +1,19 @@
import {createGetInitialProps} from '@mantine/next'
import Document, {Head, Html, Main, NextScript} from 'next/document'
-import config from '~/lib/config'
+// Mantine uses Next.js getInitialProps to load styles on server side.
const getInitialProps = createGetInitialProps()
+/**
+ * Custom document component.
+ */
export default class _Document extends Document {
static getInitialProps = getInitialProps
render() {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/pages/api/reddit.ts b/pages/api/reddit.ts
index 9a773715..b52cacf2 100644
--- a/pages/api/reddit.ts
+++ b/pages/api/reddit.ts
@@ -97,13 +97,15 @@ export default async function reddit(req: NextRequest) {
id: post.data.id,
images: post.data.preview.images[0].resolutions.pop(),
media: post.data.media,
+ video_preview: post.data.preview.reddit_video_preview,
+ over_18: post.data.over_18,
permalink: `https://www.reddit.com${post.data.permalink}`,
- secure_media: post.data.secure_media,
- subreddit: `https://www.reddit.com/${post.data.subreddit_name_prefixed}`,
+ post_hint: post.data.post_hint,
+ score: post.data.score,
+ secure_media_embed: post.data.secure_media_embed,
+ subreddit: post.data.subreddit,
thumbnail: post.data.thumbnail,
title: post.data.title,
- type: post.data.post_hint,
- ups: post.data.ups,
url: post.data.url
})),
after: json?.data?.after
diff --git a/pages/api/search.ts b/pages/api/search.ts
index 39fa590b..c5586b41 100644
--- a/pages/api/search.ts
+++ b/pages/api/search.ts
@@ -64,7 +64,7 @@ export default async function search(req: NextRequest) {
// Filter uneeded data to keep the payload small.
const filtered = subs.data.children.map((sub) => {
return {
- over18: sub.data.over18 ? true : false,
+ over18: sub.data.over18 ? 'true' : 'false',
url: sub.data.url ? sub.data.url : '',
value: sub.data.display_name ? sub.data.display_name : ''
}
diff --git a/pages/index.tsx b/pages/index.tsx
index 0627aa49..d1fbba89 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -1,11 +1,12 @@
import {createStyles} from '@mantine/core'
-import {IconBrandGithub} from '@tabler/icons'
-import Head from 'next/head'
import BackToTop from '~/components/BackToTop'
+import Footer from '~/components/Footer'
+import Header from '~/components/Header'
+import Meta from '~/components/Meta'
import Results from '~/components/Results'
import Search from '~/components/Search'
+import Settings from '~/components/Settings'
import Sort from '~/components/Sort'
-import config from '~/lib/config'
const useStyles = createStyles((theme) => ({
container: {
@@ -16,40 +17,13 @@ const useStyles = createStyles((theme) => ({
padding: theme.spacing.xl
}
},
- header: {
- alignItems: 'center',
- display: 'flex',
- justifyContent: 'space-between',
- [`@media (max-width: ${theme.breakpoints.sm}px)`]: {
- flexDirection: 'column',
- textAlign: 'center'
- }
- },
- title: {
- fontSize: theme.fontSizes.xl,
- margin: 0,
- [`@media (max-width: ${theme.breakpoints.sm}px)`]: {
- lineHeight: 1
- }
- },
- controls: {
+ search: {
display: 'flex',
gap: theme.spacing.md,
marginBottom: theme.spacing.xl
},
main: {
minHeight: '100vh'
- },
- footer: {
- alignItems: 'center',
- display: 'flex',
- gap: theme.spacing.md,
- justifyContent: 'center',
- textAlign: 'center',
-
- a: {
- color: theme.colors.dark[0]
- }
}
}))
@@ -60,43 +34,18 @@ export default function Homepage() {
const {classes} = useStyles()
return (
<>
-
- {config?.siteTitle}
-
-
+
-
- {config.siteTitle}
- {config.siteDescription}
-
+
-
>