This commit is contained in:
Isaac Wise
2024-07-27 17:41:13 -05:00
parent 4faf389a2b
commit 94be3a7448
34 changed files with 308 additions and 256 deletions
+12 -5
View File
@@ -1,5 +1,8 @@
import Link from "next/link";
import { AccountSettings, CollectionIncludingMembersAndLinkCount } from "@/types/global";
import {
AccountSettings,
CollectionIncludingMembersAndLinkCount,
} from "@/types/global";
import React, { useEffect, useState } from "react";
import ProfilePhoto from "./ProfilePhoto";
import usePermissions from "@/hooks/usePermissions";
@@ -33,7 +36,9 @@ export default function CollectionCard({ collection, className }: Props) {
const permissions = usePermissions(collection.id as number);
const [collectionOwner, setCollectionOwner] = useState<Partial<AccountSettings>>({});
const [collectionOwner, setCollectionOwner] = useState<
Partial<AccountSettings>
>({});
useEffect(() => {
const fetchOwner = async () => {
@@ -151,9 +156,11 @@ export default function CollectionCard({ collection, className }: Props) {
<Link
href={`/collections/${collection.id}`}
style={{
backgroundImage: `linear-gradient(45deg, ${collection.color}30 10%, ${settings.theme === "dark" ? "oklch(var(--b2))" : "oklch(var(--b2))"
} 50%, ${settings.theme === "dark" ? "oklch(var(--b2))" : "oklch(var(--b2))"
} 100%)`,
backgroundImage: `linear-gradient(45deg, ${collection.color}30 10%, ${
settings.theme === "dark" ? "oklch(var(--b2))" : "oklch(var(--b2))"
} 50%, ${
settings.theme === "dark" ? "oklch(var(--b2))" : "oklch(var(--b2))"
} 100%)`,
}}
className="card card-compact shadow-md hover:shadow-none duration-200 border border-neutral-content"
>
+5 -4
View File
@@ -231,10 +231,11 @@ const renderItem = (
return (
<div ref={provided.innerRef} {...provided.draggableProps} className="mb-1">
<div
className={`${currentPath === `/collections/${collection.id}`
? "bg-primary/20 is-active"
: "hover:bg-neutral/20"
} duration-100 flex gap-1 items-center pr-2 pl-1 rounded-md`}
className={`${
currentPath === `/collections/${collection.id}`
? "bg-primary/20 is-active"
: "hover:bg-neutral/20"
} duration-100 flex gap-1 items-center pr-2 pl-1 rounded-md`}
>
{Icon(item as ExtendedTreeItem, onExpand, onCollapse)}
+49 -45
View File
@@ -4,15 +4,15 @@ import ClickAwayHandler from "./ClickAwayHandler";
type MenuItem =
| {
name: string;
onClick: MouseEventHandler;
href?: string;
}
name: string;
onClick: MouseEventHandler;
href?: string;
}
| {
name: string;
onClick?: MouseEventHandler;
href: string;
}
name: string;
onClick?: MouseEventHandler;
href: string;
}
| undefined;
type Props = {
@@ -60,46 +60,50 @@ export default function Dropdown({
}
}, [points, dropdownHeight]);
return !points || pos && (
<ClickAwayHandler
onMount={(e) => {
setDropdownHeight(e.height);
setDropdownWidth(e.width);
}}
style={
points
? {
position: "fixed",
top: `${pos?.y}px`,
left: `${pos?.x}px`,
}
: undefined
}
onClickOutside={onClickOutside}
className={`${className || ""
return (
!points ||
(pos && (
<ClickAwayHandler
onMount={(e) => {
setDropdownHeight(e.height);
setDropdownWidth(e.width);
}}
style={
points
? {
position: "fixed",
top: `${pos?.y}px`,
left: `${pos?.x}px`,
}
: undefined
}
onClickOutside={onClickOutside}
className={`${
className || ""
} py-1 shadow-md border border-neutral-content bg-base-200 rounded-md flex flex-col z-20`}
>
{items.map((e, i) => {
const inner = e && (
<div className="cursor-pointer rounded-md">
<div className="flex items-center gap-2 py-1 px-2 hover:bg-base-100 duration-100">
<p className="select-none">{e.name}</p>
>
{items.map((e, i) => {
const inner = e && (
<div className="cursor-pointer rounded-md">
<div className="flex items-center gap-2 py-1 px-2 hover:bg-base-100 duration-100">
<p className="select-none">{e.name}</p>
</div>
</div>
</div>
);
);
return e && e.href ? (
<Link key={i} href={e.href}>
{inner}
</Link>
) : (
e && (
<div key={i} onClick={e.onClick}>
return e && e.href ? (
<Link key={i} href={e.href}>
{inner}
</div>
)
);
})}
</ClickAwayHandler>
</Link>
) : (
e && (
<div key={i} onClick={e.onClick}>
{inner}
</div>
)
);
})}
</ClickAwayHandler>
))
);
}
@@ -10,11 +10,11 @@ type Props = {
onChange: (newValue: unknown, actionMeta: ActionMeta<unknown>) => void;
showDefaultValue?: boolean;
defaultValue?:
| {
label: string;
value?: number;
}
| undefined;
| {
label: string;
value?: number;
}
| undefined;
creatable?: boolean;
};
@@ -107,7 +107,7 @@ export default function CollectionSelection({
components={{
Option: customOption,
}}
// menuPosition="fixed"
// menuPosition="fixed"
/>
);
} else {
@@ -123,7 +123,7 @@ export default function CollectionSelection({
components={{
Option: customOption,
}}
// menuPosition="fixed"
// menuPosition="fixed"
/>
);
}
@@ -79,8 +79,9 @@ export default function LinkActions({
return (
<>
<div
className={`dropdown dropdown-left absolute ${position || "top-3 right-3"
} ${alignToTop ? "" : "dropdown-end"} z-20`}
className={`dropdown dropdown-left absolute ${
position || "top-3 right-3"
} ${alignToTop ? "" : "dropdown-end"} z-20`}
>
<div
tabIndex={0}
@@ -91,8 +92,9 @@ export default function LinkActions({
<i title="More" className="bi-three-dots text-xl" />
</div>
<ul
className={`dropdown-content z-[20] menu shadow bg-base-200 border border-neutral-content rounded-box w-44 mr-1 ${alignToTop ? "" : "translate-y-10"
}`}
className={`dropdown-content z-[20] menu shadow bg-base-200 border border-neutral-content rounded-box w-44 mr-1 ${
alignToTop ? "" : "translate-y-10"
}`}
>
<li>
<div
@@ -122,20 +124,21 @@ export default function LinkActions({
</div>
</li>
)}
{permissions === true || permissions?.canUpdate && (
<li>
<div
role="button"
tabIndex={0}
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
setEditLinkModal(true);
}}
>
{t("edit_link")}
</div>
</li>
)}
{permissions === true ||
(permissions?.canUpdate && (
<li>
<div
role="button"
tabIndex={0}
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
setEditLinkModal(true);
}}
>
{t("edit_link")}
</div>
</li>
))}
{link.type === "url" && (
<li>
<div
@@ -150,20 +153,21 @@ export default function LinkActions({
</div>
</li>
)}
{permissions === true || permissions?.canDelete && (
<li>
<div
role="button"
tabIndex={0}
onClick={(e) => {
(document?.activeElement as HTMLElement)?.blur();
e.shiftKey ? deleteLink() : setDeleteLinkModal(true);
}}
>
{t("delete")}
</div>
</li>
)}
{permissions === true ||
(permissions?.canDelete && (
<li>
<div
role="button"
tabIndex={0}
onClick={(e) => {
(document?.activeElement as HTMLElement)?.blur();
e.shiftKey ? deleteLink() : setDeleteLinkModal(true);
}}
>
{t("delete")}
</div>
</li>
))}
</ul>
</div>
+5 -4
View File
@@ -91,8 +91,9 @@ export default function LinkCardCompact({
return (
<>
<div
className={`${selectedStyle} border relative items-center flex ${!showInfo && !isPWA() ? "hover:bg-base-300 p-3" : "py-3"
} duration-200 rounded-lg w-full`}
className={`${selectedStyle} border relative items-center flex ${
!showInfo && !isPWA() ? "hover:bg-base-300 p-3" : "py-3"
} duration-200 rounded-lg w-full`}
onClick={() =>
selectable
? handleCheckboxClick(link)
@@ -152,8 +153,8 @@ export default function LinkCardCompact({
collection={collection}
position="top-3 right-3"
flipDropdown={flipDropdown}
// toggleShowInfo={() => setShowInfo(!showInfo)}
// linkInfo={showInfo}
// toggleShowInfo={() => setShowInfo(!showInfo)}
// linkInfo={showInfo}
/>
</div>
<div
+5 -6
View File
@@ -9,7 +9,7 @@ import { useTranslation } from "next-i18next";
type Props = {};
export default function MobileNavigation({ }: Props) {
export default function MobileNavigation({}: Props) {
const { t } = useTranslation();
const [newLinkModal, setNewLinkModal] = useState(false);
const [newCollectionModal, setNewCollectionModal] = useState(false);
@@ -21,8 +21,9 @@ export default function MobileNavigation({ }: Props) {
className={`fixed bottom-0 left-0 right-0 z-30 duration-200 sm:hidden`}
>
<div
className={`w-full flex bg-base-100 ${isIphone() && isPWA() ? "pb-5" : ""
} border-solid border-t-neutral-content border-t`}
className={`w-full flex bg-base-100 ${
isIphone() && isPWA() ? "pb-5" : ""
} border-solid border-t-neutral-content border-t`}
>
<MobileNavigationButton href={`/dashboard`} icon={"bi-house"} />
<MobileNavigationButton
@@ -83,9 +84,7 @@ export default function MobileNavigation({ }: Props) {
<MobileNavigationButton href={`/collections`} icon={"bi-folder"} />
</div>
</div>
{newLinkModal && (
<NewLinkModal onClose={() => setNewLinkModal(false)} />
)}
{newLinkModal && <NewLinkModal onClose={() => setNewLinkModal(false)} />}
{newCollectionModal && (
<NewCollectionModal onClose={() => setNewCollectionModal(false)} />
)}
@@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react";
import TextInput from "@/components/TextInput";
import useCollectionStore from "@/store/collections";
import toast from "react-hot-toast";
import { AccountSettings, CollectionIncludingMembersAndLinkCount, Member } from "@/types/global";
import {
AccountSettings,
CollectionIncludingMembersAndLinkCount,
Member,
} from "@/types/global";
import getPublicUserData from "@/lib/client/getPublicUserData";
import useAccountStore from "@/store/account";
import usePermissions from "@/hooks/usePermissions";
@@ -62,7 +66,9 @@ export default function EditCollectionSharingModal({
const [memberUsername, setMemberUsername] = useState("");
const [collectionOwner, setCollectionOwner] = useState<Partial<AccountSettings>>({});
const [collectionOwner, setCollectionOwner] = useState<
Partial<AccountSettings>
>({});
useEffect(() => {
const fetchOwner = async () => {
@@ -38,7 +38,9 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {
let isPublic = router.pathname.startsWith("/public") ? true : undefined;
const [collectionOwner, setCollectionOwner] = useState<Partial<AccountSettings>>({});
const [collectionOwner, setCollectionOwner] = useState<
Partial<AccountSettings>
>({});
useEffect(() => {
const fetchOwner = async () => {
@@ -143,9 +145,9 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {
<p className="text-xl font-thin">{t("preserved_formats")}</p>
<div className="divider mb-2 mt-1"></div>
{screenshotAvailable(link) ||
pdfAvailable(link) ||
readabilityAvailable(link) ||
monolithAvailable(link) ? (
pdfAvailable(link) ||
readabilityAvailable(link) ||
monolithAvailable(link) ? (
<p className="mb-3">{t("available_formats")}</p>
) : (
""
@@ -206,21 +208,25 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {
<p className="text-center text-2xl">{t("preservation_in_queue")}</p>
<p className="text-center text-lg">{t("check_back_later")}</p>
</div>
) : !isReady() && atLeastOneFormatAvailable() && (
<div className={`w-full h-full flex flex-col justify-center p-5`}>
<BeatLoader
color="oklch(var(--p))"
className="mx-auto mb-3"
size={20}
/>
<p className="text-center">{t("there_are_more_formats")}</p>
<p className="text-center text-sm">{t("check_back_later")}</p>
</div>
) : (
!isReady() &&
atLeastOneFormatAvailable() && (
<div className={`w-full h-full flex flex-col justify-center p-5`}>
<BeatLoader
color="oklch(var(--p))"
className="mx-auto mb-3"
size={20}
/>
<p className="text-center">{t("there_are_more_formats")}</p>
<p className="text-center text-sm">{t("check_back_later")}</p>
</div>
)
)}
<div
className={`flex flex-col sm:flex-row gap-3 items-center justify-center ${isReady() ? "sm:mt " : ""
}`}
className={`flex flex-col sm:flex-row gap-3 items-center justify-center ${
isReady() ? "sm:mt " : ""
}`}
>
<Link
href={`https://web.archive.org/web/${link?.url?.replace(
+1 -3
View File
@@ -120,9 +120,7 @@ export default function Navbar() {
</ClickAwayHandler>
</div>
)}
{newLinkModal && (
<NewLinkModal onClose={() => setNewLinkModal(false)} />
)}
{newLinkModal && <NewLinkModal onClose={() => setNewLinkModal(false)} />}
{newCollectionModal && (
<NewCollectionModal onClose={() => setNewCollectionModal(false)} />
)}
+1 -3
View File
@@ -39,9 +39,7 @@ export default function NoLinksFound({ text }: Props) {
</span>
</div>
</div>
{newLinkModal && (
<NewLinkModal onClose={() => setNewLinkModal(false)} />
)}
{newLinkModal && <NewLinkModal onClose={() => setNewLinkModal(false)} />}
</div>
);
}
+12 -10
View File
@@ -90,18 +90,20 @@ export default function PreservedFormatRow({
</div>
<div className="flex gap-1">
{downloadable || false && (
<div
onClick={() => handleDownload()}
className="btn btn-sm btn-square"
>
<i className="bi-cloud-arrow-down text-xl text-neutral" />
</div>
)}
{downloadable ||
(false && (
<div
onClick={() => handleDownload()}
className="btn btn-sm btn-square"
>
<i className="bi-cloud-arrow-down text-xl text-neutral" />
</div>
))}
<Link
href={`${isPublic ? "/public" : ""
}/preserved/${link?.id}?format=${format}`}
href={`${
isPublic ? "/public" : ""
}/preserved/${link?.id}?format=${format}`}
target="_blank"
className="btn btn-sm btn-square"
>
+3 -2
View File
@@ -32,8 +32,9 @@ export default function ProfileDropdown() {
/>
</div>
<ul
className={`dropdown-content z-[1] menu shadow bg-base-200 border border-neutral-content rounded-box ${isAdmin ? "w-48" : "w-40"
} mt-1`}
className={`dropdown-content z-[1] menu shadow bg-base-200 border border-neutral-content rounded-box ${
isAdmin ? "w-48" : "w-40"
} mt-1`}
>
<li>
<Link
+9 -7
View File
@@ -192,7 +192,8 @@ export default function ReadableView({ link }: Props) {
>
<i className="bi-link-45deg"></i>
{isValidUrl(link?.url || "") && new URL(link?.url as string).host}
{isValidUrl(link?.url || "") &&
new URL(link?.url as string).host}
</Link>
)}
</div>
@@ -229,10 +230,10 @@ export default function ReadableView({ link }: Props) {
<p className="min-w-fit text-sm text-neutral">
{date
? new Date(date).toLocaleString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})
year: "numeric",
month: "long",
day: "numeric",
})
: undefined}
</p>
@@ -257,8 +258,9 @@ export default function ReadableView({ link }: Props) {
></div>
) : (
<div
className={`w-full h-full flex flex-col justify-center p-10 ${link?.readable === "pending" || !link?.readable ? "skeleton" : ""
}`}
className={`w-full h-full flex flex-col justify-center p-10 ${
link?.readable === "pending" || !link?.readable ? "skeleton" : ""
}`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
+3 -1
View File
@@ -10,7 +10,9 @@ export default function ToggleDarkMode({ className }: Props) {
const { t } = useTranslation();
const { settings, updateSettings } = useLocalSettingsStore();
const [theme, setTheme] = useState<string | null>(localStorage.getItem("theme"));
const [theme, setTheme] = useState<string | null>(
localStorage.getItem("theme")
);
const handleToggle = (e: ChangeEvent<HTMLInputElement>) => {
setTheme(e.target.checked ? "dark" : "light");