damus.io

damus.io website
git clone git://jb55.com/damus.io
Log | Files | Refs | README | LICENSE

PurpleAccount.tsx (6778B)


      1 import { ArrowUpRight, Star, Check, LogOut, X } from "lucide-react";
      2 import { Button } from "../ui/Button";
      3 import { FormattedMessage, useIntl } from "react-intl";
      4 import Link from "next/link";
      5 import Image from "next/image";
      6 import { useEffect, useRef, useState } from "react";
      7 import { AccountInfo, Profile, getProfile, getPurpleAccountInfo } from "@/utils/PurpleUtils";
      8 import { useLocalStorage } from "usehooks-ts";
      9 import { ErrorDialog } from "../ErrorDialog";
     10 import { PurpleLayout } from "../PurpleLayout";
     11 import { usePurpleLoginSession } from "@/hooks/usePurpleLoginSession";
     12 
     13 
     14 export function PurpleAccount() {
     15   const intl = useIntl()
     16   const [error, setError] = useState<string | null>(null)
     17   const { accountInfo: loggedInAccountInfo, logout } = usePurpleLoginSession(setError)
     18   const [profile, setProfile] = useState<Profile | null>(null)
     19   const [pubkey, setPubkey] = useState<string | null>(null)
     20 
     21   // MARK: - Functions
     22 
     23   const fetchProfile = async () => {
     24     if (!pubkey) {
     25       return
     26     }
     27     try {
     28       const profile = await getProfile(pubkey)
     29       setProfile(profile)
     30     }
     31     catch (e) {
     32       console.error(e)
     33       setError("Failed to get profile info from the relay. Please wait a few minutes and refresh the page. If the problem persists, please contact support.")
     34     }
     35   }
     36 
     37   // MARK: - Effects and hooks
     38 
     39   // Load the profile when the pubkey changes
     40   useEffect(() => {
     41     if (pubkey) {
     42       fetchProfile()
     43     }
     44   }, [pubkey])
     45 
     46   useEffect(() => {
     47     if (loggedInAccountInfo) {
     48       setPubkey(loggedInAccountInfo.pubkey)
     49     }
     50     else if (loggedInAccountInfo === null) {
     51       // Redirect to the login page
     52       window.location.href = "/purple/login?redirect=" + encodeURIComponent("/purple/account")
     53     }
     54   }, [loggedInAccountInfo])
     55 
     56   // MARK: - Render
     57 
     58   return (<>
     59     <ErrorDialog error={error} setError={setError} />
     60     <PurpleLayout>
     61       {((profile || profile === null) && pubkey) && (<>
     62         <div className="mt-2 mb-4 flex flex-col items-center">
     63           <div className="mt-4 flex flex-col gap-1 items-center justify-center mb-4">
     64             <Image src={profile?.picture || ("https://robohash.org/" + (profile?.pubkey || pubkey))} width={128} height={128} className="rounded-full" alt={profile?.name || intl.formatMessage({ id: "purple.account.unknown-user", defaultMessage: "Generic user avatar" })} />
     65             <div className="flex flex-wrap gap-1 items-center">
     66               <div className="text-purple-50 font-semibold text-3xl">
     67                 {profile?.name || "No name"}
     68               </div>
     69               <div className="text-purple-200/70 font-normal text-xs flex gap-1">
     70                 <Star strokeWidth={1.75} className="w-4 h-4 shrink-0 text-amber-400" fill="currentColor" />
     71                 {loggedInAccountInfo?.created_at && unixTimestampToDateString(loggedInAccountInfo?.created_at)}
     72               </div>
     73             </div>
     74             {loggedInAccountInfo?.active ? (
     75               <div className="flex gap-1 bg-gradient-to-r from-damuspink-500 to-damuspink-600 rounded-full px-3 py-1 items-center mt-3 mb-6">
     76                 <div className="w-4 h-4 rounded-full bg-white flex justify-center items-center">
     77                   <Check className="w-2 h-2 shrink-0 text-damuspink-500" strokeWidth={5} />
     78                 </div>
     79                 <div className="text-white font-semibold text-sm">
     80                   {intl.formatMessage({ id: "purple.account.active", defaultMessage: "Active account" })}
     81                 </div>
     82               </div>
     83             ) : (
     84               <div className="flex gap-1 bg-red-200 rounded-full px-3 py-1 items-center mt-3 mb-6">
     85                 <div className="w-4 h-4 rounded-full bg-red-500 flex justify-center items-center">
     86                   <X className="w-2 h-2 shrink-0 text-red-200" strokeWidth={5} />
     87                 </div>
     88                 <div className="text-red-500 font-semibold text-sm">
     89                   {intl.formatMessage({ id: "purple.account.expired", defaultMessage: "Expired account" })}
     90                 </div>
     91               </div>
     92             )}
     93           </div>
     94           <div className="flex flex-col bg-purple-50/10 rounded-xl px-4 text-purple-50 w-full">
     95             <AccountInfoRow label={intl.formatMessage({ id: "purple.account.expiry-date", defaultMessage: "Expiry date" })} value={(loggedInAccountInfo?.expiry && unixTimestampToDateString(loggedInAccountInfo?.expiry)) || "N/A"} />
     96             <AccountInfoRow label={intl.formatMessage({ id: "purple.account.account-creation", defaultMessage: "Account creation" })} value={(loggedInAccountInfo?.created_at && unixTimestampToDateString(loggedInAccountInfo?.created_at)) || "N/A"} />
     97             <AccountInfoRow label={intl.formatMessage({ id: "purple.account.subscriber-number", defaultMessage: "Subscriber number" })} value={(loggedInAccountInfo?.subscriber_number && "#" + loggedInAccountInfo?.subscriber_number) || "N/A"} last={loggedInAccountInfo?.testflight_url ? false : true} />
     98             {loggedInAccountInfo?.testflight_url && <Link href={loggedInAccountInfo?.testflight_url} target="_blank">
     99               <div className="py-2 border-b border-purple-200/20">
    100                 <Button variant="link" className="w-full text-left">
    101                   <ArrowUpRight className="text-damuspink-600 mr-2" />
    102                   {intl.formatMessage({ id: "purple.account.testflight-link", defaultMessage: "Join TestFlight" })}
    103                 </Button>
    104               </div>
    105             </Link>}
    106             <div className="py-2">
    107               <Link href="/notedeck/install" target="_blank">
    108                 <Button variant="link" className="w-full text-left">
    109                   <ArrowUpRight className="text-damuspink-600 mr-2" />
    110                   {intl.formatMessage({ id: "purple.account.notedeck-install-link", defaultMessage: "Try Notedeck" })}
    111                 </Button>
    112               </Link>
    113             </div>
    114           </div>
    115           <Button className="w-full md:w-auto opacity-70 hover:opacity-100 transition mt-4 text-sm" onClick={() => logout()} variant="link">
    116             <LogOut className="text-damuspink-600 mr-2" />
    117             {intl.formatMessage({ id: "purple.account.sign-out", defaultMessage: "Sign out" })}
    118           </Button>
    119         </div>
    120       </>)}
    121     </PurpleLayout>
    122   </>)
    123 }
    124 
    125 function AccountInfoRow({ label, value, last }: { label: string, value: string | number, last?: boolean }) {
    126   return (
    127     <div className={`flex gap-2 items-center justify-between ${last ? '' : 'border-b border-purple-200/20'} py-4`}>
    128       <div className="font-bold">
    129         {label}
    130       </div>
    131       <div>
    132         {value}
    133       </div>
    134     </div>
    135   );
    136 }
    137 
    138 function unixTimestampToDateString(timestamp: number) {
    139   return new Date(timestamp * 1000).toLocaleDateString()
    140 }