feat: pwa
This commit is contained in:
parent
869e164469
commit
c5b0c0543a
23 changed files with 3825 additions and 51 deletions
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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%;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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 = () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue