improvements to the pwa

This commit is contained in:
daniel31x13 2024-01-14 10:09:09 -05:00
parent 834d25a99e
commit d4f59d7f32
18 changed files with 168 additions and 23 deletions

View File

@ -9,6 +9,7 @@ import useAccountStore from "@/store/account";
import EditCollectionModal from "./ModalContent/EditCollectionModal";
import EditCollectionSharingModal from "./ModalContent/EditCollectionSharingModal";
import DeleteCollectionModal from "./ModalContent/DeleteCollectionModal";
import { dropdownTriggerer } from "@/lib/client/utils";
type Props = {
collection: CollectionIncludingMembersAndLinkCount;
@ -70,6 +71,7 @@ export default function CollectionCard({ collection, className }: Props) {
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="btn btn-ghost btn-sm btn-square text-neutral"
>
<i className="bi-three-dots text-xl" title="More"></i>

View File

@ -1,3 +1,4 @@
import { dropdownTriggerer } from "@/lib/client/utils";
import React from "react";
type Props = {
@ -20,6 +21,7 @@ export default function FilterSearchDropdown({
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="btn btn-sm btn-square btn-ghost"
>
<i className="bi-funnel text-neutral text-2xl"></i>

View File

@ -126,17 +126,12 @@ export default function LinkGrid({ link, count, className }: Props) {
{unescapeString(link.name || link.description) || link.url}
</p>
<Link
href={link.url || ""}
target="_blank"
title={link.url || ""}
className="w-fit"
>
<div title={link.url || ""} className="w-fit">
<div className="flex gap-1 item-center select-none text-neutral mt-1">
<i className="bi-link-45deg text-lg mt-[0.15rem] leading-none"></i>
<p className="text-sm truncate">{shortendURL}</p>
</div>
</Link>
</div>
</div>
<hr className="divider mt-2 mb-1 last:hidden border-t border-neutral-content h-[1px]" />

View File

@ -10,6 +10,7 @@ import PreservedFormatsModal from "@/components/ModalContent/PreservedFormatsMod
import useLinkStore from "@/store/links";
import { toast } from "react-hot-toast";
import useAccountStore from "@/store/account";
import { dropdownTriggerer } from "@/lib/client/utils";
type Props = {
link: LinkIncludingShortenedCollectionAndTags;
@ -71,6 +72,7 @@ export default function LinkActions({
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="btn btn-ghost btn-sm btn-square text-neutral"
>
<i title="More" className="bi-three-dots text-xl" />

View File

@ -9,6 +9,7 @@ import usePermissions from "@/hooks/usePermissions";
import ProfilePhoto from "../ProfilePhoto";
import addMemberToCollection from "@/lib/client/addMemberToCollection";
import Modal from "../Modal";
import { dropdownTriggerer } from "@/lib/client/utils";
type Props = {
onClose: Function;
@ -264,6 +265,7 @@ export default function EditCollectionSharingModal({
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="btn btn-sm btn-primary font-normal"
>
{roleLabel}

View File

@ -13,6 +13,8 @@ import NewLinkModal from "./ModalContent/NewLinkModal";
import NewCollectionModal from "./ModalContent/NewCollectionModal";
import Link from "next/link";
import UploadFileModal from "./ModalContent/UploadFileModal";
import { dropdownTriggerer } from "@/lib/client/utils";
import NewButtonMobile from "./NewButtonMobile";
export default function Navbar() {
const { settings, updateSettings } = useLocalSettingsStore();
@ -35,14 +37,12 @@ export default function Navbar() {
useEffect(() => {
setSidebar(false);
}, [width]);
useEffect(() => {
setSidebar(false);
}, [router]);
document.body.style.overflow = "auto";
}, [width, router]);
const toggleSidebar = () => {
setSidebar(!sidebar);
setSidebar(false);
document.body.style.overflow = "auto";
};
const [newLinkModal, setNewLinkModal] = useState(false);
@ -52,7 +52,10 @@ export default function Navbar() {
return (
<div className="flex justify-between gap-2 items-center pl-3 pr-4 py-2 border-solid border-b-neutral-content border-b">
<div
onClick={toggleSidebar}
onClick={() => {
setSidebar(true);
document.body.style.overflow = "hidden";
}}
className="text-neutral btn btn-square btn-sm btn-ghost lg:hidden"
>
<i className="bi-list text-2xl leading-none"></i>
@ -61,11 +64,12 @@ export default function Navbar() {
<div className="flex items-center gap-2">
<ToggleDarkMode className="hidden sm:inline-grid" />
<div className="dropdown dropdown-end">
<div className="dropdown dropdown-end sm:inline-block hidden">
<div className="tooltip tooltip-bottom" data-tip="Create New...">
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="flex min-w-[3.4rem] items-center btn btn-accent dark:border-violet-400 text-white btn-sm max-h-[2rem] px-2 relative"
>
<span>
@ -117,7 +121,12 @@ export default function Navbar() {
</div>
<div className="dropdown dropdown-end">
<div tabIndex={0} role="button" className="btn btn-circle btn-ghost">
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="btn btn-circle btn-ghost"
>
<ProfilePhoto
src={account.image ? account.image : undefined}
priority={true}
@ -134,7 +143,7 @@ export default function Navbar() {
Settings
</Link>
</li>
<li>
<li className="block sm:hidden">
<div
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
@ -161,6 +170,9 @@ export default function Navbar() {
</ul>
</div>
</div>
<NewButtonMobile />
{sidebar ? (
<div className="fixed top-0 bottom-0 right-0 left-0 bg-black bg-opacity-10 backdrop-blur-sm flex items-center fade-in z-40">
<ClickAwayHandler className="h-full" onClickOutside={toggleSidebar}>

View File

@ -0,0 +1,97 @@
import { dropdownTriggerer } from "@/lib/client/utils";
import React from "react";
import { useEffect, useState } from "react";
import NewLinkModal from "./ModalContent/NewLinkModal";
import NewCollectionModal from "./ModalContent/NewCollectionModal";
import UploadFileModal from "./ModalContent/UploadFileModal";
type Props = {};
export default function NewButtonMobile({}: Props) {
const [hasScrolled, setHasScrolled] = useState(false);
const [newLinkModal, setNewLinkModal] = useState(false);
const [newCollectionModal, setNewCollectionModal] = useState(false);
const [uploadFileModal, setUploadFileModal] = useState(false);
useEffect(() => {
const handleScroll = () => {
if (window.scrollY > 0) {
setHasScrolled(true);
} else {
setHasScrolled(false);
}
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
return (
<>
<div className="dropdown dropdown-end dropdown-top sm:hidden fixed bottom-10 right-10 z-30">
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className={`flex items-center btn btn-accent dark:border-violet-400 text-white btn-circle btn-lg px-2 relative ${
hasScrolled ? "opacity-50" : ""
}`}
>
<span>
<i className="bi-plus text-5xl pointer-events-none"></i>
</span>
</div>
<ul className="dropdown-content z-[1] menu shadow bg-base-200 border border-neutral-content rounded-box w-40 mb-1">
<li>
<div
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
setNewLinkModal(true);
}}
tabIndex={0}
role="button"
>
New Link
</div>
</li>
{/* <li>
<div
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
setUploadFileModal(true);
}}
tabIndex={0}
role="button"
>
Upload File
</div>
</li> */}
<li>
<div
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
setNewCollectionModal(true);
}}
tabIndex={0}
role="button"
>
New Collection
</div>
</li>
</ul>
</div>
{newLinkModal ? (
<NewLinkModal onClose={() => setNewLinkModal(false)} />
) : undefined}
{newCollectionModal ? (
<NewCollectionModal onClose={() => setNewCollectionModal(false)} />
) : undefined}
{uploadFileModal ? (
<UploadFileModal onClose={() => setUploadFileModal(false)} />
) : undefined}
</>
);
}

View File

@ -44,7 +44,7 @@ export default function Sidebar({ className }: { className?: string }) {
return (
<div
id="sidebar"
className={`bg-base-200 h-full w-72 lg:w-80 overflow-y-auto border-solid border border-base-200 border-r-neutral-content p-2 z-20 ${
className={`bg-base-200 h-full w-80 overflow-y-auto border-solid border border-base-200 border-r-neutral-content p-2 z-20 ${
className || ""
}`}
>

View File

@ -1,5 +1,6 @@
import React, { Dispatch, SetStateAction } from "react";
import { Sort } from "@/types/global";
import { dropdownTriggerer } from "@/lib/client/utils";
type Props = {
sortBy: Sort;
@ -12,6 +13,7 @@ export default function SortDropdown({ sortBy, setSort }: Props) {
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="btn btn-sm btn-square btn-ghost"
>
<i className="bi-chevron-expand text-neutral text-2xl"></i>

16
lib/client/utils.ts Normal file
View File

@ -0,0 +1,16 @@
export function isPWA() {
return (
window.matchMedia("(display-mode: standalone)").matches ||
(window.navigator as any).standalone ||
document.referrer.includes("android-app://")
);
}
export function dropdownTriggerer(e: any) {
let targetEl = e.currentTarget;
if (targetEl && targetEl.matches(":focus")) {
setTimeout(function () {
targetEl.blur();
}, 0);
}
}

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
import "@/styles/globals.css";
import "bootstrap-icons/font/bootstrap-icons.css";
import { SessionProvider } from "next-auth/react";
@ -7,6 +7,7 @@ import Head from "next/head";
import AuthRedirect from "@/layouts/AuthRedirect";
import { Toaster } from "react-hot-toast";
import { Session } from "next-auth";
import { isPWA } from "@/lib/client/utils";
export default function App({
Component,
@ -14,6 +15,15 @@ export default function App({
}: AppProps<{
session: Session;
}>) {
useEffect(() => {
if (isPWA()) {
const meta = document.createElement("meta");
meta.name = "viewport";
meta.content = "width=device-width, initial-scale=1, maximum-scale=1";
document.getElementsByTagName("head")[0].appendChild(meta);
}
}, []);
return (
<SessionProvider
session={pageProps.session}

View File

@ -23,6 +23,7 @@ import ViewDropdown from "@/components/ViewDropdown";
import CardView from "@/components/LinkViews/Layouts/CardView";
// import GridView from "@/components/LinkViews/Layouts/GridView";
import ListView from "@/components/LinkViews/Layouts/ListView";
import { dropdownTriggerer } from "@/lib/client/utils";
export default function Index() {
const { settings } = useLocalSettingsStore();
@ -125,6 +126,7 @@ export default function Index() {
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="btn btn-ghost btn-sm btn-square text-neutral"
>
<i className="bi-three-dots text-xl" title="More"></i>

View File

@ -11,7 +11,6 @@ import PageHeader from "@/components/PageHeader";
export default function Collections() {
const { collections } = useCollectionStore();
const [expandDropdown, setExpandDropdown] = useState(false);
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
const [sortedCollections, setSortedCollections] = useState(collections);

View File

@ -16,6 +16,7 @@ import PageHeader from "@/components/PageHeader";
import CardView from "@/components/LinkViews/Layouts/CardView";
import ListView from "@/components/LinkViews/Layouts/ListView";
import ViewDropdown from "@/components/ViewDropdown";
import { dropdownTriggerer } from "@/lib/client/utils";
// import GridView from "@/components/LinkViews/Layouts/GridView";
export default function Dashboard() {
@ -200,6 +201,7 @@ export default function Dashboard() {
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="inline-flex items-center gap-2 text-sm btn btn-outline btn-neutral"
id="import-dropdown"
>

View File

@ -25,8 +25,6 @@ export default function Search() {
tags: true,
});
const [filterDropdown, setFilterDropdown] = useState(false);
const [viewMode, setViewMode] = useState<string>(
localStorage.getItem("viewMode") || ViewMode.Card
);

View File

@ -11,6 +11,7 @@ import React from "react";
import { MigrationFormat, MigrationRequest } from "@/types/global";
import Link from "next/link";
import Checkbox from "@/components/Checkbox";
import { dropdownTriggerer } from "@/lib/client/utils";
export default function Account() {
const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
@ -245,6 +246,7 @@ export default function Account() {
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="flex gap-2 text-sm btn btn-outline btn-neutral group"
id="import-dropdown"
>

View File

@ -11,6 +11,7 @@ import ViewDropdown from "@/components/ViewDropdown";
import CardView from "@/components/LinkViews/Layouts/CardView";
// import GridView from "@/components/LinkViews/Layouts/GridView";
import ListView from "@/components/LinkViews/Layouts/ListView";
import { dropdownTriggerer } from "@/lib/client/utils";
export default function Index() {
const router = useRouter();
@ -153,6 +154,7 @@ export default function Index() {
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="btn btn-ghost btn-sm btn-square text-neutral"
>
<i

View File

@ -1 +1 @@
{"name":"Linkwarden","short_name":"Linkwarden","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
{"name":"Linkwarden","short_name":"Linkwarden","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#000000","background_color":"#000000","display":"standalone"}