feat: pwa

This commit is contained in:
codytseng 2024-12-22 16:37:38 +08:00
parent 869e164469
commit c5b0c0543a
23 changed files with 3825 additions and 51 deletions

View file

@ -32,7 +32,7 @@ export default function ProfileButton({
if (variant === 'titlebar') {
triggerComponent = (
<button>
<Avatar className="w-7 h-7 hover:opacity-90">
<Avatar className="ml-2 w-6 h-6 hover:opacity-90">
<AvatarImage src={avatar} />
<AvatarFallback>
<img src={defaultAvatar} />

View file

@ -19,7 +19,7 @@ export default function ScrollToTopButton({
<Button
variant="secondary-2"
className={cn(
`absolute bottom-2 right-2 rounded-full w-11 h-11 p-0 hover:text-background transition-transform ${visible ? '' : 'translate-y-14'}`,
`absolute bottom-6 right-6 rounded-full w-12 h-12 p-0 hover:text-background transition-transform ${visible ? '' : 'translate-y-20'}`,
className
)}
onClick={handleScrollToTop}

View file

@ -6,7 +6,6 @@ import AboutInfoDialog from '../AboutInfoDialog'
import AccountButton from '../AccountButton'
import NotificationButton from '../NotificationButton'
import PostButton from '../PostButton'
import RefreshButton from '../RefreshButton'
import RelaySettingsButton from '../RelaySettingsButton'
import SearchButton from '../SearchButton'
@ -23,7 +22,6 @@ export default function PrimaryPageSidebar() {
<RelaySettingsButton variant="sidebar" />
<NotificationButton variant="sidebar" />
<SearchButton variant="sidebar" />
<RefreshButton variant="sidebar" />
<AboutInfoDialog>
<Button variant="sidebar" size="sidebar">
<Info />

View file

@ -19,8 +19,6 @@
body {
font-family: 'Inter', sans-serif;
color: hsl(var(--foreground));
background-color: hsl(var(--background));
overflow: hidden;
-webkit-overflow-scrolling: touch;
text-size-adjust: 100%;

View file

@ -2,7 +2,6 @@ import Logo from '@/assets/Logo'
import AccountButton from '@/components/AccountButton'
import NotificationButton from '@/components/NotificationButton'
import PostButton from '@/components/PostButton'
import RefreshButton from '@/components/RefreshButton'
import RelaySettingsButton from '@/components/RelaySettingsButton'
import ScrollToTopButton from '@/components/ScrollToTopButton'
import SearchButton from '@/components/SearchButton'
@ -62,7 +61,7 @@ const PrimaryPageLayout = forwardRef(({ children }: { children?: React.ReactNode
>
<PrimaryPageTitlebar visible={visible} />
<div className="sm:px-4 pb-4 pt-11 xl:pt-4">{children}</div>
<ScrollToTopButton scrollAreaRef={scrollAreaRef} visible={visible} />
<ScrollToTopButton scrollAreaRef={scrollAreaRef} visible={visible && lastScrollTop > 500} />
</ScrollArea>
)
})
@ -107,7 +106,6 @@ function PrimaryPageTitlebar({ visible = true }: { visible?: boolean }) {
<SearchButton />
</div>
<div className="flex gap-2 items-center">
<RefreshButton />
<RelaySettingsButton />
<NotificationButton />
</div>

View file

@ -58,7 +58,7 @@ export default function SecondaryPageLayout({
<div className="sm:px-4 pb-4 pt-11 w-full h-full">{children}</div>
<ScrollToTopButton
scrollAreaRef={scrollAreaRef}
visible={!hideScrollToTopButton && visible}
visible={!hideScrollToTopButton && visible && lastScrollTop > 500}
/>
</ScrollArea>
)

View file

@ -41,20 +41,16 @@ export function RelaySettingsProvider({ children }: { children: React.ReactNode
const [areAlgoRelays, setAreAlgoRelays] = useState(false)
useEffect(() => {
const init = async () => {
const searchParams = new URLSearchParams(window.location.search)
const tempRelays = searchParams
.getAll('r')
.filter((url) => isWebsocketUrl(url))
.map((url) => normalizeUrl(url))
if (tempRelays.length) {
setTemporaryRelayUrls(tempRelays)
}
const storedGroups = await storage.getRelayGroups()
setRelayGroups(storedGroups)
const searchParams = new URLSearchParams(window.location.search)
const tempRelays = searchParams
.getAll('r')
.filter((url) => isWebsocketUrl(url))
.map((url) => normalizeUrl(url))
if (tempRelays.length) {
setTemporaryRelayUrls(tempRelays)
}
init()
const storedGroups = storage.getRelayGroups()
setRelayGroups(storedGroups)
}, [])
useEffect(() => {
@ -63,25 +59,25 @@ export function RelaySettingsProvider({ children }: { children: React.ReactNode
? temporaryRelayUrls
: (relayGroups.find((group) => group.isActive)?.relayUrls ?? [])
if (JSON.stringify(relayUrls) !== JSON.stringify(newRelayUrls)) {
setRelayUrls(newRelayUrls)
}
const relayInfos = await client.fetchRelayInfos(newRelayUrls)
setSearchableRelayUrls(newRelayUrls.filter((_, index) => checkSearchRelay(relayInfos[index])))
const nonAlgoRelayUrls = newRelayUrls.filter((_, index) => !checkAlgoRelay(relayInfos[index]))
setAreAlgoRelays(newRelayUrls.length > 0 && nonAlgoRelayUrls.length === 0)
client.setCurrentRelayUrls(nonAlgoRelayUrls)
if (JSON.stringify(relayUrls) !== JSON.stringify(newRelayUrls)) {
setRelayUrls(newRelayUrls)
}
}
handler()
}, [relayGroups, temporaryRelayUrls, relayUrls])
const updateGroups = async (fn: (pre: TRelayGroup[]) => TRelayGroup[]) => {
const updateGroups = (fn: (pre: TRelayGroup[]) => TRelayGroup[]) => {
let newGroups = relayGroups
setRelayGroups((pre) => {
newGroups = fn(pre)
return newGroups
})
await storage.setRelayGroups(newGroups)
storage.setRelayGroups(newGroups)
}
const switchRelayGroup = (groupName: string) => {

View file

@ -1,4 +1,4 @@
import { createContext, useContext, useEffect, useState } from 'react'
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
type TScreenSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl'
@ -18,8 +18,8 @@ export const useScreenSize = () => {
}
export function ScreenSizeProvider({ children }: { children: React.ReactNode }) {
const [screenSize, setScreenSize] = useState<TScreenSize>('xl')
const isSmallScreen = screenSize === 'sm'
const [screenSize, setScreenSize] = useState<TScreenSize>('sm')
const isSmallScreen = useMemo(() => screenSize === 'sm', [screenSize])
useEffect(() => {
const handleResize = () => {