Skip to content

Commit

Permalink
feat: optimize
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Jul 8, 2023
1 parent c1c7372 commit fc6c00e
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 82 deletions.
2 changes: 2 additions & 0 deletions src/app/thinking/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const FETCH_SIZE = 10
export const QUERY_KEY = ['recent']
6 changes: 2 additions & 4 deletions src/app/thinking/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ import { urlBuilder } from '~/lib/url-builder'
import { useAggregationSelector } from '~/providers/root/aggregation-data-provider'
import { useModalStack } from '~/providers/root/modal-stack-provider'

const FETCH_SIZE = 10
const QUERY_KEY = ['recent']
import { FETCH_SIZE, QUERY_KEY } from './constants'

export default function Page() {
return (
<div>
Expand Down Expand Up @@ -168,8 +168,6 @@ const List = () => {
return acc + cur.length
}, 0)

console.log(count)

animate(
'li',
{
Expand Down
5 changes: 4 additions & 1 deletion src/components/layout/header/internal/HeaderContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ const ForDesktop: Component<{
<div className="flex px-4 font-medium text-zinc-800 dark:text-zinc-200">
{headerMenuConfig.map((section) => {
const subItemActive =
section.subMenu?.findIndex((item) => item.path === pathname) || -1
section.subMenu?.findIndex((item) => {
return item.path === pathname || pathname.slice(1) === item.path
}) ?? -1

return (
<HeaderMenuItem
iconLayout={animatedIcon}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/link-card/LinkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { FC, ReactNode, SyntheticEvent } from 'react'
import { simpleCamelcaseKeys as camelcaseKeys } from '@mx-space/api-client'

import { LazyLoad } from '~/components/common/Lazyload'
import { usePeek } from '~/components/widgets/peek/PeekLink'
import { usePeek } from '~/components/widgets/peek/usePeek'
import { useIsClientTransition } from '~/hooks/common/use-is-client'
import { preventDefault } from '~/lib/dom'
import { apiClient } from '~/lib/request'
Expand Down
61 changes: 1 addition & 60 deletions src/components/widgets/peek/PeekLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,9 @@ import Link from 'next/link'
import type { LinkProps } from 'next/link'
import type { FC, PropsWithChildren, SyntheticEvent } from 'react'

import { useIsMobile } from '~/atoms'
import { preventDefault } from '~/lib/dom'
import { useModalStack } from '~/providers/root/modal-stack-provider'

import { PeekModal } from './PeekModal'

export const usePeek = () => {
const isMobile = useIsMobile()
const { present } = useModalStack()
return useCallback(
(href: string) => {
if (isMobile) return
const basePresentProps = {
clickOutsideToDismiss: true,
title: 'Preview',
modalClassName:
'relative mx-auto mt-[10vh] scrollbar-none max-w-full overflow-auto px-2 lg:max-w-[65rem] lg:p-0',
}

if (href.startsWith('/notes/')) {
requestAnimationFrame(async () => {
const NotePreview = await import('./NotePreview').then(
(module) => module.NotePreview,
)
present({
...basePresentProps,
CustomModalComponent: () => (
<PeekModal to={href}>
<NotePreview noteId={parseInt(href.split('/').pop()!)} />
</PeekModal>
),
content: () => null,
})
})

return true
} else if (href.startsWith('/posts/')) {
requestAnimationFrame(async () => {
const PostPreview = await import('./PostPreview').then(
(module) => module.PostPreview,
)
const splitpath = href.split('/')
const slug = splitpath.pop()!
const category = splitpath.pop()!
present({
...basePresentProps,
CustomModalComponent: () => (
<PeekModal to={href}>
<PostPreview category={category} slug={slug} />
</PeekModal>
),
content: () => null,
})
})
return true
}

return false
},
[isMobile, present],
)
}
import { usePeek } from './usePeek'

export const PeekLink: FC<
{
Expand Down
18 changes: 18 additions & 0 deletions src/components/widgets/peek/PeekPortal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useLayoutEffect } from 'react'

import { usePeek } from './usePeek'

declare global {
interface Window {
peek: ReturnType<typeof usePeek>
}
}

export const PeekPortal = () => {
const peek = usePeek()

useLayoutEffect(() => {
peek && (window.peek = peek)
}, [peek])
return null
}
63 changes: 63 additions & 0 deletions src/components/widgets/peek/usePeek.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useCallback } from 'react'

import { useIsMobile } from '~/atoms'
import { useModalStack } from '~/providers/root/modal-stack-provider'

import { PeekModal } from './PeekModal'

export const usePeek = () => {
const isMobile = useIsMobile()
const { present } = useModalStack()
return useCallback(
(href: string) => {
if (isMobile) return
const basePresentProps = {
clickOutsideToDismiss: true,
title: 'Preview',
modalClassName:
'relative mx-auto mt-[10vh] scrollbar-none max-w-full overflow-auto px-2 lg:max-w-[65rem] lg:p-0',
}

if (href.startsWith('/notes/')) {
requestAnimationFrame(async () => {
const NotePreview = await import('./NotePreview').then(
(module) => module.NotePreview,
)
present({
...basePresentProps,
CustomModalComponent: () => (
<PeekModal to={href}>
<NotePreview noteId={parseInt(href.split('/').pop()!)} />
</PeekModal>
),
content: () => null,
})
})

return true
} else if (href.startsWith('/posts/')) {
requestAnimationFrame(async () => {
const PostPreview = await import('./PostPreview').then(
(module) => module.PostPreview,
)
const splitpath = href.split('/')
const slug = splitpath.pop()!
const category = splitpath.pop()!
present({
...basePresentProps,
CustomModalComponent: () => (
<PeekModal to={href}>
<PostPreview category={category} slug={slug} />
</PeekModal>
),
content: () => null,
})
})
return true
}

return false
},
[isMobile, present],
)
}
16 changes: 12 additions & 4 deletions src/components/widgets/shared/ToastCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ export const ToastCard: FC<{
toastProps?: ToastProps
iconElement?: JSX.Element
closeToast?: () => void
onClick?: () => void
}> = (props) => {
const { iconElement, message, closeToast } = props
const { iconElement, message, closeToast, onClick } = props
const id = useId()

const MotionTag = onClick ? m.button : m.div

return (
<m.div
<MotionTag
layoutId={id}
layout="position"
className={clsx(
Expand All @@ -39,17 +43,21 @@ export const ToastCard: FC<{
'flex items-center',
'select-none',
)}
onClick={onClick}
>
{iconElement ?? typeMap[props.toastProps?.type ?? 'default']}
<span>{message}</span>

<MotionButtonBase
aria-label="Close toast"
className="absolute bottom-0 right-3 top-0 flex items-center text-sm text-base-content/40 duration-200 hover:text-base-content/80"
onClick={closeToast}
onClick={(e) => {
e.stopPropagation()
closeToast?.()
}}
>
<i className="icon-[mingcute--close-fill] p-2" />
</MotionButtonBase>
</m.div>
</MotionTag>
)
}
1 change: 1 addition & 0 deletions src/lib/route-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const enum Routes {

Says = '/says',
Friends = '/friends',
Thinking = '/thinking',

PageDeletd = '/common/deleted',
}
Expand Down
26 changes: 15 additions & 11 deletions src/lib/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,34 @@ interface ToastCustom {
): Id
}

interface CustomToastOptions {
iconElement?: JSX.Element
onClick?: () => void
}
interface ToastCustom {
success(message: string, options?: ToastOptions): Id
info(message: string, options?: ToastOptions): Id
warn(message: string, options?: ToastOptions): Id
error(message: string, options?: ToastOptions): Id
success(message: string, options?: ToastOptions & CustomToastOptions): Id
info(message: string, options?: ToastOptions & CustomToastOptions): Id
warn(message: string, options?: ToastOptions & CustomToastOptions): Id
error(message: string, options?: ToastOptions & CustomToastOptions): Id
}

// @ts-ignore
export const toast: ToastCustom = (
message: string,
type?: TypeOptions,
options?: ToastOptions & {
iconElement?: JSX.Element
},
options?: ToastOptions & CustomToastOptions,
) => {
const { iconElement, ...rest } = options || {}
return Toast(createElement(ToastCard, { message, iconElement }), {
const { iconElement, onClick, ...rest } = options || {}
return Toast(createElement(ToastCard, { message, iconElement, onClick }), {
type,
...baseConfig,
...rest,
})
}
;['success', 'info', 'warn', 'error'].forEach((type) => {
// @ts-ignore
toast[type] = (message: string, options?: ToastOptions) =>
toast(message, type as TypeOptions, options)
toast[type] = (
message: string,
options?: ToastOptions & CustomToastOptions,
) => toast(message, type as TypeOptions, options)
})
3 changes: 3 additions & 0 deletions src/providers/root/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { LazyMotion } from 'framer-motion'
import { ThemeProvider } from 'next-themes'
import type { PropsWithChildren } from 'react'

import { PeekPortal } from '~/components/widgets/peek/PeekPortal'

import { ProviderComposer } from '../../components/common/ProviderComposer'
import { AccentColorProvider } from './accent-color-provider'
import { DebugProvider } from './debug-provider'
Expand All @@ -31,6 +33,7 @@ export function Providers({ children }: PropsWithChildren) {
<>
<ProviderComposer contexts={contexts}>
{children}
<PeekPortal />
<SocketContainer />
<ModalStackProvider key="modalStackProvider" />
<EventProvider key="viewportProvider" />
Expand Down
56 changes: 55 additions & 1 deletion src/socket/handler.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { queryClient } from '~/providers/root/react-query-provider'
import React from 'react'
import { produce } from 'immer'
import type {
NoteModel,
PaginateResult,
PostModel,
RecentlyModel,
SayModel,
} from '@mx-space/api-client'
import type { InfiniteData } from '@tanstack/react-query'
import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context'

import { sayQueryKey } from '~/app/says/query'
import { QUERY_KEY as ThinkingQueryKey } from '~/app/thinking/constants'
import { setOnlineCount } from '~/atoms'
import { setActivityMediaInfo, setActivityProcessName } from '~/atoms/activity'
import {
FaSolidFeatherAlt,
IcTwotoneSignpost,
MdiLightbulbOn20,
} from '~/components/icons/menu-collection'
import { isDev } from '~/lib/env'
import { routeBuilder, Routes } from '~/lib/route-builder'
import { toast } from '~/lib/toast'
Expand Down Expand Up @@ -95,7 +103,53 @@ export const eventHandler = (
break
}

// TODO create event
case EventTypes.NOTE_CREATE: {
const { title, nid } = data as NoteModel

toast.success('有新的内容发布了:' + `「${title}」`, {
onClick: () => {
window.peek(`/notes/${nid}`)
},
iconElement: React.createElement(FaSolidFeatherAlt),
autoClose: false,
})

break
}

case EventTypes.POST_CREATE: {
const { title, category, slug } = data as PostModel
toast.success('有新的内容发布了:' + `「${title}」`, {
onClick: () => {
window.peek(`/posts/${category.slug}/${slug}`)
},
iconElement: React.createElement(IcTwotoneSignpost),
})

break
}

case EventTypes.RECENTLY_CREATE: {
if (location.pathname === routeBuilder(Routes.Thinking, {})) {
queryClient.setQueryData<InfiniteData<RecentlyModel[]>>(
ThinkingQueryKey,
(prev) => {
return produce(prev, (draft) => {
draft?.pages[0].unshift(data as RecentlyModel)
})
},
)
} else {
toast.success(`写下一点小思考:\n${(data as RecentlyModel).content}`, {
autoClose: 10000,
iconElement: React.createElement(MdiLightbulbOn20),
onClick: () => {
router.push(routeBuilder(Routes.Thinking, {}))
},
})
}
break
}

case EventTypes.SAY_CREATE: {
if (location.pathname === routeBuilder(Routes.Says, {})) {
Expand Down

1 comment on commit fc6c00e

@vercel
Copy link

@vercel vercel bot commented on fc6c00e Jul 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

shiro – ./

innei.in
shiro-innei.vercel.app
springtide.vercel.app
shiro-git-main-innei.vercel.app

Please sign in to comment.