mirror of
https://github.com/penxio/penx.git
synced 2026-04-19 03:03:06 -04:00
feat: improve login ui
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
"showSpinner": false
|
||||
},
|
||||
"Keyboard": {
|
||||
"resize": "none"
|
||||
"resize": "ionic"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"showSpinner": false
|
||||
},
|
||||
"Keyboard": {
|
||||
"resize": "none"
|
||||
"resize": "ionic"
|
||||
}
|
||||
},
|
||||
"packageClassList": [
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Capacitor } from '@capacitor/core'
|
||||
import { StatusBar, Style } from '@capacitor/status-bar'
|
||||
import {
|
||||
IonApp,
|
||||
IonNav,
|
||||
IonRouterOutlet,
|
||||
IonSplitPane,
|
||||
setupIonicReact,
|
||||
@@ -41,6 +42,11 @@ import '@ionic/react/css/palettes/dark.class.css'
|
||||
// import '@ionic/react/css/palettes/dark.system.css'
|
||||
/* Theme variables */
|
||||
import './theme/variables.css'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { appEmitter } from '@penx/emitter'
|
||||
import { useCreationId } from '@penx/hooks/useCreationId'
|
||||
import { ICreationNode } from '@penx/model-type'
|
||||
import { PageCreation } from './pages/PageCreation'
|
||||
|
||||
async function init() {
|
||||
const platform = Capacitor.getPlatform()
|
||||
@@ -113,6 +119,27 @@ init()
|
||||
setupIonicReact()
|
||||
|
||||
const App: React.FC = () => {
|
||||
const nav = useRef<HTMLIonNavElement>(null)
|
||||
const { creationId, setCreationId } = useCreationId()
|
||||
|
||||
useEffect(() => {
|
||||
// if (initRef.current) return
|
||||
// initRef.current = true
|
||||
function handle(creation: ICreationNode) {
|
||||
console.log('handle route to creation: ', creation.id)
|
||||
// setCreationId(creation.id)
|
||||
nav.current?.push(PageCreation, {
|
||||
creationId: creation.id,
|
||||
nav: nav.current,
|
||||
})
|
||||
}
|
||||
|
||||
appEmitter.on('ROUTE_TO_CREATION', handle)
|
||||
return () => {
|
||||
appEmitter.off('ROUTE_TO_CREATION', handle)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<IonApp>
|
||||
<LinguiClientProvider initialLocale={'en'} initialMessages={{}}>
|
||||
@@ -126,7 +153,7 @@ const App: React.FC = () => {
|
||||
<Redirect to="/folder/area" />
|
||||
</Route>
|
||||
<Route path="/folder/:name" exact={true}>
|
||||
<PageHome />
|
||||
<IonNav ref={nav} root={() => <PageHome />}></IonNav>
|
||||
</Route>
|
||||
</IonRouterOutlet>
|
||||
</IonSplitPane>
|
||||
|
||||
@@ -20,7 +20,7 @@ export const Footer = ({ onAdd }: Props) => {
|
||||
'--border-width': 0,
|
||||
}}
|
||||
>
|
||||
<div className="bg-background flex items-center justify-between gap-3 rounded-full px-3 dark:bg-neutral-900">
|
||||
<div className="flex items-center justify-between gap-3 rounded-full px-3">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
@@ -36,7 +36,7 @@ export const Footer = ({ onAdd }: Props) => {
|
||||
variant="ghost"
|
||||
className={cn('size-8 rounded-full')}
|
||||
onClick={async () => {
|
||||
setType('HOME')
|
||||
setType('NOTE')
|
||||
}}
|
||||
>
|
||||
<span className="icon-[solar--notes-linear] size-6"></span>
|
||||
@@ -66,7 +66,7 @@ export const Footer = ({ onAdd }: Props) => {
|
||||
variant="ghost"
|
||||
className={cn('size-8 rounded-full')}
|
||||
onClick={async () => {
|
||||
setType('TASK')
|
||||
setType('PROFILE')
|
||||
}}
|
||||
>
|
||||
<span className="icon-[solar--user-linear] size-6"></span>
|
||||
|
||||
@@ -15,10 +15,8 @@ import { IconGoogle } from '@penx/uikit/IconGoogle'
|
||||
import { LoadingDots } from '@penx/uikit/loading-dots'
|
||||
import { LoginForm } from './LoginForm'
|
||||
|
||||
interface Props {
|
||||
setVisible: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}
|
||||
export function AppleLoginButton({ setVisible }: Props) {
|
||||
interface Props {}
|
||||
export function AppleLoginButton({}: Props) {
|
||||
const { login } = useSession()
|
||||
const [json, setJson] = useState({})
|
||||
const [error, setError] = useState({})
|
||||
@@ -58,7 +56,6 @@ export function AppleLoginButton({ setVisible }: Props) {
|
||||
await set('SESSION', session)
|
||||
queryClient.setQueryData(['SESSION'], session)
|
||||
appEmitter.emit('APP_LOGIN_SUCCESS', session)
|
||||
setVisible(false)
|
||||
} catch (error) {
|
||||
console.log('=========error:', error)
|
||||
|
||||
|
||||
42
apps/mobile/src/components/Login/EmailLoginButton.tsx
Normal file
42
apps/mobile/src/components/Login/EmailLoginButton.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
'use client'
|
||||
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { PageEmailLogin } from '@/pages/PageEmailLogin'
|
||||
import { Capacitor } from '@capacitor/core'
|
||||
import { SocialLogin } from '@capgo/capacitor-social-login'
|
||||
import { IonButton, IonNavLink, useIonRouter } from '@ionic/react'
|
||||
import { set } from 'idb-keyval'
|
||||
import { MailIcon } from 'lucide-react'
|
||||
import { appEmitter } from '@penx/emitter'
|
||||
import { localDB } from '@penx/local-db'
|
||||
import { queryClient } from '@penx/query-client'
|
||||
import { useSession } from '@penx/session'
|
||||
import { MobileGoogleLoginInfo } from '@penx/types'
|
||||
import { Button } from '@penx/uikit/button'
|
||||
import { IconGoogle } from '@penx/uikit/IconGoogle'
|
||||
import { LoadingDots } from '@penx/uikit/loading-dots'
|
||||
import { LoginForm } from './LoginForm'
|
||||
|
||||
interface Props {}
|
||||
export function EmailLoginButton({}: Props) {
|
||||
const { login } = useSession()
|
||||
const [json, setJson] = useState({})
|
||||
const [error, setError] = useState({})
|
||||
const [session, setSession] = useState({})
|
||||
const [loading, setLoading] = useState(false)
|
||||
const router = useIonRouter()
|
||||
|
||||
return (
|
||||
<IonNavLink routerDirection="forward" component={() => <PageEmailLogin />}>
|
||||
<Button className="w-full gap-2">
|
||||
{loading && <LoadingDots className="bg-foreground" />}
|
||||
{!loading && (
|
||||
<>
|
||||
<MailIcon size={20} />
|
||||
<div className="">Email login</div>
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</IonNavLink>
|
||||
)
|
||||
}
|
||||
@@ -14,10 +14,8 @@ import { IconGoogle } from '@penx/uikit/IconGoogle'
|
||||
import { LoadingDots } from '@penx/uikit/loading-dots'
|
||||
import { LoginForm } from './LoginForm'
|
||||
|
||||
interface Props {
|
||||
setVisible: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}
|
||||
export function GoogleLoginButton({ setVisible }: Props) {
|
||||
interface Props {}
|
||||
export function GoogleLoginButton({}: Props) {
|
||||
const { login } = useSession()
|
||||
const [json, setJson] = useState({})
|
||||
const [error, setError] = useState({})
|
||||
@@ -53,7 +51,6 @@ export function GoogleLoginButton({ setVisible }: Props) {
|
||||
await set('SESSION', session)
|
||||
queryClient.setQueryData(['SESSION'], session)
|
||||
appEmitter.emit('APP_LOGIN_SUCCESS', session)
|
||||
setVisible(false)
|
||||
} catch (error) {
|
||||
console.log('=========error:', error)
|
||||
|
||||
@@ -64,7 +61,7 @@ export function GoogleLoginButton({ setVisible }: Props) {
|
||||
setLoading(false)
|
||||
}
|
||||
return (
|
||||
<Button onClick={onLogin} className="w-full gap-2" variant="secondary">
|
||||
<Button onClick={onLogin} className="w-full gap-2">
|
||||
{loading && <LoadingDots className="bg-foreground" />}
|
||||
{!loading && (
|
||||
<>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { IonMenuToggle } from '@ionic/react'
|
||||
import { Drawer } from 'vaul'
|
||||
import { useSession } from '@penx/session'
|
||||
import { Button } from '@penx/uikit/button'
|
||||
import { DialogDescription, DialogTitle } from '@penx/uikit/dialog'
|
||||
import { LoginContent } from './LoginContent'
|
||||
import { ProfileButton } from './ProfileButton'
|
||||
|
||||
export function LoginButton() {
|
||||
const { isLoading, session } = useSession()
|
||||
const [visible, setVisible] = useState(false)
|
||||
if (session) {
|
||||
return <ProfileButton />
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<IonMenuToggle>
|
||||
<Button size="sm" onClick={() => setVisible(true)}>
|
||||
Log in
|
||||
</Button>
|
||||
</IonMenuToggle>
|
||||
|
||||
<Drawer.Root open={visible} onOpenChange={setVisible}>
|
||||
<Drawer.Portal>
|
||||
<Drawer.Overlay className="fixed inset-0 bg-black/40" />
|
||||
<Drawer.Content className="bg-background text-foreground fixed bottom-0 left-0 right-0 flex max-h-[95vh] min-h-[95vh] flex-col rounded-t-[10px] px-0 pb-0 outline-none">
|
||||
<div
|
||||
aria-hidden
|
||||
className="bg-foreground/30 mx-auto mb-4 mt-2 h-1 w-10 flex-shrink-0 rounded-full"
|
||||
/>
|
||||
|
||||
<DialogTitle className="hidden">
|
||||
<DialogDescription />
|
||||
</DialogTitle>
|
||||
{session && <div>{JSON.stringify(session, null, 2)}</div>}
|
||||
<LoginContent setVisible={setVisible} />
|
||||
</Drawer.Content>
|
||||
</Drawer.Portal>
|
||||
</Drawer.Root>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -13,24 +13,24 @@ import { Button } from '@penx/uikit/button'
|
||||
import { IconGoogle } from '@penx/uikit/IconGoogle'
|
||||
import { LoadingDots } from '@penx/uikit/loading-dots'
|
||||
import { AppleLoginButton } from './AppleLoginButton'
|
||||
import { EmailLoginButton } from './EmailLoginButton'
|
||||
import { GoogleLoginButton } from './GoogleLoginButton'
|
||||
import { LoginForm } from './LoginForm'
|
||||
|
||||
interface Props {
|
||||
setVisible: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}
|
||||
interface Props {}
|
||||
|
||||
const platform = Capacitor.getPlatform()
|
||||
|
||||
export function LoginContent({ setVisible }: Props) {
|
||||
export function LoginContent({}: Props) {
|
||||
return (
|
||||
<div className="-mt-10 flex h-full flex-1 flex-col justify-center px-6">
|
||||
<div className="flex h-full flex-1 flex-col justify-center px-6">
|
||||
<div className="space-y-2">
|
||||
<GoogleLoginButton setVisible={setVisible} />
|
||||
{platform === 'ios' && <AppleLoginButton setVisible={setVisible} />}
|
||||
<GoogleLoginButton />
|
||||
{platform === 'ios' && <AppleLoginButton />}
|
||||
|
||||
<div className="text-foreground/40 my-4 text-center">or</div>
|
||||
<LoginForm setVisible={setVisible} />
|
||||
<EmailLoginButton />
|
||||
{/* <div className="text-foreground/40 my-4 text-center">or</div>
|
||||
<LoginForm setVisible={setVisible} /> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -32,11 +32,9 @@ const FormSchema = z.object({
|
||||
}),
|
||||
})
|
||||
|
||||
interface Props {
|
||||
setVisible: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}
|
||||
interface Props {}
|
||||
|
||||
export function LoginForm({ setVisible }: Props) {
|
||||
export function LoginForm({}: Props) {
|
||||
const [isLoading, setLoading] = useState(false)
|
||||
const { login } = useSession()
|
||||
|
||||
@@ -64,7 +62,6 @@ export function LoginForm({ setVisible }: Props) {
|
||||
if (!session.isLoggedIn) {
|
||||
toast.error(session.message)
|
||||
} else {
|
||||
setVisible(false)
|
||||
await set('SESSION', session)
|
||||
queryClient.setQueryData(['SESSION'], session)
|
||||
appEmitter.emit('APP_LOGIN_SUCCESS', session)
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { Drawer } from 'vaul'
|
||||
import { useSession } from '@penx/session'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@penx/uikit/avatar'
|
||||
import { Button } from '@penx/uikit/button'
|
||||
import { DialogDescription, DialogTitle } from '@penx/uikit/dialog'
|
||||
import { cn, getUrl } from '@penx/utils'
|
||||
import { generateGradient } from '@penx/utils/generateGradient'
|
||||
import { LoginContent } from './LoginContent'
|
||||
|
||||
export function ProfileButton() {
|
||||
const { session, logout } = useSession()
|
||||
const [visible, setVisible] = useState(false)
|
||||
if (!session) return
|
||||
|
||||
return (
|
||||
<>
|
||||
<Avatar className="size-7" onClick={() => setVisible(true)}>
|
||||
<AvatarImage src={getUrl(session?.image)} />
|
||||
<AvatarFallback
|
||||
className={cn(generateGradient(session?.name))}
|
||||
></AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
<Drawer.Root open={visible} onOpenChange={setVisible}>
|
||||
<Drawer.Portal>
|
||||
<Drawer.Overlay className="fixed inset-0 bg-black/40" />
|
||||
<Drawer.Content className="bg-background fixed bottom-0 left-0 right-0 flex max-h-[95vh] min-h-[95vh] flex-col rounded-t-[10px] px-0 pb-0 outline-none text-foreground">
|
||||
<div
|
||||
aria-hidden
|
||||
className="mx-auto mb-4 mt-2 h-1.5 w-12 flex-shrink-0 rounded-full"
|
||||
/>
|
||||
|
||||
<DialogTitle className="hidden">
|
||||
<DialogDescription />
|
||||
</DialogTitle>
|
||||
<div className="flex flex-1 flex-col items-center">
|
||||
<div className="flex flex-1 flex-col items-center">
|
||||
<Avatar className="size-12" onClick={() => setVisible(true)}>
|
||||
<AvatarImage src={getUrl(session?.image)} />
|
||||
<AvatarFallback
|
||||
className={cn(generateGradient(session?.name))}
|
||||
></AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="font-semibold">{session.name}</div>
|
||||
<div className="text-foreground/50 text-sm">
|
||||
{session.email}
|
||||
</div>
|
||||
</div>
|
||||
<div className="pb-6">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="mt-4 text-red-500"
|
||||
onClick={() => {
|
||||
setVisible(false)
|
||||
logout()
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Drawer.Content>
|
||||
</Drawer.Portal>
|
||||
</Drawer.Root>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { IonContent, IonMenu } from '@ionic/react';
|
||||
import { ProfileButton } from '@penx/components/ProfileButton';
|
||||
import { useSession } from '@penx/session';
|
||||
import { cn } from '@penx/utils';
|
||||
import { AreaList } from './AreaList';
|
||||
import { LoginButton } from './Login/LoginButton';
|
||||
import { MobileModeToggle } from './MobileModeToggle';
|
||||
|
||||
import { Capacitor } from '@capacitor/core'
|
||||
import { IonContent, IonMenu } from '@ionic/react'
|
||||
import { useSession } from '@penx/session'
|
||||
import { cn } from '@penx/utils'
|
||||
import { AreaList } from './AreaList'
|
||||
import { MobileModeToggle } from './MobileModeToggle'
|
||||
|
||||
const platform = Capacitor.getPlatform()
|
||||
|
||||
@@ -17,7 +14,7 @@ const Menu: React.FC = () => {
|
||||
|
||||
return (
|
||||
<IonMenu contentId="main" type="overlay">
|
||||
<IonContent className="ion-padding safe-area h-full drawer-menu">
|
||||
<IonContent className="ion-padding safe-area drawer-menu h-full">
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-full flex-col pt-5',
|
||||
@@ -29,7 +26,6 @@ const Menu: React.FC = () => {
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<MobileModeToggle />
|
||||
<ProfileButton loginButton={<LoginButton></LoginButton>} />
|
||||
</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
@@ -37,4 +33,4 @@ const Menu: React.FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export default Menu
|
||||
export default Menu
|
||||
|
||||
@@ -7,14 +7,9 @@ import { AreaWidgets } from '@penx/components/AreaWidgets'
|
||||
import { MobileAddCreationButton } from './MobileAddCreationButton'
|
||||
|
||||
export function MobileHome() {
|
||||
const [mode, setMode] = React.useState({})
|
||||
useEffect(() => {
|
||||
DarkMode.isDarkMode().then((isDark) => setMode(isDark))
|
||||
}, [])
|
||||
return (
|
||||
<div className="">
|
||||
{/* <QuickSearchTrigger /> */}
|
||||
<div>isDark: {JSON.stringify(mode, null, 2)}</div>
|
||||
<AreaWidgets />
|
||||
{/* <div className="fixed bottom-3 left-0 flex w-full items-center justify-center">
|
||||
<MobileAddCreationButton />
|
||||
|
||||
@@ -4,52 +4,35 @@ import { MobileCreation } from '@/components/MobileCreation'
|
||||
import { Capacitor } from '@capacitor/core'
|
||||
import { OverlayEventDetail } from '@ionic/core'
|
||||
import {
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonContent,
|
||||
IonFab,
|
||||
IonHeader,
|
||||
IonModal,
|
||||
IonToolbar,
|
||||
} from '@ionic/react'
|
||||
import { XIcon } from 'lucide-react'
|
||||
import { appEmitter } from '@penx/emitter'
|
||||
import { useCreationId } from '@penx/hooks/useCreationId'
|
||||
import { ICreationNode } from '@penx/model-type'
|
||||
|
||||
const platform = Capacitor.getPlatform()
|
||||
|
||||
export const PageCreation: React.FC = () => {
|
||||
const modal = useRef<HTMLIonModalElement>(null)
|
||||
const [creationId, setCreationId] = useState('')
|
||||
export const PageCreation = ({
|
||||
creationId,
|
||||
nav,
|
||||
}: {
|
||||
creationId: string
|
||||
nav: HTMLIonNavElement
|
||||
}) => {
|
||||
// const { creationId, setCreationId } = useCreationId()
|
||||
|
||||
useEffect(() => {
|
||||
// if (initRef.current) return
|
||||
// initRef.current = true
|
||||
function handle(creation: ICreationNode) {
|
||||
console.log('handle route to creation: ', creation.id)
|
||||
setCreationId(creation.id)
|
||||
modal.current?.present()
|
||||
}
|
||||
|
||||
appEmitter.on('ROUTE_TO_CREATION', handle)
|
||||
return () => {
|
||||
appEmitter.off('ROUTE_TO_CREATION', handle)
|
||||
}
|
||||
}, [])
|
||||
|
||||
function onWillDismiss(event: CustomEvent<OverlayEventDetail>) {
|
||||
if (event.detail.role === 'confirm') {
|
||||
// setMessage(`Hello, ${event.detail.data}!`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!creationId) return null
|
||||
// if (!creationId) return null
|
||||
|
||||
return (
|
||||
<IonModal
|
||||
ref={modal}
|
||||
trigger="open-modal"
|
||||
onWillDismiss={(event) => onWillDismiss(event)}
|
||||
>
|
||||
<>
|
||||
<IonHeader
|
||||
className={platform === 'android' ? 'safe-area' : ''}
|
||||
style={{
|
||||
@@ -66,16 +49,15 @@ export const PageCreation: React.FC = () => {
|
||||
}}
|
||||
>
|
||||
<IonButtons slot="start">
|
||||
<IonButton color="dark" onClick={() => modal.current?.dismiss()}>
|
||||
<XIcon size={20} />
|
||||
</IonButton>
|
||||
<IonBackButton color="dark" text=""></IonBackButton>
|
||||
</IonButtons>
|
||||
{/* <IonTitle>Welcome</IonTitle> */}
|
||||
<IonButtons slot="end">
|
||||
<CreationMenu
|
||||
creationId={creationId}
|
||||
afterDelete={() => {
|
||||
modal.current?.dismiss()
|
||||
// modal.current?.dismiss()
|
||||
nav.pop()
|
||||
}}
|
||||
/>
|
||||
</IonButtons>
|
||||
@@ -83,7 +65,14 @@ export const PageCreation: React.FC = () => {
|
||||
</IonHeader>
|
||||
<IonContent className="ion-padding">
|
||||
<MobileCreation creationId={creationId} />
|
||||
<IonFab
|
||||
slot="fixed"
|
||||
vertical="bottom"
|
||||
className="flex w-full justify-center"
|
||||
>
|
||||
<div className="h-10 bg-amber-100">Hello world</div>
|
||||
</IonFab>
|
||||
</IonContent>
|
||||
</IonModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
33
apps/mobile/src/pages/PageEmailLogin.tsx
Normal file
33
apps/mobile/src/pages/PageEmailLogin.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonNavLink,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
} from '@ionic/react'
|
||||
|
||||
export function PageEmailLogin() {
|
||||
return (
|
||||
<>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton></IonBackButton>
|
||||
</IonButtons>
|
||||
<IonTitle>Page Two</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent class="ion-padding">
|
||||
<h1>Page Two</h1>
|
||||
{/* <IonNavLink routerDirection="forward" component={() => <PageThree />}>
|
||||
<IonButton>Go to Page Three</IonButton>
|
||||
</IonNavLink> */}
|
||||
</IonContent>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { Footer } from '@/components/Footer'
|
||||
import { LoginContent } from '@/components/Login/LoginContent'
|
||||
import { MobileHome } from '@/components/MobileHome'
|
||||
import { SearchButton } from '@/components/MobileSearch/SearchButton'
|
||||
import { MobileTask } from '@/components/MobileTask/MobileTask'
|
||||
@@ -37,6 +38,8 @@ import { AreaDialog } from '@penx/components/AreaDialog'
|
||||
import { QuickInput } from '@penx/components/QuickInput'
|
||||
import { appEmitter } from '@penx/emitter'
|
||||
import { useArea } from '@penx/hooks/useArea'
|
||||
import { useCreationId } from '@penx/hooks/useCreationId'
|
||||
import { ICreationNode } from '@penx/model-type'
|
||||
import { Button } from '@penx/uikit/button'
|
||||
import { Separator } from '@penx/uikit/separator'
|
||||
import { cn } from '@penx/utils'
|
||||
@@ -44,7 +47,7 @@ import { PageCreation } from './PageCreation'
|
||||
|
||||
const platform = Capacitor.getPlatform()
|
||||
|
||||
const PageHome: React.FC = () => {
|
||||
const PageHome: React.FC = ({ nav }: any) => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [scrolled, setScrolled] = useState(false)
|
||||
const { area } = useArea()
|
||||
@@ -53,6 +56,8 @@ const PageHome: React.FC = () => {
|
||||
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
const { creationId, setCreationId } = useCreationId()
|
||||
|
||||
const handleScroll = (event: CustomEvent) => {
|
||||
const scrollTop = event.detail.scrollTop
|
||||
setScrolled(scrollTop > 0)
|
||||
@@ -185,7 +190,7 @@ const PageHome: React.FC = () => {
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen className="text-foreground">
|
||||
<IonContent fullscreen className="text-foreground content">
|
||||
{/* <IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">{name}</IonTitle>
|
||||
@@ -193,21 +198,19 @@ const PageHome: React.FC = () => {
|
||||
</IonHeader> */}
|
||||
|
||||
<div
|
||||
className="text-foreground relative flex flex-col px-1 pb-14"
|
||||
className="text-foreground relative flex min-h-full flex-col px-1"
|
||||
style={
|
||||
{
|
||||
'--background': 'oklch(1 0 0)',
|
||||
} as any
|
||||
}
|
||||
>
|
||||
<div className="">
|
||||
{isHome && <MobileHome />}
|
||||
{!isHome && <MobileTask />}
|
||||
</div>
|
||||
{type === 'HOME' && <MobileHome />}
|
||||
{type === 'TASK' && <MobileTask />}
|
||||
{type === 'PROFILE' && <LoginContent />}
|
||||
</div>
|
||||
|
||||
<PageCreation />
|
||||
</IonContent>
|
||||
|
||||
<Footer
|
||||
onAdd={() => {
|
||||
setOpen(true)
|
||||
|
||||
@@ -15,16 +15,25 @@
|
||||
--background: white;
|
||||
}
|
||||
|
||||
.content {
|
||||
--background: white;
|
||||
}
|
||||
|
||||
.dark .content,
|
||||
.ion-palette-dark .content {
|
||||
--background: #222;
|
||||
}
|
||||
|
||||
.dark .toolbar,
|
||||
.ion-palette-dark .toolbar {
|
||||
/* --background: hsl(240 10% 3.9%); */
|
||||
--background: dark;
|
||||
--background: #222;
|
||||
}
|
||||
|
||||
.dark .drawer-menu,
|
||||
.ion-palette-dark .drawer-menu {
|
||||
/* --background: hsl(240 10% 3.9%); */
|
||||
--background: #111;
|
||||
--background: #333;
|
||||
}
|
||||
|
||||
:root,
|
||||
|
||||
@@ -243,6 +243,7 @@ export function Creation({ panel, className }: Props) {
|
||||
}
|
||||
showAddButton
|
||||
showFixedToolbar={false}
|
||||
// showFixedToolbar
|
||||
onChange={(v: any[]) => {
|
||||
const input: UpdateCreationInput = {
|
||||
id: creation.id,
|
||||
|
||||
@@ -27,6 +27,7 @@ import { cursorOverlayPlugin } from './cursor-overlay-plugin'
|
||||
import { deletePlugins } from './delete-plugins'
|
||||
import { dndPlugins } from './dnd-plugins'
|
||||
import { exitBreakPlugin } from './exit-break-plugin'
|
||||
import { FixedToolbarPlugin } from './fixed-toolbar-plugin'
|
||||
import { resetBlockTypePlugin } from './reset-block-type-plugin'
|
||||
import { softBreakPlugin } from './soft-break-plugin'
|
||||
import { viewPlugins } from './views-plugins'
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
'use client'
|
||||
|
||||
import { createPlatePlugin } from '@udecode/plate/react'
|
||||
import { FixedToolbar } from '@penx/editor-plugins/plate-ui/fixed-toolbar'
|
||||
import { FixedToolbarButtons } from '@penx/editor-plugins/plate-ui/fixed-toolbar-buttons'
|
||||
import { createPlatePlugin } from '@udecode/plate/react'
|
||||
|
||||
export const FixedToolbarPlugin = createPlatePlugin({
|
||||
key: 'fixed-toolbar',
|
||||
render: {
|
||||
beforeEditable: () => (
|
||||
<div className="sm:px-[max(10px,calc(50%-350px))]">
|
||||
<FixedToolbar className="bg-background">
|
||||
<FixedToolbarButtons />
|
||||
</FixedToolbar>
|
||||
</div>
|
||||
),
|
||||
beforeEditable: () => {
|
||||
return (
|
||||
<div className="sm:px-[max(10px,calc(50%-350px))]">
|
||||
<FixedToolbar className="bg-background">
|
||||
<FixedToolbarButtons />
|
||||
</FixedToolbar>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -4,12 +4,16 @@ import React, { useEffect, useImperativeHandle, useRef, useState } from 'react'
|
||||
import { DndProvider } from 'react-dnd'
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend'
|
||||
import isEqual from 'react-fast-compare'
|
||||
import { ItalicPlugin } from '@udecode/plate-basic-marks/react'
|
||||
import { Plate } from '@udecode/plate/react'
|
||||
import { ItalicIcon } from 'lucide-react'
|
||||
import {
|
||||
Editor,
|
||||
EditorContainer,
|
||||
EditorVariantProps,
|
||||
} from '@penx/editor-plugins/plate-ui/editor'
|
||||
import { MarkToolbarButton } from '@penx/editor-plugins/plate-ui/mark-toolbar-button'
|
||||
import { Toolbar } from '@penx/editor-plugins/plate-ui/toolbar'
|
||||
import {
|
||||
PlateEditorType,
|
||||
useCreateEditor,
|
||||
|
||||
3034
pnpm-lock.yaml
generated
3034
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user