Merge pull request #678 from IsaacWise06/fixes

General Fixes
This commit is contained in:
Daniel
2024-08-18 16:40:48 -04:00
committed by GitHub
53 changed files with 420 additions and 445 deletions
+23 -27
View File
@@ -1,5 +1,8 @@
import Link from "next/link";
import { 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";
@@ -12,12 +15,11 @@ import { dropdownTriggerer } from "@/lib/client/utils";
import { useTranslation } from "next-i18next";
import { useUser } from "@/hooks/store/user";
type Props = {
export default function CollectionCard({
collection,
}: {
collection: CollectionIncludingMembersAndLinkCount;
className?: string;
};
export default function CollectionCard({ collection, className }: Props) {
}) {
const { t } = useTranslation();
const { settings } = useLocalSettingsStore();
const { data: user = {} } = useUser();
@@ -33,15 +35,9 @@ export default function CollectionCard({ collection, className }: Props) {
const permissions = usePermissions(collection.id as number);
const [collectionOwner, setCollectionOwner] = useState({
id: null as unknown as number,
name: "",
username: "",
image: "",
archiveAsScreenshot: undefined as unknown as boolean,
archiveAsMonolith: undefined as unknown as boolean,
archiveAsPDF: undefined as unknown as boolean,
});
const [collectionOwner, setCollectionOwner] = useState<
Partial<AccountSettings>
>({});
useEffect(() => {
const fetchOwner = async () => {
@@ -132,12 +128,12 @@ export default function CollectionCard({ collection, className }: Props) {
className="flex items-center absolute bottom-3 left-3 z-10 btn px-2 btn-ghost rounded-full"
onClick={() => setEditCollectionSharingModal(true)}
>
{collectionOwner.id ? (
{collectionOwner.id && (
<ProfilePhoto
src={collectionOwner.image || undefined}
name={collectionOwner.name}
/>
) : undefined}
)}
{collection.members
.sort((a, b) => (a.userId as number) - (b.userId as number))
.map((e, i) => {
@@ -151,13 +147,13 @@ export default function CollectionCard({ collection, className }: Props) {
);
})
.slice(0, 3)}
{collection.members.length - 3 > 0 ? (
{collection.members.length - 3 > 0 && (
<div className={`avatar drop-shadow-md placeholder -ml-3`}>
<div className="bg-base-100 text-neutral rounded-full w-8 h-8 ring-2 ring-neutral-content">
<span>+{collection.members.length - 3}</span>
</div>
</div>
) : null}
)}
</div>
<Link
href={`/collections/${collection.id}`}
@@ -181,12 +177,12 @@ export default function CollectionCard({ collection, className }: Props) {
<div className="flex justify-end items-center">
<div className="text-right">
<div className="font-bold text-sm flex justify-end gap-1 items-center">
{collection.isPublic ? (
{collection.isPublic && (
<i
className="bi-globe2 drop-shadow text-neutral"
title="This collection is being shared publicly."
></i>
) : undefined}
)}
<i
className="bi-link-45deg text-lg text-neutral"
title="This collection is being shared publicly."
@@ -206,24 +202,24 @@ export default function CollectionCard({ collection, className }: Props) {
</div>
</div>
</Link>
{editCollectionModal ? (
{editCollectionModal && (
<EditCollectionModal
onClose={() => setEditCollectionModal(false)}
activeCollection={collection}
/>
) : undefined}
{editCollectionSharingModal ? (
)}
{editCollectionSharingModal && (
<EditCollectionSharingModal
onClose={() => setEditCollectionSharingModal(false)}
activeCollection={collection}
/>
) : undefined}
{deleteCollectionModal ? (
)}
{deleteCollectionModal && (
<DeleteCollectionModal
onClose={() => setDeleteCollectionModal(false)}
activeCollection={collection}
/>
) : undefined}
)}
</div>
);
}
+2 -2
View File
@@ -272,12 +272,12 @@ const renderItem = (
></i>
<p className="truncate w-full">{collection.name}</p>
{collection.isPublic ? (
{collection.isPublic && (
<i
className="bi-globe2 text-sm text-black/50 dark:text-white/50 drop-shadow"
title="This collection is being shared publicly."
></i>
) : undefined}
)}
<div className="drop-shadow text-neutral text-xs">
{collection._count?.links}
</div>
+42 -40
View File
@@ -60,47 +60,49 @@ 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 || ""
} 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>
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>
</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>
) : null;
</Link>
) : (
e && (
<div key={i} onClick={e.onClick}>
{inner}
</div>
)
);
})}
</ClickAwayHandler>
)
);
}
-1
View File
@@ -34,7 +34,6 @@ export default function TagSelection({ onChange, defaultValue }: Props) {
options={options}
styles={styles}
defaultValue={defaultValue}
// menuPosition="fixed"
isMulti
/>
);
@@ -136,7 +136,7 @@ export default function LinkActions({
{t("show_link_details")}
</div>
</li>
{permissions === true || permissions?.canUpdate ? (
{(permissions === true || permissions?.canUpdate) && (
<li>
<div
role="button"
@@ -150,7 +150,7 @@ export default function LinkActions({
{t("edit_link")}
</div>
</li>
) : undefined}
)}
{link.type === "url" && (
<li>
<div
@@ -166,7 +166,7 @@ export default function LinkActions({
</div>
</li>
)}
{permissions === true || permissions?.canDelete ? (
{(permissions === true || permissions?.canDelete) && (
<li>
<div
role="button"
@@ -196,36 +196,35 @@ export default function LinkActions({
{t("delete")}
</div>
</li>
) : undefined}
)}
</ul>
</div>
)}
{editLinkModal ? (
{editLinkModal && (
<EditLinkModal
onClose={() => setEditLinkModal(false)}
activeLink={link}
/>
) : undefined}
{deleteLinkModal ? (
)}
{deleteLinkModal && (
<DeleteLinkModal
onClose={() => setDeleteLinkModal(false)}
activeLink={link}
/>
) : undefined}
{preservedFormatsModal ? (
)}
{preservedFormatsModal && (
<PreservedFormatsModal
onClose={() => setPreservedFormatsModal(false)}
link={link}
/>
) : undefined}
{linkDetailModal ? (
)}
{linkDetailModal && (
<LinkDetailModal
onClose={() => setLinkDetailModal(false)}
onEdit={() => setEditLinkModal(true)}
link={link}
/>
) : undefined}
)}
</>
);
}
@@ -96,7 +96,7 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
let isPublic = router.pathname.startsWith("/public") ? true : undefined;
useEffect(() => {
let interval: any;
let interval: NodeJS.Timeout | null = null;
if (
isVisible &&
@@ -127,9 +127,9 @@ export default function LinkCardCompact({
<div className="mt-1 flex flex-col sm:flex-row sm:items-center gap-2 text-xs text-neutral">
<div className="flex items-center gap-x-3 text-neutral flex-wrap">
{collection ? (
{collection && (
<LinkCollection link={link} collection={collection} />
) : undefined}
)}
{link.name && <LinkTypeBadge link={link} />}
<LinkDate link={link} />
</div>
@@ -88,7 +88,7 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
const permissions = usePermissions(collection?.id as number);
useEffect(() => {
let interval: any;
let interval: NodeJS.Timeout | null = null;
if (
isVisible &&
+5 -7
View File
@@ -87,15 +87,13 @@ export default function MobileNavigation({}: Props) {
<MobileNavigationButton href={`/collections`} icon={"bi-folder"} />
</div>
</div>
{newLinkModal ? (
<NewLinkModal onClose={() => setNewLinkModal(false)} />
) : undefined}
{newCollectionModal ? (
{newLinkModal && <NewLinkModal onClose={() => setNewLinkModal(false)} />}
{newCollectionModal && (
<NewCollectionModal onClose={() => setNewCollectionModal(false)} />
) : undefined}
{uploadFileModal ? (
)}
{uploadFileModal && (
<UploadFileModal onClose={() => setUploadFileModal(false)} />
) : undefined}
)}
</>
);
}
@@ -1,7 +1,11 @@
import React, { useEffect, useState } from "react";
import TextInput from "@/components/TextInput";
import toast from "react-hot-toast";
import { CollectionIncludingMembersAndLinkCount, Member } from "@/types/global";
import {
AccountSettings,
CollectionIncludingMembersAndLinkCount,
Member,
} from "@/types/global";
import getPublicUserData from "@/lib/client/getPublicUserData";
import usePermissions from "@/hooks/usePermissions";
import ProfilePhoto from "../ProfilePhoto";
@@ -65,15 +69,9 @@ export default function EditCollectionSharingModal({
const [memberUsername, setMemberUsername] = useState("");
const [collectionOwner, setCollectionOwner] = useState({
id: null as unknown as number,
name: "",
username: "",
image: "",
archiveAsScreenshot: undefined as unknown as boolean,
archiveAsMonolith: undefined as unknown as boolean,
archiveAsPDF: undefined as unknown as boolean,
});
const [collectionOwner, setCollectionOwner] = useState<
Partial<AccountSettings>
>({});
useEffect(() => {
const fetchOwner = async () => {
@@ -133,7 +131,7 @@ export default function EditCollectionSharingModal({
</div>
)}
{collection.isPublic ? (
{collection.isPublic && (
<div>
<p className="mb-2">{t("sharable_link_guide")}</p>
<div className="w-full hide-scrollbar overflow-x-auto whitespace-nowrap rounded-md p-2 bg-base-200 border-neutral-content border-solid border flex items-center gap-2 justify-between">
@@ -141,7 +139,7 @@ export default function EditCollectionSharingModal({
<CopyButton text={publicCollectionURL} />
</div>
</div>
) : null}
)}
{permissions === true && <div className="divider my-3"></div>}
+4 -4
View File
@@ -77,7 +77,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
<div className="divider mb-3 mt-1"></div>
{link.url ? (
{link.url && (
<Link
href={link.url}
className="truncate text-neutral flex gap-2 mb-5 w-fit max-w-full"
@@ -87,7 +87,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
<i className="bi-link-45deg text-xl" />
<p>{shortenedURL}</p>
</Link>
) : undefined}
)}
<div className="w-full">
<p className="mb-2">{t("name")}</p>
@@ -103,7 +103,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
<div className="grid sm:grid-cols-2 gap-3">
<div>
<p className="mb-2">{t("collection")}</p>
{link.collection.name ? (
{link.collection.name && (
<CollectionSelection
onChange={setCollection}
defaultValue={
@@ -113,7 +113,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
}
creatable={false}
/>
) : null}
)}
</div>
<div>
+4 -4
View File
@@ -124,7 +124,7 @@ export default function NewLinkModal({ onClose }: Props) {
</div>
<div className="sm:col-span-2 col-span-5">
<p className="mb-2">{t("collection")}</p>
{link.collection.name ? (
{link.collection.name && (
<CollectionSelection
onChange={setCollection}
defaultValue={{
@@ -132,11 +132,11 @@ export default function NewLinkModal({ onClose }: Props) {
value: link.collection.id,
}}
/>
) : null}
)}
</div>
</div>
<div className={"mt-2"}>
{optionsExpanded ? (
{optionsExpanded && (
<div className="mt-5">
<div className="grid sm:grid-cols-2 gap-3">
<div>
@@ -171,7 +171,7 @@ export default function NewLinkModal({ onClose }: Props) {
</div>
</div>
</div>
) : undefined}
)}
</div>
<div className="flex justify-between items-center mt-5">
<div
+2 -2
View File
@@ -79,7 +79,7 @@ export default function NewUserModal({ onClose }: Props) {
/>
</div>
{emailEnabled ? (
{emailEnabled && (
<div>
<p className="mb-2">{t("email")}</p>
<TextInput
@@ -89,7 +89,7 @@ export default function NewUserModal({ onClose }: Props) {
value={form.email}
/>
</div>
) : undefined}
)}
<div>
<p className="mb-2">
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import {
LinkIncludingShortenedCollectionAndTags,
ArchivedFormat,
AccountSettings,
} from "@/types/global";
import toast from "react-hot-toast";
import Link from "next/link";
@@ -35,15 +36,9 @@ export default function PreservedFormatsModal({ onClose, link }: Props) {
let isPublic = router.pathname.startsWith("/public") ? true : undefined;
const [collectionOwner, setCollectionOwner] = useState({
id: null as unknown as number,
name: "",
username: "",
image: "",
archiveAsScreenshot: undefined as unknown as boolean,
archiveAsMonolith: undefined as unknown as boolean,
archiveAsPDF: undefined as unknown as boolean,
});
const [collectionOwner, setCollectionOwner] = useState<
Partial<AccountSettings>
>({});
useEffect(() => {
const fetchOwner = async () => {
@@ -99,7 +94,7 @@ export default function PreservedFormatsModal({ onClose, link }: Props) {
await getLink.mutateAsync({ id: link.id as number });
})();
let interval: any;
let interval: NodeJS.Timeout | null = null;
if (!isReady()) {
interval = setInterval(async () => {
@@ -149,7 +144,7 @@ export default function PreservedFormatsModal({ onClose, link }: Props) {
)}
<div className={`flex flex-col gap-3`}>
{monolithAvailable(link) ? (
{monolithAvailable(link) && (
<PreservedFormatRow
name={t("webpage")}
icon={"bi-filetype-html"}
@@ -157,9 +152,9 @@ export default function PreservedFormatsModal({ onClose, link }: Props) {
link={link}
downloadable={true}
/>
) : undefined}
)}
{screenshotAvailable(link) ? (
{screenshotAvailable(link) && (
<PreservedFormatRow
name={t("screenshot")}
icon={"bi-file-earmark-image"}
@@ -171,9 +166,9 @@ export default function PreservedFormatsModal({ onClose, link }: Props) {
link={link}
downloadable={true}
/>
) : undefined}
)}
{pdfAvailable(link) ? (
{pdfAvailable(link) && (
<PreservedFormatRow
name={t("pdf")}
icon={"bi-file-earmark-pdf"}
@@ -181,16 +176,16 @@ export default function PreservedFormatsModal({ onClose, link }: Props) {
link={link}
downloadable={true}
/>
) : undefined}
)}
{readabilityAvailable(link) ? (
{readabilityAvailable(link) && (
<PreservedFormatRow
name={t("readable")}
icon={"bi-file-earmark-text"}
format={ArchivedFormat.readability}
link={link}
/>
) : undefined}
)}
{!isReady() && !atLeastOneFormatAvailable() ? (
<div className={`w-full h-full flex flex-col justify-center p-10`}>
@@ -203,17 +198,20 @@ export default function PreservedFormatsModal({ onClose, link }: 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>
) : undefined}
) : (
!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 ${
+5 -5
View File
@@ -150,7 +150,7 @@ export default function UploadFileModal({ onClose }: Props) {
<label className="btn h-10 btn-sm w-full border border-neutral-content hover:border-neutral-content flex justify-between">
<input
type="file"
accept=".pdf,.png,.jpg,.jpeg,.html"
accept=".pdf,.png,.jpg,.jpeg"
className="cursor-pointer custom-file-input"
onChange={(e) => e.target.files && setFile(e.target.files[0])}
/>
@@ -163,7 +163,7 @@ export default function UploadFileModal({ onClose }: Props) {
</div>
<div className="sm:col-span-2 col-span-5">
<p className="mb-2">{t("collection")}</p>
{link.collection.name ? (
{link.collection.name && (
<CollectionSelection
onChange={setCollection}
defaultValue={{
@@ -171,10 +171,10 @@ export default function UploadFileModal({ onClose }: Props) {
value: link.collection.id,
}}
/>
) : null}
)}
</div>
</div>
{optionsExpanded ? (
{optionsExpanded && (
<div className="mt-5">
<div className="grid sm:grid-cols-2 gap-3">
<div>
@@ -209,7 +209,7 @@ export default function UploadFileModal({ onClose }: Props) {
</div>
</div>
</div>
) : undefined}
)}
<div className="flex justify-between items-center mt-5">
<div
onClick={() => setOptionsExpanded(!optionsExpanded)}
+7 -9
View File
@@ -114,7 +114,7 @@ export default function Navbar() {
<MobileNavigation />
{sidebar ? (
{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}>
<div className="slide-right h-full shadow-lg">
@@ -122,16 +122,14 @@ export default function Navbar() {
</div>
</ClickAwayHandler>
</div>
) : null}
{newLinkModal ? (
<NewLinkModal onClose={() => setNewLinkModal(false)} />
) : undefined}
{newCollectionModal ? (
)}
{newLinkModal && <NewLinkModal onClose={() => setNewLinkModal(false)} />}
{newCollectionModal && (
<NewCollectionModal onClose={() => setNewCollectionModal(false)} />
) : undefined}
{uploadFileModal ? (
)}
{uploadFileModal && (
<UploadFileModal onClose={() => setUploadFileModal(false)} />
) : undefined}
)}
</div>
);
}
+1 -3
View File
@@ -39,9 +39,7 @@ export default function NoLinksFound({ text }: Props) {
</span>
</div>
</div>
{newLinkModal ? (
<NewLinkModal onClose={() => setNewLinkModal(false)} />
) : undefined}
{newLinkModal && <NewLinkModal onClose={() => setNewLinkModal(false)} />}
</div>
);
}
-3
View File
@@ -4,7 +4,6 @@ import {
} from "@/types/global";
import Link from "next/link";
import { useRouter } from "next/router";
import { useGetLink } from "@/hooks/store/links";
type Props = {
name: string;
@@ -21,8 +20,6 @@ export default function PreservedFormatRow({
link,
downloadable,
}: Props) {
const getLink = useGetLink();
const router = useRouter();
let isPublic = router.pathname.startsWith("/public") ? true : undefined;
+2 -2
View File
@@ -60,7 +60,7 @@ export default function ProfileDropdown() {
})}
</div>
</li>
{isAdmin ? (
{isAdmin && (
<li>
<Link
href="/admin"
@@ -72,7 +72,7 @@ export default function ProfileDropdown() {
{t("server_administration")}
</Link>
</li>
) : null}
)}
<li>
<div
onClick={() => {
+5 -6
View File
@@ -68,7 +68,7 @@ export default function ReadableView({ link }: Props) {
useEffect(() => {
if (link) getLink.mutateAsync({ id: link.id as number });
let interval: any;
let interval: NodeJS.Timeout | null = null;
if (
link &&
(link?.image === "pending" ||
@@ -182,7 +182,7 @@ export default function ReadableView({ link }: Props) {
link?.name || link?.description || link?.url || ""
)}
</p>
{link?.url ? (
{link?.url && (
<Link
href={link?.url || ""}
title={link?.url}
@@ -191,11 +191,10 @@ export default function ReadableView({ link }: Props) {
>
<i className="bi-link-45deg"></i>
{isValidUrl(link?.url || "")
? new URL(link?.url as string).host
: undefined}
{isValidUrl(link?.url || "") &&
new URL(link?.url as string).host}
</Link>
) : undefined}
)}
</div>
</div>
+9 -5
View File
@@ -1,5 +1,5 @@
import useLocalSettingsStore from "@/store/localSettings";
import { useEffect, useState } from "react";
import { useEffect, useState, ChangeEvent } from "react";
import { useTranslation } from "next-i18next";
type Props = {
@@ -10,14 +10,18 @@ export default function ToggleDarkMode({ className }: Props) {
const { t } = useTranslation();
const { settings, updateSettings } = useLocalSettingsStore();
const [theme, setTheme] = useState(localStorage.getItem("theme"));
const [theme, setTheme] = useState<string | null>(
localStorage.getItem("theme")
);
const handleToggle = (e: any) => {
const handleToggle = (e: ChangeEvent<HTMLInputElement>) => {
setTheme(e.target.checked ? "dark" : "light");
};
useEffect(() => {
updateSettings({ theme: theme as string });
if (theme) {
updateSettings({ theme });
}
}, [theme]);
return (
@@ -34,7 +38,7 @@ export default function ToggleDarkMode({ className }: Props) {
type="checkbox"
onChange={handleToggle}
className="theme-controller"
checked={localStorage.getItem("theme") === "light" ? false : true}
checked={theme === "dark"}
/>
<i className="bi-sun-fill text-xl swap-on"></i>
<i className="bi-moon-fill text-xl swap-off"></i>
+2 -2
View File
@@ -74,12 +74,12 @@ const UserListing = (
</tbody>
</table>
{deleteUserModal.isOpen && deleteUserModal.userId ? (
{deleteUserModal.isOpen && deleteUserModal.userId && (
<DeleteUserModal
onClose={() => setDeleteUserModal({ isOpen: false, userId: null })}
userId={deleteUserModal.userId}
/>
) : null}
)}
</div>
);
};