diff --git a/api/resolvers/item.js b/api/resolvers/item.js
index bdaae17b..7940e382 100644
--- a/api/resolvers/item.js
+++ b/api/resolvers/item.js
@@ -50,8 +50,8 @@ export async function getItem (parent, { id }, { me, models }) {
function topClause (within) {
let interval = ' AND "Item".created_at >= $1 - INTERVAL '
switch (within) {
- case 'day':
- interval += "'1 day'"
+ case 'forever':
+ interval = ''
break
case 'week':
interval += "'7 days'"
@@ -63,12 +63,23 @@ function topClause (within) {
interval += "'1 year'"
break
default:
- interval = ''
+ interval += "'1 day'"
break
}
return interval
}
+async function topOrderClause (sort, me, models) {
+ switch (sort) {
+ case 'comments':
+ return 'ORDER BY ncomments DESC'
+ case 'sats':
+ return 'ORDER BY sats DESC'
+ default:
+ return await topOrderByWeightedSats(me, models)
+ }
+}
+
export async function orderByNumerator (me, models) {
if (me) {
const user = await models.user.findUnique({ where: { id: me.id } })
@@ -124,6 +135,40 @@ export default {
return count
},
+ topItems: async (parent, { cursor, sort, when }, { me, models }) => {
+ const decodedCursor = decodeCursor(cursor)
+ const items = await models.$queryRaw(`
+ ${SELECT}
+ FROM "Item"
+ WHERE "parentId" IS NULL AND "Item".created_at <= $1
+ AND "pinId" IS NULL
+ ${topClause(when)}
+ ${await filterClause(me, models)}
+ ${await topOrderClause(sort, me, models)}
+ OFFSET $2
+ LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
+ return {
+ cursor: items.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
+ items
+ }
+ },
+ topComments: async (parent, { cursor, sort, when }, { me, models }) => {
+ const decodedCursor = decodeCursor(cursor)
+ const comments = await models.$queryRaw(`
+ ${SELECT}
+ FROM "Item"
+ WHERE "parentId" IS NOT NULL
+ AND "Item".created_at <= $1
+ ${topClause(when)}
+ ${await filterClause(me, models)}
+ ${await topOrderClause(sort, me, models)}
+ OFFSET $2
+ LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
+ return {
+ cursor: comments.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
+ comments
+ }
+ },
items: async (parent, { sub, sort, cursor, name, within }, { me, models }) => {
const decodedCursor = decodeCursor(cursor)
let items; let user; let pins; let subFull
diff --git a/api/resolvers/user.js b/api/resolvers/user.js
index 711d01e1..72617243 100644
--- a/api/resolvers/user.js
+++ b/api/resolvers/user.js
@@ -6,8 +6,8 @@ import serialize from './serial'
export function topClause (within) {
let interval = ' AND "ItemAct".created_at >= $1 - INTERVAL '
switch (within) {
- case 'day':
- interval += "'1 day'"
+ case 'forever':
+ interval = ''
break
case 'week':
interval += "'7 days'"
@@ -19,7 +19,7 @@ export function topClause (within) {
interval += "'1 year'"
break
default:
- interval = ''
+ interval += "'1 day'"
break
}
return interval
@@ -28,8 +28,8 @@ export function topClause (within) {
export function earnWithin (within) {
let interval = ' AND "Earn".created_at >= $1 - INTERVAL '
switch (within) {
- case 'day':
- interval += "'1 day'"
+ case 'forever':
+ interval = ''
break
case 'week':
interval += "'7 days'"
@@ -41,8 +41,30 @@ export function earnWithin (within) {
interval += "'1 year'"
break
default:
+ interval += "'1 day'"
+ break
+ }
+ return interval
+}
+
+export function itemWithin (within) {
+ let interval = ' AND "Item".created_at >= $1 - INTERVAL '
+ switch (within) {
+ case 'forever':
interval = ''
break
+ case 'week':
+ interval += "'7 days'"
+ break
+ case 'month':
+ interval += "'1 month'"
+ break
+ case 'year':
+ interval += "'1 year'"
+ break
+ default:
+ interval += "'1 day'"
+ break
}
return interval
}
@@ -94,37 +116,59 @@ export default {
return user.name?.toUpperCase() === name?.toUpperCase() || !(await models.user.findUnique({ where: { name } }))
},
- topUsers: async (parent, { cursor, within, userType }, { models, me }) => {
+ topUsers: async (parent, { cursor, when, sort }, { models, me }) => {
const decodedCursor = decodeCursor(cursor)
let users
- if (userType === 'spent') {
+ if (sort === 'spent') {
users = await models.$queryRaw(`
- SELECT users.name, users.created_at, sum("ItemAct".sats) as amount
+ SELECT users.*, sum("ItemAct".sats) as spent
FROM "ItemAct"
JOIN users on "ItemAct"."userId" = users.id
WHERE "ItemAct".created_at <= $1
- ${topClause(within)}
+ ${topClause(when)}
GROUP BY users.id, users.name
- ORDER BY amount DESC NULLS LAST, users.created_at DESC
+ ORDER BY spent DESC NULLS LAST, users.created_at DESC
+ OFFSET $2
+ LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
+ } else if (sort === 'posts') {
+ users = await models.$queryRaw(`
+ SELECT users.*, count(*) as nitems
+ FROM users
+ JOIN "Item" on "Item"."userId" = users.id
+ WHERE "Item".created_at <= $1 AND "Item"."parentId" IS NULL
+ ${itemWithin(when)}
+ GROUP BY users.id
+ ORDER BY nitems DESC NULLS LAST, users.created_at DESC
+ OFFSET $2
+ LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
+ } else if (sort === 'comments') {
+ users = await models.$queryRaw(`
+ SELECT users.*, count(*) as ncomments
+ FROM users
+ JOIN "Item" on "Item"."userId" = users.id
+ WHERE "Item".created_at <= $1 AND "Item"."parentId" IS NOT NULL
+ ${itemWithin(when)}
+ GROUP BY users.id
+ ORDER BY ncomments DESC NULLS LAST, users.created_at DESC
OFFSET $2
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
} else {
users = await models.$queryRaw(`
- SELECT name, created_at, sum(sats) as amount
+ SELECT u.id, u.name, u."photoId", sum(amount) as stacked
FROM
- ((SELECT users.name, users.created_at, "ItemAct".sats as sats
+ ((SELECT users.*, "ItemAct".sats as amount
FROM "ItemAct"
JOIN "Item" on "ItemAct"."itemId" = "Item".id
JOIN users on "Item"."userId" = users.id
WHERE act <> 'BOOST' AND "ItemAct"."userId" <> users.id AND "ItemAct".created_at <= $1
- ${topClause(within)})
+ ${topClause(when)})
UNION ALL
- (SELECT users.name, users.created_at, "Earn".msats/1000 as sats
+ (SELECT users.*, "Earn".msats/1000 as amount
FROM "Earn"
JOIN users on users.id = "Earn"."userId"
- WHERE "Earn".msats > 0 ${earnWithin(within)})) u
- GROUP BY name, created_at
- ORDER BY amount DESC NULLS LAST, created_at DESC
+ WHERE "Earn".msats > 0 ${earnWithin(when)})) u
+ GROUP BY u.id, u.name, u.created_at, u."photoId"
+ ORDER BY stacked DESC NULLS LAST, created_at DESC
OFFSET $2
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
}
@@ -260,9 +304,15 @@ export default {
User: {
authMethods,
nitems: async (user, args, { models }) => {
+ if (user.nitems) {
+ return user.nitems
+ }
return await models.item.count({ where: { userId: user.id, parentId: null } })
},
ncomments: async (user, args, { models }) => {
+ if (user.ncomments) {
+ return user.ncomments
+ }
return await models.item.count({ where: { userId: user.id, parentId: { not: null } } })
},
stacked: async (user, args, { models }) => {
@@ -273,6 +323,10 @@ export default {
return Math.floor((user.stackedMsats || 0) / 1000)
},
spent: async (user, args, { models }) => {
+ if (user.spent) {
+ return user.spent
+ }
+
const { sum: { sats } } = await models.itemAct.aggregate({
sum: {
sats: true
diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js
index 17aef11a..73bc9942 100644
--- a/api/typeDefs/item.js
+++ b/api/typeDefs/item.js
@@ -15,6 +15,8 @@ export default gql`
outlawedItems(cursor: String): Items
borderlandItems(cursor: String): Items
freebieItems(cursor: String): Items
+ topItems(cursor: String, sort: String, when: String): Items
+ topComments(cursor: String, sort: String, when: String): Comments
}
type ItemActResult {
diff --git a/api/typeDefs/user.js b/api/typeDefs/user.js
index 748acc1b..85850716 100644
--- a/api/typeDefs/user.js
+++ b/api/typeDefs/user.js
@@ -7,7 +7,7 @@ export default gql`
user(name: String!): User
users: [User!]
nameAvailable(name: String!): Boolean!
- topUsers(cursor: String, within: String!, userType: String!): TopUsers
+ topUsers(cursor: String, when: String, sort: String): Users
searchUsers(q: String!, limit: Int, similarity: Float): [User!]!
}
@@ -16,17 +16,6 @@ export default gql`
users: [User!]!
}
- type TopUsers {
- cursor: String
- users: [TopUser!]!
- }
-
- type TopUser {
- name: String!
- createdAt: String!
- amount: Int!
- }
-
extend type Mutation {
setName(name: String!): Boolean
setSettings(tipDefault: Int!, fiatCurrency: String!, noteItemSats: Boolean!, noteEarning: Boolean!,
diff --git a/components/comment.js b/components/comment.js
index 157ba5e8..f1be8849 100644
--- a/components/comment.js
+++ b/components/comment.js
@@ -17,6 +17,7 @@ import { useMe } from './me'
import DontLikeThis from './dont-link-this'
import Flag from '../svgs/flag-fill.svg'
import { Badge } from 'react-bootstrap'
+import { abbrNum } from '../lib/format'
function Parent ({ item, rootText }) {
const ParentFrag = () => (
@@ -114,11 +115,11 @@ export default function Comment ({
-
{item.sats} sats
+
{abbrNum(item.sats)} sats
\
{item.boost > 0 &&
<>
-
{item.boost} boost
+
{abbrNum(item.boost)} boost
\
>}
diff --git a/components/comments-flat.js b/components/comments-flat.js
index f26f70ab..bfbbe6f6 100644
--- a/components/comments-flat.js
+++ b/components/comments-flat.js
@@ -3,8 +3,8 @@ import { MORE_FLAT_COMMENTS } from '../fragments/comments'
import { CommentFlat, CommentSkeleton } from './comment'
import MoreFooter from './more-footer'
-export default function CommentsFlat ({ variables, comments, cursor, ...props }) {
- const { data, fetchMore } = useQuery(MORE_FLAT_COMMENTS, {
+export default function CommentsFlat ({ variables, query, destructureData, comments, cursor, ...props }) {
+ const { data, fetchMore } = useQuery(query || MORE_FLAT_COMMENTS, {
variables
})
@@ -13,7 +13,11 @@ export default function CommentsFlat ({ variables, comments, cursor, ...props })
}
if (data) {
- ({ moreFlatComments: { comments, cursor } } = data)
+ if (destructureData) {
+ ({ comments, cursor } = destructureData(data))
+ } else {
+ ({ moreFlatComments: { comments, cursor } } = data)
+ }
}
return (
diff --git a/components/comments.js b/components/comments.js
index db5dfe2b..22c16926 100644
--- a/components/comments.js
+++ b/components/comments.js
@@ -5,6 +5,7 @@ import styles from './header.module.css'
import { Nav, Navbar } from 'react-bootstrap'
import { COMMENTS_QUERY } from '../fragments/items'
import { COMMENTS } from '../fragments/comments'
+import { abbrNum } from '../lib/format'
export function CommentsHeader ({ handleSort, commentSats }) {
const [sort, setSort] = useState('hot')
@@ -23,7 +24,7 @@ export function CommentsHeader ({ handleSort, commentSats }) {
activeKey={sort}
>
- {commentSats} sats
+ {abbrNum(commentSats)} sats
diff --git a/components/header.js b/components/header.js
index d42f1b68..fb49b551 100644
--- a/components/header.js
+++ b/components/header.js
@@ -11,7 +11,7 @@ import { signOut, signIn } from 'next-auth/client'
import { useLightning } from './lightning'
import { useEffect, useState } from 'react'
import { randInRange } from '../lib/rand'
-import { formatSats } from '../lib/format'
+import { abbrNum } from '../lib/format'
import NoteIcon from '../svgs/notification-4-fill.svg'
import { useQuery, gql } from '@apollo/client'
import LightningIcon from '../svgs/bolt.svg'
@@ -19,7 +19,7 @@ import LightningIcon from '../svgs/bolt.svg'
function WalletSummary ({ me }) {
if (!me) return null
- return `${formatSats(me.sats)}`
+ return `${abbrNum(me.sats)}`
}
export default function Header ({ sub }) {
@@ -154,7 +154,7 @@ export default function Header ({ sub }) {
{!prefix &&
-
+
top
}
@@ -230,7 +230,7 @@ const NavItemsStatic = ({ className }) => {
-
+
top
diff --git a/components/item.js b/components/item.js
index 651f8c76..722c40a8 100644
--- a/components/item.js
+++ b/components/item.js
@@ -14,6 +14,7 @@ import { newComments } from '../lib/new-comments'
import { useMe } from './me'
import DontLikeThis from './dont-link-this'
import Flag from '../svgs/flag-fill.svg'
+import { abbrNum } from '../lib/format'
export function SearchTitle ({ title }) {
return reactStringReplace(title, /:high\[([^\]]+)\]/g, (match, i) => {
@@ -87,12 +88,12 @@ export default function Item ({ item, rank, showFwdUser, toc, children }) {
{!item.position &&
<>
-
{item.sats} sats
+
{abbrNum(item.sats)} sats
\
>}
{item.boost > 0 &&
<>
-
{item.boost} boost
+
{abbrNum(item.boost)} boost
\
>}
diff --git a/components/item.module.css b/components/item.module.css
index 19bc09c1..470934c8 100644
--- a/components/item.module.css
+++ b/components/item.module.css
@@ -127,6 +127,14 @@ a.link:visited {
margin: 0;
}
+.skeleton .name {
+ background-color: var(--theme-grey);
+ width: 100px;
+ border-radius: .4rem;
+ height: 17px;
+ margin: 0;
+}
+
.skeleton .link {
height: 14px;
background-color: var(--theme-grey);
diff --git a/components/items.js b/components/items.js
index beb7f719..40cbda27 100644
--- a/components/items.js
+++ b/components/items.js
@@ -7,15 +7,19 @@ import MoreFooter from './more-footer'
import React from 'react'
import Comment from './comment'
-export default function Items ({ variables = {}, rank, items, pins, cursor }) {
- const { data, fetchMore } = useQuery(ITEMS, { variables })
+export default function Items ({ variables = {}, query, destructureData, rank, items, pins, cursor }) {
+ const { data, fetchMore } = useQuery(query || ITEMS, { variables })
if (!data && !items) {
return
}
if (data) {
- ({ items: { items, pins, cursor } } = data)
+ if (destructureData) {
+ ({ items, pins, cursor } = destructureData(data))
+ } else {
+ ({ items: { items, pins, cursor } } = data)
+ }
}
const pinMap = pins?.reduce((a, p) => { a[p.position] = p; return a }, {})
diff --git a/components/top-header.js b/components/top-header.js
index 08250d15..72175c78 100644
--- a/components/top-header.js
+++ b/components/top-header.js
@@ -1,137 +1,58 @@
-import { Nav, Navbar } from 'react-bootstrap'
-import styles from './header.module.css'
-import Link from 'next/link'
import { useRouter } from 'next/router'
+import { Form, Select } from './form'
export default function TopHeader ({ cat }) {
const router = useRouter()
- const within = router.query.within
- const userType = router.query.userType || 'stacked'
+
+ const top = async values => {
+ const what = values.what
+ delete values.what
+ if (values.sort === '') delete values.sort
+ if (values.when === '') delete values.when
+ await router.push({
+ pathname: `/top/${what}`,
+ query: values
+ })
+ }
return (
- <>
-
-
-
- {cat.split('/')[0] === 'users' &&
-
-
- }
-
-
-
- >
+
)
}
diff --git a/components/user-list.js b/components/user-list.js
index 4d7b0a50..87c86196 100644
--- a/components/user-list.js
+++ b/components/user-list.js
@@ -1,5 +1,6 @@
import Link from 'next/link'
import { Image } from 'react-bootstrap'
+import { abbrNum } from '../lib/format'
import styles from './item.module.css'
import userStyles from './user-header.module.css'
@@ -22,19 +23,19 @@ export default function UserList ({ users }) {
@@ -44,3 +45,28 @@ export default function UserList ({ users }) {
>
)
}
+
+export function UsersSkeleton () {
+ const users = new Array(21).fill(null)
+
+ return (
+
{users.map((_, i) => (
+
+ ))}
+
+ )
+}
diff --git a/fragments/comments.js b/fragments/comments.js
index f54705d2..d0145ae4 100644
--- a/fragments/comments.js
+++ b/fragments/comments.js
@@ -45,6 +45,19 @@ export const MORE_FLAT_COMMENTS = gql`
}
`
+export const TOP_COMMENTS = gql`
+ ${COMMENT_FIELDS}
+
+ query topComments($sort: String, $cursor: String, $when: String) {
+ topComments(sort: $sort, cursor: $cursor, when: $when) {
+ cursor
+ comments {
+ ...CommentFields
+ }
+ }
+ }
+`
+
export const COMMENTS = gql`
${COMMENT_FIELDS}
diff --git a/fragments/items.js b/fragments/items.js
index 15f9f0e8..706a6af9 100644
--- a/fragments/items.js
+++ b/fragments/items.js
@@ -70,6 +70,23 @@ export const ITEMS = gql`
}
}`
+export const TOP_ITEMS = gql`
+ ${ITEM_FIELDS}
+
+ query topItems($sort: String, $cursor: String, $when: String) {
+ topItems(sort: $sort, cursor: $cursor, when: $when) {
+ cursor
+ items {
+ ...ItemFields
+ position
+ },
+ pins {
+ ...ItemFields
+ position
+ }
+ }
+ }`
+
export const OUTLAWED_ITEMS = gql`
${ITEM_FIELDS}
diff --git a/fragments/users.js b/fragments/users.js
index 1be4a8de..564b5104 100644
--- a/fragments/users.js
+++ b/fragments/users.js
@@ -152,11 +152,15 @@ export const USER_FIELDS = gql`
}`
export const TOP_USERS = gql`
- query TopUsers($cursor: String, $within: String!, $userType: String!) {
- topUsers(cursor: $cursor, within: $within, userType: $userType) {
+ query TopUsers($cursor: String, $when: String, $sort: String) {
+ topUsers(cursor: $cursor, when: $when, sort: $sort) {
users {
name
- amount
+ photoId
+ stacked
+ spent
+ ncomments
+ nitems
}
cursor
}
diff --git a/lib/apollo.js b/lib/apollo.js
index ac34db37..f60a1898 100644
--- a/lib/apollo.js
+++ b/lib/apollo.js
@@ -26,7 +26,7 @@ export default function getApolloClient () {
Query: {
fields: {
topUsers: {
- keyArgs: ['within'],
+ keyArgs: ['when', 'sort'],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.users)) {
return incoming
@@ -52,6 +52,33 @@ export default function getApolloClient () {
}
}
},
+ topItems: {
+ keyArgs: ['sort', 'when'],
+ merge (existing, incoming) {
+ if (isFirstPage(incoming.cursor, existing?.items)) {
+ return incoming
+ }
+
+ return {
+ cursor: incoming.cursor,
+ items: [...(existing?.items || []), ...incoming.items],
+ pins: existing?.pins || null
+ }
+ }
+ },
+ topComments: {
+ keyArgs: ['sort', 'when'],
+ merge (existing, incoming) {
+ if (isFirstPage(incoming.cursor, existing?.comments)) {
+ return incoming
+ }
+
+ return {
+ cursor: incoming.cursor,
+ comments: [...(existing?.comments || []), ...incoming.comments]
+ }
+ }
+ },
outlawedItems: {
keyArgs: [],
merge (existing, incoming) {
diff --git a/lib/format.js b/lib/format.js
index 4dfa0da6..7cb2f1d4 100644
--- a/lib/format.js
+++ b/lib/format.js
@@ -1,4 +1,4 @@
-export const formatSats = n => {
+export const abbrNum = n => {
if (n < 1e4) return n
if (n >= 1e4 && n < 1e6) return +(n / 1e3).toFixed(1) + 'k'
if (n >= 1e6 && n < 1e9) return +(n / 1e6).toFixed(1) + 'm'
diff --git a/pages/top/comments/[within].js b/pages/top/comments/index.js
similarity index 56%
rename from pages/top/comments/[within].js
rename to pages/top/comments/index.js
index 9099b527..67152c41 100644
--- a/pages/top/comments/[within].js
+++ b/pages/top/comments/index.js
@@ -2,12 +2,12 @@ import Layout from '../../../components/layout'
import { useRouter } from 'next/router'
import { getGetServerSideProps } from '../../../api/ssrApollo'
import TopHeader from '../../../components/top-header'
-import { MORE_FLAT_COMMENTS } from '../../../fragments/comments'
+import { TOP_COMMENTS } from '../../../fragments/comments'
import CommentsFlat from '../../../components/comments-flat'
-export const getServerSideProps = getGetServerSideProps(MORE_FLAT_COMMENTS, { sort: 'top' })
+export const getServerSideProps = getGetServerSideProps(TOP_COMMENTS)
-export default function Index ({ data: { moreFlatComments: { comments, cursor } } }) {
+export default function Index ({ data: { topComments: { comments, cursor } } }) {
const router = useRouter()
return (
@@ -15,7 +15,9 @@ export default function Index ({ data: { moreFlatComments: { comments, cursor }
data.topComments}
+ variables={{ sort: router.query.sort, when: router.query.when }}
includeParent noReply
/>
diff --git a/pages/top/posts/[within].js b/pages/top/posts/index.js
similarity index 54%
rename from pages/top/posts/[within].js
rename to pages/top/posts/index.js
index f90b8f65..6218ee74 100644
--- a/pages/top/posts/[within].js
+++ b/pages/top/posts/index.js
@@ -2,13 +2,12 @@ import Layout from '../../../components/layout'
import Items from '../../../components/items'
import { useRouter } from 'next/router'
import { getGetServerSideProps } from '../../../api/ssrApollo'
-import { ITEMS } from '../../../fragments/items'
-
+import { TOP_ITEMS } from '../../../fragments/items'
import TopHeader from '../../../components/top-header'
-export const getServerSideProps = getGetServerSideProps(ITEMS, { sort: 'top' })
+export const getServerSideProps = getGetServerSideProps(TOP_ITEMS)
-export default function Index ({ data: { items: { items, cursor } } }) {
+export default function Index ({ data: { topItems: { items, cursor } } }) {
const router = useRouter()
return (
@@ -16,7 +15,9 @@ export default function Index ({ data: { items: { items, cursor } } }) {
data.topItems}
+ variables={{ sort: router.query.sort, when: router.query.when }} rank
/>
)
diff --git a/pages/top/users/[userType]/[within].js b/pages/top/users/[userType]/[within].js
deleted file mode 100644
index 32d8880b..00000000
--- a/pages/top/users/[userType]/[within].js
+++ /dev/null
@@ -1,52 +0,0 @@
-import Layout from '../../../../components/layout'
-import { useRouter } from 'next/router'
-import { getGetServerSideProps } from '../../../../api/ssrApollo'
-import TopHeader from '../../../../components/top-header'
-import { TOP_USERS } from '../../../../fragments/users'
-import { useQuery } from '@apollo/client'
-import Link from 'next/link'
-import MoreFooter from '../../../../components/more-footer'
-
-export const getServerSideProps = getGetServerSideProps(TOP_USERS)
-
-export default function Index ({ data: { topUsers: { users, cursor } } }) {
- const router = useRouter()
- const userType = router.query.userType
-
- const { data, fetchMore } = useQuery(TOP_USERS, {
- variables: { within: router.query?.within, userType: router.query?.userType }
- })
-
- if (data) {
- ({ topUsers: { users, cursor } } = data)
- }
-
- return (
-
-
- {users.map(user => (
-
-
-
@{user.name}
- {user.amount} {userType}
-
-
- ))}
-
-
- )
-}
-
-function UsersSkeleton () {
- const users = new Array(21).fill(null)
-
- return (
- {users.map((_, i) => (
-
- ))}
-
- )
-}
diff --git a/pages/top/users/index.js b/pages/top/users/index.js
new file mode 100644
index 00000000..586a7e24
--- /dev/null
+++ b/pages/top/users/index.js
@@ -0,0 +1,30 @@
+import Layout from '../../../components/layout'
+import { useRouter } from 'next/router'
+import { getGetServerSideProps } from '../../../api/ssrApollo'
+import TopHeader from '../../../components/top-header'
+import { TOP_USERS } from '../../../fragments/users'
+import { useQuery } from '@apollo/client'
+import MoreFooter from '../../../components/more-footer'
+import UserList, { UsersSkeleton } from '../../../components/user-list'
+
+export const getServerSideProps = getGetServerSideProps(TOP_USERS)
+
+export default function Index ({ data: { topUsers: { users, cursor } } }) {
+ const router = useRouter()
+
+ const { data, fetchMore } = useQuery(TOP_USERS, {
+ variables: { when: router.query.when, sort: router.query.sort }
+ })
+
+ if (data) {
+ ({ topUsers: { users, cursor } } = data)
+ }
+
+ return (
+
+
+
+
+
+ )
+}
diff --git a/pages/users/forever.js b/pages/users/forever.js
index 7c21f07f..23fe2644 100644
--- a/pages/users/forever.js
+++ b/pages/users/forever.js
@@ -3,7 +3,7 @@ import { getGetServerSideProps } from '../../api/ssrApollo'
import Layout from '../../components/layout'
import { LineChart, Line, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, AreaChart, Area } from 'recharts'
import { Col, Row } from 'react-bootstrap'
-import { formatSats } from '../../lib/format'
+import { abbrNum } from '../../lib/format'
import { UsageHeader } from '../../components/usage-header'
export const getServerSideProps = getGetServerSideProps(
@@ -115,7 +115,7 @@ function GrowthAreaChart ({ data, xName, title }) {
dataKey='time' tickFormatter={dateFormatter} name={xName}
tick={{ fill: 'var(--theme-grey)' }}
/>
-
+
{Object.keys(data[0]).filter(v => v !== 'time' && v !== '__typename').map((v, i) =>
@@ -141,7 +141,7 @@ function GrowthLineChart ({ data, xName, yName }) {
dataKey='time' tickFormatter={dateFormatter} name={xName}
tick={{ fill: 'var(--theme-grey)' }}
/>
-
+
diff --git a/pages/users/week.js b/pages/users/week.js
index 0b0d514d..928ff7a0 100644
--- a/pages/users/week.js
+++ b/pages/users/week.js
@@ -49,18 +49,18 @@ export default function Growth ({
-
+
items
-
- stacked
-
-
-
+
spent
+
+ stacked
+
+
)