feat: dashboard index page

This commit is contained in:
DIYgod 2022-06-22 19:20:30 +01:00
parent fc3e38e9df
commit f88b20ff7a
No known key found for this signature in database
GPG Key ID: D159328F47A80DCA
7 changed files with 250 additions and 54 deletions

View File

@ -63,7 +63,7 @@
"remark-rehype": "^10.1.0",
"sortablejs": "^1.15.0",
"ua-parser-js": "^1.0.2",
"unidata.js": "^0.5.2",
"unidata.js": "^0.5.4",
"unified": "^10.1.2",
"unist-builder": "^3.0.0",
"unist-util-visit": "^4.1.0",

View File

@ -80,7 +80,7 @@ specifiers:
tailwindcss: ^3.0.24
typescript: ^4.5.5
ua-parser-js: ^1.0.2
unidata.js: ^0.5.2
unidata.js: ^0.5.4
unified: ^10.1.2
unist-builder: ^3.0.0
unist-util-visit: ^4.1.0
@ -141,7 +141,7 @@ dependencies:
remark-rehype: 10.1.0
sortablejs: 1.15.0
ua-parser-js: 1.0.2
unidata.js: 0.5.2
unidata.js: 0.5.4
unified: 10.1.2
unist-builder: 3.0.0
unist-util-visit: 4.1.0
@ -1432,7 +1432,7 @@ packages:
/@types/bn.js/4.11.6:
resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==}
dependencies:
'@types/node': 17.0.31
'@types/node': 17.0.45
dev: false
/@types/busboy/0.3.2:
@ -1520,6 +1520,10 @@ packages:
/@types/node/17.0.31:
resolution: {integrity: sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==}
/@types/node/17.0.45:
resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
dev: false
/@types/normalize-package-data/2.4.1:
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
dev: false
@ -2851,7 +2855,7 @@ packages:
dependencies:
ethers: 5.6.9
ts-mixer: 6.0.1
undici: 5.3.0
undici: 5.5.1
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@ -4533,8 +4537,8 @@ packages:
multiformats: 9.6.5
dev: false
/interface-datastore/6.1.0:
resolution: {integrity: sha512-oNHdsrWBsI/kDwUtEgt+aaZtQFKtQYN0TGZzc3SGiIA6m+plZ6malhmsygtbmDpfpIsNNC7ce9Gyaj+Tki+gVw==}
/interface-datastore/6.1.1:
resolution: {integrity: sha512-AmCS+9CT34pp2u0QQVXjKztkuq3y5T+BIciuiHDDtDZucZD8VudosnSdUyXJV6IsRkN5jc4RFDhCk1O6Q3Gxjg==}
dependencies:
interface-store: 2.0.2
nanoid: 3.3.4
@ -4595,6 +4599,165 @@ packages:
- supports-color
dev: false
/ipfs-core-types/0.8.4:
resolution: {integrity: sha512-sbRZA1QX3xJ6ywTiVQZMOxhlhp4osAZX2SXx3azOLxAtxmGWDMkHYt722VV4nZ2GyJy8qyk5GHQIZ0uvQnpaTg==}
dependencies:
interface-datastore: 6.1.1
multiaddr: 10.0.1
multiformats: 9.6.5
transitivePeerDependencies:
- node-fetch
- supports-color
dev: false
/ipfs-core-utils/0.12.2:
resolution: {integrity: sha512-RfxP3rPhXuqKIUmTAUhmee6fmaV3A7LMnjOUikRKpSyqESz/DR7aGK7tbttMxkZdkSEr0rFXlqbyb0vVwmn0wQ==}
dependencies:
any-signal: 2.1.2
blob-to-it: 1.0.4
browser-readablestream-to-it: 1.0.3
debug: 4.3.4
err-code: 3.0.1
ipfs-core-types: 0.8.4
ipfs-unixfs: 6.0.9
ipfs-utils: 9.0.6
it-all: 1.0.6
it-map: 1.0.6
it-peekable: 1.0.3
it-to-stream: 1.0.0
merge-options: 3.0.4
multiaddr: 10.0.1
multiaddr-to-uri: 8.0.0
multiformats: 9.6.5
nanoid: 3.3.4
parse-duration: 1.0.2
timeout-abort-controller: 1.1.1
uint8arrays: 3.0.0
transitivePeerDependencies:
- node-fetch
- supports-color
dev: false
/ipfs-unixfs-exporter/7.0.11:
resolution: {integrity: sha512-qTYa69J7HbI2EIYNUddKPg9Y3rHkYZV0bNdmzZKA5+ZbwRVoUEuBW/cguEqTp22zHygh3sMnzYZFm0naVIdMgQ==}
engines: {node: '>=16.0.0', npm: '>=7.0.0'}
dependencies:
'@ipld/dag-cbor': 7.0.2
'@ipld/dag-pb': 2.1.17
'@multiformats/murmur3': 1.1.3
err-code: 3.0.1
hamt-sharding: 2.0.1
interface-blockstore: 2.0.3
ipfs-unixfs: 6.0.9
it-last: 1.0.6
multiformats: 9.6.5
uint8arrays: 3.0.0
dev: false
/ipfs-unixfs-importer/9.0.10:
resolution: {integrity: sha512-W+tQTVcSmXtFh7FWYWwPBGXJ1xDgREbIyI1E5JzDcimZLIyT5gGMfxR3oKPxxWj+GKMpP5ilvMQrbsPzWcm3Fw==}
engines: {node: '>=16.0.0', npm: '>=7.0.0'}
dependencies:
'@ipld/dag-pb': 2.1.17
'@multiformats/murmur3': 1.1.3
bl: 5.0.0
err-code: 3.0.1
hamt-sharding: 2.0.1
interface-blockstore: 2.0.3
ipfs-unixfs: 6.0.9
it-all: 1.0.6
it-batch: 1.0.9
it-first: 1.0.7
it-parallel-batch: 1.0.10
merge-options: 3.0.4
multiformats: 9.6.5
rabin-wasm: 0.1.5
uint8arrays: 3.0.0
transitivePeerDependencies:
- encoding
- supports-color
dev: false
/ipfs-unixfs/6.0.9:
resolution: {integrity: sha512-0DQ7p0/9dRB6XCb0mVCTli33GzIzSVx5udpJuVM47tGcD+W+Bl4LsnoLswd3ggNnNEakMv1FdoFITiEnchXDqQ==}
engines: {node: '>=16.0.0', npm: '>=7.0.0'}
dependencies:
err-code: 3.0.1
protobufjs: 6.11.3
dev: false
/ipfs-utils/9.0.6:
resolution: {integrity: sha512-/WfdwOIiJVb3uqfKRQ9Eo+vCEKsDgp7h4Pdc37MRwAiFciZ7xKAkEqsfXubV0VQi8x5jWTifeHn8WEPBLL451w==}
dependencies:
any-signal: 3.0.1
buffer: 6.0.3
electron-fetch: 1.7.4
err-code: 3.0.1
is-electron: 2.2.1
iso-url: 1.2.1
it-glob: 1.0.2
it-to-stream: 1.0.0
merge-options: 3.0.4
nanoid: 3.3.4
native-fetch: 3.0.0_hmwa7nplpltavckpkeobtw6pv4
node-fetch: /@achingbrain/node-fetch/2.6.7
react-native-fetch-api: 2.0.0
stream-to-it: 0.2.4
dev: false
/ipns/0.16.0:
resolution: {integrity: sha512-fBYkRjN3/fc6IQujUF4WBEyOXegK715w+wx9IErV6H2B5JXsMnHOBceUKn3L90dj+wJfHs6T+hM/OZiTT6mQCw==}
dependencies:
cborg: 1.9.4
debug: 4.3.4
err-code: 3.0.1
interface-datastore: 6.1.1
libp2p-crypto: 0.21.2
long: 4.0.0
multiformats: 9.6.5
peer-id: 0.16.0
protobufjs: 6.11.3
timestamp-nano: 1.0.0
uint8arrays: 3.0.0
transitivePeerDependencies:
- supports-color
dev: false
/is-alphabetical/2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
dev: false
/ipfs-car/0.7.0:
resolution: {integrity: sha512-9ser6WWZ1ZMTCGbcVkRXUzOrpQ4SIiLfzIEnk+3LQsXbV09yeZg3ijhRuEXozEIYE68Go9JmOFshamsK9iKlNQ==}
hasBin: true
dependencies:
'@ipld/car': 3.2.4
'@web-std/blob': 3.0.4
bl: 5.0.0
blockstore-core: 1.0.5
browser-readablestream-to-it: 1.0.3
idb-keyval: 6.2.0
interface-blockstore: 2.0.3
ipfs-core-types: 0.8.4
ipfs-core-utils: 0.12.2
ipfs-unixfs-exporter: 7.0.11
ipfs-unixfs-importer: 9.0.10
ipfs-utils: 9.0.6
it-all: 1.0.6
it-last: 1.0.6
it-pipe: 1.1.0
meow: 9.0.0
move-file: 2.1.0
multiformats: 9.6.5
stream-to-it: 0.2.4
streaming-iterables: 6.2.0
uint8arrays: 3.0.0
transitivePeerDependencies:
- encoding
- node-fetch
- supports-color
dev: false
/ipfs-core-types/0.8.4:
resolution: {integrity: sha512-sbRZA1QX3xJ6ywTiVQZMOxhlhp4osAZX2SXx3azOLxAtxmGWDMkHYt722VV4nZ2GyJy8qyk5GHQIZ0uvQnpaTg==}
dependencies:
@ -5826,6 +5989,10 @@ packages:
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
/ms/2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
dev: false
/multiaddr-to-uri/8.0.0:
resolution: {integrity: sha512-dq4p/vsOOUdVEd1J1gl+R2GFrXJQH8yjLtz4hodqdVbieg39LvBOdMQRdQnfbg5LSM/q1BYNVf5CBbwZFFqBgA==}
dependencies:
@ -5990,7 +6157,7 @@ packages:
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
dependencies:
hosted-git-info: 2.8.9
resolve: 1.22.0
resolve: 1.22.1
semver: 5.7.1
validate-npm-package-license: 3.0.4
dev: false
@ -6502,7 +6669,7 @@ packages:
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
'@types/long': 4.0.2
'@types/node': 17.0.31
'@types/node': 17.0.45
long: 4.0.0
dev: false
@ -6810,7 +6977,7 @@ packages:
/receptacle/1.3.2:
resolution: {integrity: sha512-HrsFvqZZheusncQRiEE7GatOAETrARKV/lnfYicIm8lbvp/JQOdADOfhjBd2DajvoszEyxSM6RlAAIZgEoeu/A==}
dependencies:
ms: 2.1.2
ms: 2.1.3
dev: false
/redent/3.0.0:
@ -6946,6 +7113,15 @@ packages:
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
/resolve/1.22.1:
resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
hasBin: true
dependencies:
is-core-module: 2.9.0
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: false
/resolve/2.0.0-next.3:
resolution: {integrity: sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==}
dependencies:
@ -7587,6 +7763,32 @@ packages:
- utf-8-validate
dev: false
/undici/5.5.1:
resolution: {integrity: sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw==}
engines: {node: '>=12.18'}
dev: false
/unidata.js/0.5.4:
resolution: {integrity: sha512-amdu7judZHIE2JC9FC27/IuU4oeCTa4sJr3WMRuo9LHCVeOkipknUlHqZ0vYWZDZfhorqHp4RnTi7Eh2tOk63Q==}
dependencies:
'@types/lodash': 4.14.182
'@urql/core': 2.5.0_graphql@16.5.0
axios: 0.27.2
crossbell.js: 0.5.15
ethers: 5.6.8
graphql: 16.5.0
lodash: 4.17.21
mime: 3.0.0
web3.storage: 4.2.0
transitivePeerDependencies:
- bufferutil
- debug
- encoding
- node-fetch
- supports-color
- utf-8-validate
dev: false
/unified/10.1.2:
resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==}
dependencies:

View File

@ -1,7 +1,7 @@
import clsx from "clsx"
import Link from "next/link"
import { useRouter } from "next/router"
import React from "react"
import React, { useState, useEffect } from "react"
import { APP_NAME } from "~/lib/env"
import { getSiteLink } from "~/lib/helpers"
import { trpc } from "~/lib/trpc"
@ -10,6 +10,9 @@ import { DashboardIcon } from "../icons/DashboardIcon"
import { UniLink } from "../ui/UniLink"
import { DashboardSidebar } from "./DashboardSidebar"
import { SiteSwitcher } from "./SiteSwitcher"
import { getUserSites } from "~/models/site.model"
import { useAccount } from 'wagmi'
import type { Profile } from "unidata.js"
export function DashboardLayout({
children,
@ -21,12 +24,17 @@ export function DashboardLayout({
const router = useRouter()
const subdomain = router.query.subdomain as string
const { data: subscriptions } = trpc.useQuery(
["user.getSubscriptions", { canManage: true }],
{},
)
const { data: viewer } = useAccount()
const { data: viewer } = trpc.useQuery(["auth.viewer"])
let [subscriptions, setSubscriptions] = useState<Profile[]>([])
useEffect(() => {
if (viewer?.address) {
getUserSites(viewer.address).then((sites) => {
setSubscriptions(sites || [])
})
}
}, [viewer?.address])
const links: {
href: string
@ -76,7 +84,7 @@ export function DashboardLayout({
<div className="mb-2">
<SiteSwitcher
subdomain={subdomain}
subscriptions={subscriptions || []}
subscriptions={subscriptions}
viewer={viewer}
/>
</div>

View File

@ -4,14 +4,12 @@ import { useEffect, useMemo } from "react"
import { trpc } from "~/lib/trpc"
import { getUserContentsUrl } from "~/lib/user-contents"
import { Avatar } from "../ui/Avatar"
import type { Profile } from "unidata.js"
type Props = {
subdomain: string
subscriptions: {
id: string
site: { id: string; name: string; subdomain: string; icon?: string | null }
}[]
viewer?: { email: string } | null
subscriptions: Profile[]
viewer?: { address?: string }
}
export const SiteSwitcher: React.FC<Props> = ({
@ -20,33 +18,20 @@ export const SiteSwitcher: React.FC<Props> = ({
viewer,
}) => {
const activeSubscription = subscriptions.find(
(s) => s.site.subdomain === subdomain
(s) => s.username === subdomain
)
const { mutate: updateMembership } = trpc.useMutation(
"membership.updateMembership"
)
useEffect(() => {
if (activeSubscription?.id) {
updateMembership({
id: activeSubscription?.id,
lastSwitchedTo: new Date().toISOString(),
})
}
}, [activeSubscription?.id, updateMembership])
return (
<div className="px-3 pt-3 pb-2 text-sm">
<Popover className="relative">
<Popover.Button className="h-8 px-2 justify-between flex w-full rounded-lg hover:bg-gray-200 hover:bg-opacity-50 transition-colors items-center">
<div className="flex items-center space-x-2">
<Avatar
images={[getUserContentsUrl(activeSubscription?.site.icon)]}
name={activeSubscription?.site.name}
images={[getUserContentsUrl(activeSubscription?.avatars?.[0])]}
name={activeSubscription?.name}
size={22}
/>
<span className="truncate">{activeSubscription?.site.name}</span>
<span className="truncate">{activeSubscription?.name}</span>
</div>
<span className="w-5 h-5 text-zinc-400 rounded-full bg-zinc-100 inline-flex items-center justify-center">
<svg className="w-4 h-4" viewBox="0 0 14 14">
@ -65,21 +50,21 @@ export const SiteSwitcher: React.FC<Props> = ({
<div className="min-w-[280px] rounded-lg shadow-modal bg-white">
{viewer && (
<div className="px-4 py-2 border-b text-sm text-zinc-500">
{viewer.email}
{viewer.address?.slice(0, 6)}...{viewer.address?.slice(-4)}
</div>
)}
<div className="p-2">
{subscriptions?.map((subscription) => {
return (
<Link
key={subscription.id}
href={`/dashboard/${subscription.site.subdomain}`}
key={subscription.username}
href={`/dashboard/${subscription.username}`}
>
<a className="flex px-2 h-8 rounded-lg items-center justify-between hover:bg-zinc-100">
<span className="truncate w-8/12">
{subscription.site.name}
{subscription.name}
</span>
{activeSubscription?.id === subscription.id && (
{activeSubscription?.username === subscription.username && (
<span className="text-accent">
<svg
className="w-5 h-5"

View File

@ -7,5 +7,5 @@ export function getUserContentsUrl<T extends string | undefined | null>(
): T
export function getUserContentsUrl(filename: string | undefined | null) {
if (!filename) return undefined
return `${R2_URL}/${filename}`
return filename
}

View File

@ -44,7 +44,7 @@ export const checkSubdomain = async ({
}
}
export const getUserLastActiveSite = async (address: string) => {
export const getUserSites = async (address: string) => {
if (unidata) {
const profiles = await unidata.profiles.get({
source: 'Crossbell Profile',
@ -52,11 +52,11 @@ export const getUserLastActiveSite = async (address: string) => {
platform: 'Ethereum',
});
const site = profiles.list?.sort((a: any, b: any) => a.date_updated - b.date_updated)?.[0]
const sites = profiles.list?.sort((a, b) => +new Date(b.date_updated || 0) - +new Date(a.date_updated || 0))
if (!site) return null
if (!sites || !sites.length) return null
return site
return sites
} else {
return null
}

View File

@ -1,5 +1,5 @@
import { redirect } from "~/lib/server-side-props"
import { getUserLastActiveSite } from "~/models/site.model"
import { getUserSites } from "~/models/site.model"
import { useAccount } from 'wagmi'
import { useEffect } from "react"
import { useRouter } from 'next/router'
@ -10,16 +10,17 @@ export default function Dashboard() {
useEffect(() => {
if (wagmiData?.address) {
getUserLastActiveSite(wagmiData.address).then((site) => {
if (!site) {
getUserSites(wagmiData.address).then((sites) => {
if (!sites) {
router.push(`/dashboard/new-site`)
} else {
router.push(`/dashboard/${sites[0].username}`)
}
router.push(`/dashboard/${site.username}`)
})
} else {
router.push("/")
}
})
}, [wagmiData?.address, router])
return <div>Loading...</div>
}