mobile: use sticky position for action bars for mobile compatibility (#335)
This commit is contained in:
@@ -159,6 +159,8 @@ For Android:
|
||||
pnpm tauri android init
|
||||
|
||||
pnpm tauri android dev
|
||||
# or if you want to dev on a real device
|
||||
pnpm tauri android dev --host
|
||||
```
|
||||
|
||||
For iOS:
|
||||
@@ -168,12 +170,16 @@ For iOS:
|
||||
pnpm tauri ios init
|
||||
|
||||
pnpm tauri ios dev
|
||||
# or if you want to dev on a real device
|
||||
pnpm tauri ios dev --host
|
||||
```
|
||||
|
||||
### 5. Build for Production
|
||||
|
||||
```bash
|
||||
pnpm tauri build
|
||||
pnpm tauri android build
|
||||
pnpm tauri ios build
|
||||
```
|
||||
|
||||
### 6. Setup dev environment with Nix
|
||||
|
||||
@@ -253,35 +253,49 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{selectedBooks.length > 0 && (
|
||||
<div
|
||||
className={clsx(
|
||||
'text-base-content bg-base-300 fixed bottom-4 left-1/2 flex',
|
||||
'-translate-x-1/2 transform space-x-4 rounded-lg p-4 shadow-lg',
|
||||
)}
|
||||
>
|
||||
<button onClick={openSelectedBooks} className='flex items-center space-x-2'>
|
||||
<MdOpenInNew />
|
||||
<span>{_('Open')}</span>
|
||||
</button>
|
||||
<button onClick={deleteSelectedBooks} className='flex items-center space-x-2'>
|
||||
<MdDelete className='fill-red-500' />
|
||||
<span className='text-red-500'>{_('Delete')}</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{loading && (
|
||||
<div className='fixed inset-0 z-50 flex items-center justify-center'>
|
||||
<Spinner loading />
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={clsx(
|
||||
'action-bar sticky bottom-0 left-0 right-0 z-40',
|
||||
'pb-[calc(env(safe-area-inset-bottom)+16px)]',
|
||||
)}
|
||||
>
|
||||
{selectedBooks.length > 0 && (
|
||||
<div
|
||||
className={clsx(
|
||||
'text-base-content bg-base-300 mx-auto flex w-fit',
|
||||
'space-x-4 rounded-lg p-4 shadow-lg',
|
||||
)}
|
||||
>
|
||||
<button onClick={openSelectedBooks} className='flex items-center space-x-2'>
|
||||
<MdOpenInNew />
|
||||
<span>{_('Open')}</span>
|
||||
</button>
|
||||
<button onClick={deleteSelectedBooks} className='flex items-center space-x-2'>
|
||||
<MdDelete className='fill-red-500' />
|
||||
<span className='text-red-500'>{_('Delete')}</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showDeleteAlert && (
|
||||
<Alert
|
||||
title={_('Confirm Deletion')}
|
||||
message={_('Are you sure to delete the selected books?')}
|
||||
onClickCancel={() => setShowDeleteAlert(false)}
|
||||
onClickConfirm={confirmDelete}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
'sticky bottom-0 left-0 right-0 z-50 flex justify-center',
|
||||
'pb-[calc(env(safe-area-inset-bottom)+16px)]',
|
||||
)}
|
||||
>
|
||||
<Alert
|
||||
title={_('Confirm Deletion')}
|
||||
message={_('Are you sure to delete the selected books?')}
|
||||
onClickCancel={() => setShowDeleteAlert(false)}
|
||||
onClickConfirm={confirmDelete}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import clsx from 'clsx';
|
||||
import * as React from 'react';
|
||||
import { useState, useRef, useEffect, Suspense } from 'react';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Book } from '@/types/book';
|
||||
@@ -384,18 +384,16 @@ const LibraryPage = () => {
|
||||
appService?.hasSafeAreaInset && 'mt-[calc(48px+env(safe-area-inset-top))]',
|
||||
)}
|
||||
>
|
||||
<Suspense>
|
||||
<Bookshelf
|
||||
libraryBooks={libraryBooks}
|
||||
isSelectMode={isSelectMode}
|
||||
handleImportBooks={handleImportBooks}
|
||||
handleBookUpload={handleBookUpload}
|
||||
handleBookDownload={handleBookDownload}
|
||||
handleBookDelete={handleBookDelete}
|
||||
handleShowDetailsBook={handleShowDetailsBook}
|
||||
booksTransferProgress={booksTransferProgress}
|
||||
/>
|
||||
</Suspense>
|
||||
<Bookshelf
|
||||
libraryBooks={libraryBooks}
|
||||
isSelectMode={isSelectMode}
|
||||
handleImportBooks={handleImportBooks}
|
||||
handleBookUpload={handleBookUpload}
|
||||
handleBookDownload={handleBookDownload}
|
||||
handleBookDelete={handleBookDelete}
|
||||
handleShowDetailsBook={handleShowDetailsBook}
|
||||
booksTransferProgress={booksTransferProgress}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className='hero h-screen items-center justify-center'>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
|
||||
const Alert: React.FC<{
|
||||
@@ -10,40 +10,42 @@ const Alert: React.FC<{
|
||||
}> = ({ title, message, onClickCancel, onClickConfirm }) => {
|
||||
const _ = useTranslation();
|
||||
return (
|
||||
<div
|
||||
role='alert'
|
||||
className={clsx(
|
||||
'alert fixed bottom-4 left-1/2 z-[100] flex -translate-x-1/2 transform items-center justify-between',
|
||||
'bg-base-300 rounded-lg p-4 shadow-2xl',
|
||||
'w-full max-w-[90vw] sm:max-w-[70vw] md:max-w-[50vw] lg:max-w-[40vw] xl:max-w-[40vw]',
|
||||
)}
|
||||
>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
className='stroke-info h-6 w-6 shrink-0'
|
||||
>
|
||||
<path
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth='2'
|
||||
d='M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'
|
||||
></path>
|
||||
</svg>
|
||||
<div className=''>
|
||||
<h3 className='font-bold'>{title}</h3>
|
||||
<div className='text-xs'>{message}</div>
|
||||
<div className={clsx('z-[100] flex justify-center px-4')}>
|
||||
<div
|
||||
role='alert'
|
||||
className={clsx(
|
||||
'alert flex items-center justify-between',
|
||||
'bg-base-300 rounded-lg border-none p-4 shadow-2xl',
|
||||
'w-full max-w-[90vw] sm:max-w-[70vw] md:max-w-[50vw] lg:max-w-[40vw] xl:max-w-[40vw]',
|
||||
)}
|
||||
>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
className='stroke-info h-6 w-6 shrink-0'
|
||||
>
|
||||
<path
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth='2'
|
||||
d='M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'
|
||||
></path>
|
||||
</svg>
|
||||
<div className=''>
|
||||
<h3 className='font-bold'>{title}</h3>
|
||||
<div className='text-xs'>{message}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex space-x-2'>
|
||||
<button className='btn btn-sm' onClick={onClickCancel}>
|
||||
{_('Cancel')}
|
||||
</button>
|
||||
<button className='btn btn-sm btn-warning' onClick={onClickConfirm}>
|
||||
{_('Confirm')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex space-x-2'>
|
||||
<button className='btn btn-sm' onClick={onClickCancel}>
|
||||
{_('Cancel')}
|
||||
</button>
|
||||
<button className='btn btn-sm btn-warning' onClick={onClickConfirm}>
|
||||
{_('Confirm')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,8 @@ export const usePullToRefresh = (ref: React.RefObject<HTMLDivElement>, onTrigger
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
|
||||
if (el.scrollTop > 0) return;
|
||||
|
||||
const initialY = startEvent.touches[0]!.clientY;
|
||||
|
||||
el.addEventListener('touchmove', handleTouchMove, { passive: false });
|
||||
|
||||
Reference in New Issue
Block a user