From e79b98d3b000eda1abc6eec18fe0a4348f6df357 Mon Sep 17 00:00:00 2001 From: Isaac Wise Date: Mon, 22 Jul 2024 22:34:36 -0500 Subject: [PATCH 01/58] Replace useless ternarys with logical ANDs --- components/CollectionCard.tsx | 28 +++++------ components/CollectionListing.tsx | 9 ++-- .../LinkViews/LinkComponents/LinkActions.tsx | 34 ++++++------- components/LinkViews/LinkList.tsx | 13 +++-- components/MobileNavigation.tsx | 19 ++++--- components/ModalContent/EditLinkModal.tsx | 4 +- components/ModalContent/NewLinkModal.tsx | 4 +- components/ModalContent/NewUserModal.tsx | 4 +- .../ModalContent/PreservedFormatsModal.tsx | 31 ++++++------ components/ModalContent/UploadFileModal.tsx | 8 +-- components/Navbar.tsx | 12 ++--- components/NoLinksFound.tsx | 4 +- components/PreserverdFormatRow.tsx | 9 ++-- components/ReadableView.tsx | 21 ++++---- layouts/CenteredForm.tsx | 13 +++-- layouts/MainLayout.tsx | 4 +- pages/collections/[id].tsx | 43 ++++++++-------- pages/collections/index.tsx | 8 +-- pages/dashboard.tsx | 4 +- pages/login.tsx | 10 ++-- pages/public/collections/[id].tsx | 49 +++++++++---------- pages/register.tsx | 26 +++++----- pages/settings/access-tokens.tsx | 8 +-- pages/settings/account.tsx | 38 +++++++------- pages/settings/delete.tsx | 4 +- 25 files changed, 195 insertions(+), 212 deletions(-) diff --git a/components/CollectionCard.tsx b/components/CollectionCard.tsx index da634cc..cd7d80e 100644 --- a/components/CollectionCard.tsx +++ b/components/CollectionCard.tsx @@ -129,12 +129,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 && ( - ) : undefined} + )} {collection.members .sort((a, b) => (a.userId as number) - (b.userId as number)) .map((e, i) => { @@ -159,11 +159,9 @@ export default function CollectionCard({ collection, className }: Props) { @@ -178,12 +176,12 @@ export default function CollectionCard({ collection, className }: Props) {
- {collection.isPublic ? ( + {collection.isPublic && ( - ) : undefined} + )}
- {editCollectionModal ? ( + {editCollectionModal && ( setEditCollectionModal(false)} activeCollection={collection} /> - ) : undefined} - {editCollectionSharingModal ? ( + )} + {editCollectionSharingModal && ( setEditCollectionSharingModal(false)} activeCollection={collection} /> - ) : undefined} - {deleteCollectionModal ? ( + )} + {deleteCollectionModal && ( setDeleteCollectionModal(false)} activeCollection={collection} /> - ) : undefined} + )}
); } diff --git a/components/CollectionListing.tsx b/components/CollectionListing.tsx index fbe2f7b..b26e8cf 100644 --- a/components/CollectionListing.tsx +++ b/components/CollectionListing.tsx @@ -231,11 +231,10 @@ const renderItem = ( return (
{Icon(item as ExtendedTreeItem, onExpand, onCollapse)} @@ -253,12 +252,12 @@ const renderItem = ( >

{collection.name}

- {collection.isPublic ? ( + {collection.isPublic && ( - ) : undefined} + )}
{collection._count?.links}
diff --git a/components/LinkViews/LinkComponents/LinkActions.tsx b/components/LinkViews/LinkComponents/LinkActions.tsx index c68dd06..41f40c5 100644 --- a/components/LinkViews/LinkComponents/LinkActions.tsx +++ b/components/LinkViews/LinkComponents/LinkActions.tsx @@ -79,9 +79,8 @@ export default function LinkActions({ return ( <>
  • - {linkInfo !== undefined && toggleShowInfo ? ( + {linkInfo !== undefined && toggleShowInfo && (
  • - ) : undefined} - {permissions === true || permissions?.canUpdate ? ( + )} + {permissions === true || permissions?.canUpdate && (
  • - ) : undefined} + )} {link.type === "url" && (
  • )} - {permissions === true || permissions?.canDelete ? ( + {permissions === true || permissions?.canDelete && (
  • - ) : undefined} + )}
- {editLinkModal ? ( + {editLinkModal && ( setEditLinkModal(false)} activeLink={link} /> - ) : undefined} - {deleteLinkModal ? ( + )} + {deleteLinkModal && ( setDeleteLinkModal(false)} activeLink={link} /> - ) : undefined} - {preservedFormatsModal ? ( + )} + {preservedFormatsModal && ( setPreservedFormatsModal(false)} activeLink={link} /> - ) : undefined} + )} {/* {expandedLink ? ( setExpandedLink(false)} link={link} /> ) : undefined} */} diff --git a/components/LinkViews/LinkList.tsx b/components/LinkViews/LinkList.tsx index 723c47e..efb10c4 100644 --- a/components/LinkViews/LinkList.tsx +++ b/components/LinkViews/LinkList.tsx @@ -91,9 +91,8 @@ export default function LinkCardCompact({ return ( <>
selectable ? handleCheckboxClick(link) @@ -139,9 +138,9 @@ export default function LinkCardCompact({
- {collection ? ( + {collection && ( - ) : undefined} + )} {link.name && }
@@ -153,8 +152,8 @@ export default function LinkCardCompact({ collection={collection} position="top-3 right-3" flipDropdown={flipDropdown} - // toggleShowInfo={() => setShowInfo(!showInfo)} - // linkInfo={showInfo} + // toggleShowInfo={() => setShowInfo(!showInfo)} + // linkInfo={showInfo} />
- {newLinkModal ? ( + {newLinkModal && ( setNewLinkModal(false)} /> - ) : undefined} - {newCollectionModal ? ( + )} + {newCollectionModal && ( setNewCollectionModal(false)} /> - ) : undefined} - {uploadFileModal ? ( + )} + {uploadFileModal && ( setUploadFileModal(false)} /> - ) : undefined} + )} ); } diff --git a/components/ModalContent/EditLinkModal.tsx b/components/ModalContent/EditLinkModal.tsx index 1d873c7..b509aa8 100644 --- a/components/ModalContent/EditLinkModal.tsx +++ b/components/ModalContent/EditLinkModal.tsx @@ -72,7 +72,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
- {link.url ? ( + {link.url && (

{shortenedURL}

- ) : undefined} + )}

{t("name")}

diff --git a/components/ModalContent/NewLinkModal.tsx b/components/ModalContent/NewLinkModal.tsx index 0284687..680f9ef 100644 --- a/components/ModalContent/NewLinkModal.tsx +++ b/components/ModalContent/NewLinkModal.tsx @@ -128,7 +128,7 @@ export default function NewLinkModal({ onClose }: Props) {
- {optionsExpanded ? ( + {optionsExpanded && (
@@ -163,7 +163,7 @@ export default function NewLinkModal({ onClose }: Props) {
- ) : undefined} + )}
- {emailEnabled ? ( + {emailEnabled && (

{t("email")}

- ) : undefined} + )}

diff --git a/components/ModalContent/PreservedFormatsModal.tsx b/components/ModalContent/PreservedFormatsModal.tsx index 87e84a7..4414b37 100644 --- a/components/ModalContent/PreservedFormatsModal.tsx +++ b/components/ModalContent/PreservedFormatsModal.tsx @@ -150,16 +150,16 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {

{t("preserved_formats")}

{screenshotAvailable(link) || - pdfAvailable(link) || - readabilityAvailable(link) || - monolithAvailable(link) ? ( + pdfAvailable(link) || + readabilityAvailable(link) || + monolithAvailable(link) ? (

{t("available_formats")}

) : ( "" )}
- {monolithAvailable(link) ? ( + {monolithAvailable(link) && ( - ) : undefined} + )} - {screenshotAvailable(link) ? ( + {screenshotAvailable(link) && ( - ) : undefined} + )} - {pdfAvailable(link) ? ( + {pdfAvailable(link) && ( - ) : undefined} + )} - {readabilityAvailable(link) ? ( + {readabilityAvailable(link) && ( - ) : undefined} + )} {!isReady() && !atLeastOneFormatAvailable() ? (
@@ -213,7 +213,7 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {

{t("preservation_in_queue")}

{t("check_back_later")}

- ) : !isReady() && atLeastOneFormatAvailable() ? ( + ) : !isReady() && atLeastOneFormatAvailable() && (
{t("there_are_more_formats")}

{t("check_back_later")}

- ) : undefined} + )}

{t("collection")}

- {link.collection.name ? ( + {link.collection.name && ( - ) : null} + )}
- {optionsExpanded ? ( + {optionsExpanded && (
@@ -203,7 +203,7 @@ export default function UploadFileModal({ onClose }: Props) {
- ) : undefined} + )}
setOptionsExpanded(!optionsExpanded)} diff --git a/components/Navbar.tsx b/components/Navbar.tsx index dcc78c5..888c56b 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -120,15 +120,15 @@ export default function Navbar() {
) : null} - {newLinkModal ? ( + {newLinkModal && ( setNewLinkModal(false)} /> - ) : undefined} - {newCollectionModal ? ( + )} + {newCollectionModal && ( setNewCollectionModal(false)} /> - ) : undefined} - {uploadFileModal ? ( + )} + {uploadFileModal && ( setUploadFileModal(false)} /> - ) : undefined} + )}
); } diff --git a/components/NoLinksFound.tsx b/components/NoLinksFound.tsx index 516dfb6..b42516a 100644 --- a/components/NoLinksFound.tsx +++ b/components/NoLinksFound.tsx @@ -39,9 +39,9 @@ export default function NoLinksFound({ text }: Props) {
- {newLinkModal ? ( + {newLinkModal && ( setNewLinkModal(false)} /> - ) : undefined} + )}
); } diff --git a/components/PreserverdFormatRow.tsx b/components/PreserverdFormatRow.tsx index ce0f21d..e28d43d 100644 --- a/components/PreserverdFormatRow.tsx +++ b/components/PreserverdFormatRow.tsx @@ -97,19 +97,18 @@ export default function PreservedFormatRow({
- {downloadable || false ? ( + {downloadable || false && (
handleDownload()} className="btn btn-sm btn-square" >
- ) : undefined} + )} diff --git a/components/ReadableView.tsx b/components/ReadableView.tsx index b600236..b862b38 100644 --- a/components/ReadableView.tsx +++ b/components/ReadableView.tsx @@ -183,7 +183,7 @@ export default function ReadableView({ link }: Props) { link?.name || link?.description || link?.url || "" )}

- {link?.url ? ( + {link?.url && ( - {isValidUrl(link?.url || "") - ? new URL(link?.url as string).host - : undefined} + {isValidUrl(link?.url || "") && new URL(link?.url as string).host} - ) : undefined} + )}
@@ -231,10 +229,10 @@ export default function ReadableView({ link }: Props) {

{date ? new Date(date).toLocaleString("en-US", { - year: "numeric", - month: "long", - day: "numeric", - }) + year: "numeric", + month: "long", + day: "numeric", + }) : undefined}

@@ -259,9 +257,8 @@ export default function ReadableView({ link }: Props) { >
) : (
- {settings.theme ? ( + {settings.theme && ( Linkwarden - ) : undefined} - {text ? ( + )} + {text && (

{text}

- ) : undefined} + )} {children}

- {showAnnouncement ? ( + {showAnnouncement && ( - ) : undefined} + )}

diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index bd24a95..b50d6df 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -115,9 +115,8 @@ export default function Index() {
{activeCollection && ( @@ -211,12 +210,12 @@ export default function Index() { className="flex items-center btn px-2 btn-ghost rounded-full w-fit" onClick={() => setEditCollectionSharingModal(true)} > - {collectionOwner.id ? ( + {collectionOwner.id && ( - ) : undefined} + )} {activeCollection.members .sort((a, b) => (a.userId as number) - (b.userId as number)) .map((e, i) => { @@ -241,20 +240,20 @@ export default function Index() {

{activeCollection.members.length > 0 && - activeCollection.members.length === 1 + activeCollection.members.length === 1 ? t("by_author_and_other", { + author: collectionOwner.name, + count: activeCollection.members.length, + }) + : activeCollection.members.length > 0 && + activeCollection.members.length !== 1 + ? t("by_author_and_others", { author: collectionOwner.name, count: activeCollection.members.length, }) - : activeCollection.members.length > 0 && - activeCollection.members.length !== 1 - ? t("by_author_and_others", { - author: collectionOwner.name, - count: activeCollection.members.length, - }) : t("by_author", { - author: collectionOwner.name, - })} + author: collectionOwner.name, + })}

@@ -299,15 +298,15 @@ export default function Index() { setSortBy={setSortBy} editMode={ permissions === true || - permissions?.canUpdate || - permissions?.canDelete + permissions?.canUpdate || + permissions?.canDelete ? editMode : undefined } setEditMode={ permissions === true || - permissions?.canUpdate || - permissions?.canDelete + permissions?.canUpdate || + permissions?.canDelete ? setEditMode : undefined } @@ -315,11 +314,11 @@ export default function Index() {

{activeCollection?._count?.links === 1 ? t("showing_count_result", { - count: activeCollection?._count?.links, - }) + count: activeCollection?._count?.links, + }) : t("showing_count_results", { - count: activeCollection?._count?.links, - })} + count: activeCollection?._count?.links, + })}

diff --git a/pages/collections/index.tsx b/pages/collections/index.tsx index 642e3dd..717732f 100644 --- a/pages/collections/index.tsx +++ b/pages/collections/index.tsx @@ -58,7 +58,7 @@ export default function Collections() {
- {sortedCollections.filter((e) => e.ownerId !== data?.user.id)[0] ? ( + {sortedCollections.filter((e) => e.ownerId !== data?.user.id)[0] && ( <> - ) : undefined} + )} - {newCollectionModal ? ( + {newCollectionModal && ( setNewCollectionModal(false)} /> - ) : undefined} + )} ); } diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index 3d1fdb6..78239de 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -324,9 +324,9 @@ export default function Dashboard() { )} - {newLinkModal ? ( + {newLinkModal && ( setNewLinkModal(false)} /> - ) : undefined} + )} ); } diff --git a/pages/login.tsx b/pages/login.tsx index c1bc8d8..4eb59c8 100644 --- a/pages/login.tsx +++ b/pages/login.tsx @@ -203,9 +203,9 @@ export default function Login({ {t("login")} - {availableLogins.buttonAuths.length > 0 ? ( + {availableLogins.buttonAuths.length > 0 && (
{t("or_continue_with")}
- ) : undefined} + )} ); } @@ -224,9 +224,9 @@ export default function Login({ loading={submitLoader} > {value.name.toLowerCase() === "google" || - value.name.toLowerCase() === "apple" ? ( - - ) : undefined} + value.name.toLowerCase() === "apple" && ( + + )} {value.name} diff --git a/pages/public/collections/[id].tsx b/pages/public/collections/[id].tsx index 47e074f..454a957 100644 --- a/pages/public/collections/[id].tsx +++ b/pages/public/collections/[id].tsx @@ -104,16 +104,17 @@ export default function PublicCollections() { // @ts-ignore const LinkComponent = linkView[viewMode]; - return collection ? ( + if (!collection) return null; + + return (
- {collection ? ( + {collection && ( {collection.name} | Linkwarden - ) : undefined} + )}

@@ -151,12 +152,12 @@ export default function PublicCollections() { className="flex items-center btn px-2 btn-ghost rounded-full" onClick={() => setEditCollectionSharingModal(true)} > - {collectionOwner.id ? ( + {collectionOwner.id && ( - ) : undefined} + )} {collection.members .sort((a, b) => (a.userId as number) - (b.userId as number)) .map((e, i) => { @@ -181,20 +182,20 @@ export default function PublicCollections() {

{collection.members.length > 0 && - collection.members.length === 1 + collection.members.length === 1 ? t("by_author_and_other", { + author: collectionOwner.name, + count: collection.members.length, + }) + : collection.members.length > 0 && + collection.members.length !== 1 + ? t("by_author_and_others", { author: collectionOwner.name, count: collection.members.length, }) - : collection.members.length > 0 && - collection.members.length !== 1 - ? t("by_author_and_others", { - author: collectionOwner.name, - count: collection.members.length, - }) : t("by_author", { - author: collectionOwner.name, - })} + author: collectionOwner.name, + })}

@@ -218,11 +219,11 @@ export default function PublicCollections() { placeholder={ collection._count?.links === 1 ? t("search_count_link", { - count: collection._count?.links, - }) + count: collection._count?.links, + }) : t("search_count_links", { - count: collection._count?.links, - }) + count: collection._count?.links, + }) } /> @@ -248,15 +249,13 @@ export default function PublicCollections() {

*/}
- {editCollectionSharingModal ? ( + {editCollectionSharingModal && ( setEditCollectionSharingModal(false)} activeCollection={collection} /> - ) : undefined} + )} - ) : ( - <> ); } diff --git a/pages/register.tsx b/pages/register.tsx index 4eba2bc..6e7fc1e 100644 --- a/pages/register.tsx +++ b/pages/register.tsx @@ -133,9 +133,9 @@ export default function Register({ loading={submitLoader} > {value.name.toLowerCase() === "google" || - value.name.toLowerCase() === "apple" ? ( - - ) : undefined} + value.name.toLowerCase() === "apple" && ( + + )} {value.name} @@ -149,8 +149,8 @@ export default function Register({ text={ process.env.NEXT_PUBLIC_STRIPE ? t("trial_offer_desc", { - count: Number(process.env.NEXT_PUBLIC_TRIAL_PERIOD_DAYS || 14), - }) + count: Number(process.env.NEXT_PUBLIC_TRIAL_PERIOD_DAYS || 14), + }) : t("register_desc") } data-testid="registration-form" @@ -201,7 +201,7 @@ export default function Register({ )} - {emailEnabled ? ( + {emailEnabled && (

{t("email")}

@@ -214,7 +214,7 @@ export default function Register({ onChange={(e) => setForm({ ...form, email: e.target.value })} />
- ) : undefined} + )}

@@ -248,7 +248,7 @@ export default function Register({ />

- {process.env.NEXT_PUBLIC_STRIPE ? ( + {process.env.NEXT_PUBLIC_STRIPE && (

- ) : undefined} + )} - {tokens.length > 0 ? ( + {tokens.length > 0 && ( @@ -93,12 +93,12 @@ export default function AccessTokens() { ))}
- ) : undefined} + )} - {newTokenModal ? ( + {newTokenModal && ( setNewTokenModal(false)} /> - ) : undefined} + )} {revokeTokenModal && selectedToken && ( { diff --git a/pages/settings/account.tsx b/pages/settings/account.tsx index cb46442..2ba80fa 100644 --- a/pages/settings/account.tsx +++ b/pages/settings/account.tsx @@ -29,19 +29,19 @@ export default function Account() { !objectIsEmpty(account) ? account : ({ - // @ts-ignore - id: null, - name: "", - username: "", - email: "", - emailVerified: null, - password: undefined, - image: "", - isPrivate: true, - // @ts-ignore - createdAt: null, - whitelistedUsers: [], - } as unknown as AccountSettings) + // @ts-ignore + id: null, + name: "", + username: "", + email: "", + emailVerified: null, + password: undefined, + image: "", + isPrivate: true, + // @ts-ignore + createdAt: null, + whitelistedUsers: [], + } as unknown as AccountSettings) ); const { t } = useTranslation(); @@ -176,7 +176,7 @@ export default function Account() { onChange={(e) => setUser({ ...user, username: e.target.value })} /> - {emailEnabled ? ( + {emailEnabled && (

{t("email")}

setUser({ ...user, email: e.target.value })} />
- ) : undefined} + )}

{t("language")}

e.target.files && setFile(e.target.files[0])} /> diff --git a/components/ToggleDarkMode.tsx b/components/ToggleDarkMode.tsx index 9404d41..28631b9 100644 --- a/components/ToggleDarkMode.tsx +++ b/components/ToggleDarkMode.tsx @@ -21,9 +21,8 @@ export default function ToggleDarkMode({ className }: Props) { useEffect(() => { if (theme) { updateSettings({ theme }); - localStorage.setItem("theme", theme); } - }, [theme, updateSettings]); + }, [theme]); return (
redactIds(item)); - } else if (data !== null && typeof data === "object") { - const fieldsToRedact = ["id", "parentId", "collectionId", "ownerId"]; - - fieldsToRedact.forEach((field) => { - if (field in data) { - delete (data as any)[field]; - } - }); - - // Recursively call redactIds for each property that is an object or an array - Object.keys(data).forEach((key) => { - const value = (data as any)[key]; - if ( - value !== null && - (typeof value === "object" || Array.isArray(value)) - ) { - redactIds(value); - } - }); - } - } - - redactIds(userData); - return { response: userData, status: 200 }; } diff --git a/lib/api/generatePreview.ts b/lib/api/generatePreview.ts index 2dc325b..1c6ca94 100644 --- a/lib/api/generatePreview.ts +++ b/lib/api/generatePreview.ts @@ -16,7 +16,7 @@ const generatePreview = async ( return; } - image.resize(1280, Jimp.AUTO).quality(20); + image.resize(1000, Jimp.AUTO).quality(20); const processedBuffer = await image.getBufferAsync(Jimp.MIME_JPEG); if ( diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index e0b107f..0bdb525 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -135,7 +135,7 @@ export default function Dashboard() {
diff --git a/pages/public/collections/[id].tsx b/pages/public/collections/[id].tsx index 4c5f777..d49660f 100644 --- a/pages/public/collections/[id].tsx +++ b/pages/public/collections/[id].tsx @@ -88,160 +88,162 @@ export default function PublicCollections() { (localStorage.getItem("viewMode") as ViewMode) || ViewMode.Card ); - if (!collection) return null; + if (!collection) return <>; + else + return ( +
+ {collection && ( + + {collection.name} | Linkwarden + + + )} +
+
+

+ {collection.name} +

+
+ - return ( -
- {collection && ( - - {collection.name} | Linkwarden - - - )} -
-
-

- {collection.name} -

-
- - - - Linkwarden - + + Linkwarden + +
-
-
-
-
-
setEditCollectionSharingModal(true)} - > - {collectionOwner.id && ( - - )} - {collection.members - .sort((a, b) => (a.userId as number) - (b.userId as number)) - .map((e, i) => { - return ( - - ); - }) - .slice(0, 3)} - {collection.members.length - 3 > 0 && ( -
-
- +{collection.members.length - 3} -
-
- )} -
- -

- {collection.members.length > 0 && - collection.members.length === 1 - ? t("by_author_and_other", { - author: collectionOwner.name, - count: collection.members.length, +

+
+
+
setEditCollectionSharingModal(true)} + > + {collectionOwner.id && ( + + )} + {collection.members + .sort((a, b) => (a.userId as number) - (b.userId as number)) + .map((e, i) => { + return ( + + ); }) - : collection.members.length > 0 && - collection.members.length !== 1 - ? t("by_author_and_others", { + .slice(0, 3)} + {collection.members.length - 3 > 0 && ( +
+
+ +{collection.members.length - 3} +
+
+ )} +
+ +

+ {collection.members.length > 0 && + collection.members.length === 1 + ? t("by_author_and_other", { author: collectionOwner.name, count: collection.members.length, }) - : t("by_author", { - author: collectionOwner.name, - })} -

+ : collection.members.length > 0 && + collection.members.length !== 1 + ? t("by_author_and_others", { + author: collectionOwner.name, + count: collection.members.length, + }) + : t("by_author", { + author: collectionOwner.name, + })} +

+
-
-

{collection.description}

+

{collection.description}

-
+
-
- - + + + + + { + const linkWithCollectionData = { + ...e, + collection: collection, // Append collection data + }; + return linkWithCollectionData; + }) as any } + layout={viewMode} + placeholderCount={1} + useData={data} /> - + {!data.isLoading && links && !links[0] && ( +

{t("nothing_found")}

+ )} - { - const linkWithCollectionData = { - ...e, - collection: collection, // Append collection data - }; - return linkWithCollectionData; - }) as any - } - layout={viewMode} - placeholderCount={1} - useData={data} - /> - {!data.isLoading && links && !links[0] &&

{t("nothing_found")}

} - - {/*

+ {/*

List created with Linkwarden.

*/} +
+ {editCollectionSharingModal && ( + setEditCollectionSharingModal(false)} + activeCollection={collection} + /> + )}
- {editCollectionSharingModal && ( - setEditCollectionSharingModal(false)} - activeCollection={collection} - /> - )} -
- ); + ); } export { getServerSideProps }; From 2893d3caf22b7e4188cd41b2217c33a9c6d39197 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Sun, 18 Aug 2024 16:52:08 -0400 Subject: [PATCH 13/58] minor improvement --- pages/links/[id].tsx | 10 ++++++---- pages/public/links/[id].tsx | 12 +++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/pages/links/[id].tsx b/pages/links/[id].tsx index 9f2aaec..55f5648 100644 --- a/pages/links/[id].tsx +++ b/pages/links/[id].tsx @@ -19,10 +19,12 @@ const Index = () => { return (
{getLink.data ? ( - +
+ +
) : (
diff --git a/pages/public/links/[id].tsx b/pages/public/links/[id].tsx index 9f2aaec..8f3ef13 100644 --- a/pages/public/links/[id].tsx +++ b/pages/public/links/[id].tsx @@ -17,12 +17,14 @@ const Index = () => { }, []); return ( -
+
{getLink.data ? ( - +
+ +
) : (
From dc388ebba550e9343f89965cd8dcdfcc102baecb Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Mon, 19 Aug 2024 18:14:09 -0400 Subject: [PATCH 14/58] improved iconPicker component + other improvements --- components/Icon.tsx | 16 +++ components/IconPicker.tsx | 104 ++++++++++++++---- .../LinkViews/LinkComponents/LinkActions.tsx | 40 +++---- .../ModalContent/EditCollectionModal.tsx | 40 +++++-- components/ModalContent/LinkDetailModal.tsx | 33 +++--- .../ModalContent/NewCollectionModal.tsx | 4 +- components/ModalContent/NewLinkModal.tsx | 3 + components/ModalContent/UploadFileModal.tsx | 3 + components/Popover.tsx | 21 ++++ components/ReadableView.tsx | 2 +- pages/collections/[id].tsx | 2 +- .../migration.sql | 8 ++ .../migration.sql | 3 + prisma/schema.prisma | 7 +- styles/globals.css | 8 ++ 15 files changed, 222 insertions(+), 72 deletions(-) create mode 100644 components/Icon.tsx create mode 100644 components/Popover.tsx create mode 100644 prisma/migrations/20240819003838_add_icon_related_fields_to_link_and_collection_model/migration.sql create mode 100644 prisma/migrations/20240819005010_remove_collection_model_default_color/migration.sql diff --git a/components/Icon.tsx b/components/Icon.tsx new file mode 100644 index 0000000..178b9e6 --- /dev/null +++ b/components/Icon.tsx @@ -0,0 +1,16 @@ +import React, { forwardRef } from "react"; +import * as Icons from "@phosphor-icons/react"; + +type Props = { + icon: string; +} & Icons.IconProps; + +const Icon = forwardRef(({ icon, ...rest }) => { + const IconComponent: any = Icons[icon as keyof typeof Icons]; + + if (!IconComponent) { + return <>; + } else return ; +}); + +export default Icon; diff --git a/components/IconPicker.tsx b/components/IconPicker.tsx index 994a5af..f90dc1f 100644 --- a/components/IconPicker.tsx +++ b/components/IconPicker.tsx @@ -2,6 +2,9 @@ import { icons } from "@/lib/client/icons"; import React, { useMemo, useState } from "react"; import Fuse from "fuse.js"; import TextInput from "./TextInput"; +import Popover from "./Popover"; +import { HexColorPicker } from "react-colorful"; +import Icon from "./Icon"; const fuse = new Fuse(icons, { keys: [{ name: "name", weight: 4 }, "tags", "categories"], @@ -9,9 +12,29 @@ const fuse = new Fuse(icons, { useExtendedSearch: true, }); -type Props = {}; +type Props = { + onClose: Function; + alignment?: "left" | "right" | "bottom" | "top"; + color: string; + setColor: Function; + iconName: string; + setIconName: Function; + weight: "light" | "regular" | "bold" | "fill" | "duotone"; + setWeight: Function; + className?: string; +}; -const IconPicker = (props: Props) => { +const IconPicker = ({ + onClose, + alignment, + color, + setColor, + iconName, + setIconName, + weight, + setWeight, + className, +}: Props) => { const [query, setQuery] = useState(""); const filteredQueryResultsSelector = useMemo(() => { @@ -22,24 +45,67 @@ const IconPicker = (props: Props) => { }, [query]); return ( -
- setQuery(e.target.value)} - /> -
- {filteredQueryResultsSelector.map((icon) => { - const IconComponent = icon.Icon; - return ( -
console.log(icon.name)}> - -
- ); - })} + +
+
+
+ + + +
+ setColor(e)} /> +
+ + setQuery(e.target.value)} + /> + +
+ {filteredQueryResultsSelector.map((icon) => { + const IconComponent = icon.Icon; + return ( +
setIconName(icon.pascal_name)} + className={`cursor-pointer btn p-1 box-border ${ + icon.pascal_name === iconName + ? "outline outline-1 outline-primary" + : "" + }`} + > + +
+ ); + })} +
-
+ ); }; diff --git a/components/LinkViews/LinkComponents/LinkActions.tsx b/components/LinkViews/LinkComponents/LinkActions.tsx index f93f969..0e04491 100644 --- a/components/LinkViews/LinkComponents/LinkActions.tsx +++ b/components/LinkViews/LinkComponents/LinkActions.tsx @@ -105,24 +105,23 @@ export default function LinkActions({ alignToTop ? "" : "translate-y-10" }`} > - {permissions === true || - (permissions?.canUpdate && ( -
  • -
    { - (document?.activeElement as HTMLElement)?.blur(); - pinLink(); - }} - className="whitespace-nowrap" - > - {link?.pinnedBy && link.pinnedBy[0] - ? t("unpin") - : t("pin_to_dashboard")} -
    -
  • - ))} + {(permissions === true || permissions?.canUpdate) && ( +
  • +
    { + (document?.activeElement as HTMLElement)?.blur(); + pinLink(); + }} + className="whitespace-nowrap" + > + {link?.pinnedBy && link.pinnedBy[0] + ? t("unpin") + : t("pin_to_dashboard")} +
    +
  • + )}
  • { (document?.activeElement as HTMLElement)?.blur(); + console.log(e.shiftKey); e.shiftKey - ? async () => { + ? (async () => { const load = toast.loading(t("deleting")); await deleteLink.mutateAsync(link.id as number, { @@ -188,7 +188,7 @@ export default function LinkActions({ } }, }); - } + })() : setDeleteLinkModal(true); }} className="whitespace-nowrap" diff --git a/components/ModalContent/EditCollectionModal.tsx b/components/ModalContent/EditCollectionModal.tsx index f8dfd20..76833e7 100644 --- a/components/ModalContent/EditCollectionModal.tsx +++ b/components/ModalContent/EditCollectionModal.tsx @@ -6,6 +6,8 @@ import Modal from "../Modal"; import { useTranslation } from "next-i18next"; import { useUpdateCollection } from "@/hooks/store/collections"; import toast from "react-hot-toast"; +import IconPicker from "../IconPicker"; +import Icon from "../Icon"; type Props = { onClose: Function; @@ -20,6 +22,7 @@ export default function EditCollectionModal({ const [collection, setCollection] = useState(activeCollection); + const [iconPicker, setIconPicker] = useState(false); const [submitLoader, setSubmitLoader] = useState(false); const updateCollection = useUpdateCollection(); @@ -71,17 +74,32 @@ export default function EditCollectionModal({

    {t("color")}

    - - setCollection({ ...collection, color }) - } - /> -
    - +
    + setIconPicker(true)} + /> + {iconPicker && ( + setIconPicker(false)} + className="top-20" + color={collection.color as string} + setColor={(color: string) => + setCollection({ ...collection, color }) + } + weight={collection.iconWeight as any} + setWeight={(iconWeight: string) => + setCollection({ ...collection, iconWeight }) + } + iconName={collection.icon as string} + setIconName={(icon: string) => + setCollection({ ...collection, icon }) + } + /> + )}
    diff --git a/components/ModalContent/LinkDetailModal.tsx b/components/ModalContent/LinkDetailModal.tsx index e9f0086..ad7d36c 100644 --- a/components/ModalContent/LinkDetailModal.tsx +++ b/components/ModalContent/LinkDetailModal.tsx @@ -120,25 +120,24 @@ export default function LinkDetailModal({ onClose, onEdit, link }: Props) {
    - {permissions === true || - (permissions?.canUpdate && ( - <> -
    -
    + {(permissions === true || permissions?.canUpdate) && ( + <> +
    +
    -
    -
    { - onEdit(); - onClose(); - }} - > - {t("edit_link")} -
    +
    +
    { + onEdit(); + onClose(); + }} + > + {t("edit_link")}
    - - ))} +
    + + )}
    ); diff --git a/components/ModalContent/NewCollectionModal.tsx b/components/ModalContent/NewCollectionModal.tsx index df9d9bb..32cb5f9 100644 --- a/components/ModalContent/NewCollectionModal.tsx +++ b/components/ModalContent/NewCollectionModal.tsx @@ -88,7 +88,7 @@ export default function NewCollectionModal({ onClose, parent }: Props) {

    {t("color")}

    setCollection({ ...collection, color }) } @@ -96,7 +96,7 @@ export default function NewCollectionModal({ onClose, parent }: Props) {
    { + return ( + onClose()} + className={`absolute z-50 ${className || ""}`} + > + {children} + + ); +}; + +export default Popover; diff --git a/components/ReadableView.tsx b/components/ReadableView.tsx index 18477a0..11954b6 100644 --- a/components/ReadableView.tsx +++ b/components/ReadableView.tsx @@ -205,7 +205,7 @@ export default function ReadableView({ link }: Props) { >

    diff --git a/prisma/migrations/20240819003838_add_icon_related_fields_to_link_and_collection_model/migration.sql b/prisma/migrations/20240819003838_add_icon_related_fields_to_link_and_collection_model/migration.sql new file mode 100644 index 0000000..e208342 --- /dev/null +++ b/prisma/migrations/20240819003838_add_icon_related_fields_to_link_and_collection_model/migration.sql @@ -0,0 +1,8 @@ +-- AlterTable +ALTER TABLE "Collection" ADD COLUMN "icon" TEXT, +ADD COLUMN "iconWeight" TEXT; + +-- AlterTable +ALTER TABLE "Link" ADD COLUMN "color" TEXT, +ADD COLUMN "icon" TEXT, +ADD COLUMN "iconWeight" TEXT; diff --git a/prisma/migrations/20240819005010_remove_collection_model_default_color/migration.sql b/prisma/migrations/20240819005010_remove_collection_model_default_color/migration.sql new file mode 100644 index 0000000..199e104 --- /dev/null +++ b/prisma/migrations/20240819005010_remove_collection_model_default_color/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Collection" ALTER COLUMN "color" DROP NOT NULL, +ALTER COLUMN "color" DROP DEFAULT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 71b5b10..664dec5 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -94,7 +94,9 @@ model Collection { id Int @id @default(autoincrement()) name String description String @default("") - color String @default("#0ea5e9") + icon String? + iconWeight String? + color String? parentId Int? parent Collection? @relation("SubCollections", fields: [parentId], references: [id]) subCollections Collection[] @relation("SubCollections") @@ -133,6 +135,9 @@ model Link { collection Collection @relation(fields: [collectionId], references: [id]) collectionId Int tags Tag[] + icon String? + iconWeight String? + color String? url String? textContent String? preview String? diff --git a/styles/globals.css b/styles/globals.css index 1f97b6e..022c160 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -36,6 +36,14 @@ scrollbar-width: none; } +.hide-color-picker { + opacity: 0; + display: block; + width: 32px; + height: 32px; + border: none; +} + .hyphens { hyphens: auto; } From accbd4cbfa8c12b764abb0ceceb1c818804cd06f Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Mon, 19 Aug 2024 23:53:43 -0400 Subject: [PATCH 15/58] bug fixes --- components/InstallApp.tsx | 2 +- pages/api/v1/auth/[...nextauth].ts | 70 +++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/components/InstallApp.tsx b/components/InstallApp.tsx index f191f00..50071da 100644 --- a/components/InstallApp.tsx +++ b/components/InstallApp.tsx @@ -8,7 +8,7 @@ const InstallApp = (props: Props) => { const [isOpen, setIsOpen] = useState(true); return isOpen && !isPWA() ? ( -

    +
    0) { + await prisma.user.update({ + where: { + id: userExists.id, + }, + data: { + emailVerified: new Date(), + }, + }); + } + + if (userExists && !userExists.username) { const autoGeneratedUsername = "user" + Math.round(Math.random() * 1000000000); @@ -1217,6 +1264,22 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) { }, }); } + } else if (trigger === "signIn") { + const user = await prisma.user.findUnique({ + where: { + id: token.id, + }, + }); + + if (user && !user.username) { + const autoGeneratedUsername = + "user" + Math.round(Math.random() * 1000000000); + + await prisma.user.update({ + where: { id: user.id }, + data: { username: autoGeneratedUsername }, + }); + } } return token; @@ -1224,6 +1287,8 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) { async session({ session, token }) { session.user.id = token.id; + console.log("session", session); + if (STRIPE_SECRET_KEY) { const user = await prisma.user.findUnique({ where: { @@ -1235,6 +1300,7 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) { }); if (user) { + // const subscribedUser = await verifySubscription(user); } } From ae2324ecd352acfc215a6222727bacc34c80f444 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Tue, 20 Aug 2024 16:59:01 -0400 Subject: [PATCH 16/58] progressed icon picker component --- components/IconPicker.tsx | 168 ++++++++++-------- .../ModalContent/EditCollectionModal.tsx | 73 +++----- pages/_app.tsx | 7 +- styles/globals.css | 9 +- 4 files changed, 130 insertions(+), 127 deletions(-) diff --git a/components/IconPicker.tsx b/components/IconPicker.tsx index f90dc1f..513da2c 100644 --- a/components/IconPicker.tsx +++ b/components/IconPicker.tsx @@ -1,31 +1,26 @@ import { icons } from "@/lib/client/icons"; -import React, { useMemo, useState } from "react"; +import React, { useMemo, useState, lazy, Suspense } from "react"; import Fuse from "fuse.js"; import TextInput from "./TextInput"; import Popover from "./Popover"; import { HexColorPicker } from "react-colorful"; +import { useTranslation } from "next-i18next"; import Icon from "./Icon"; - -const fuse = new Fuse(icons, { - keys: [{ name: "name", weight: 4 }, "tags", "categories"], - threshold: 0.2, - useExtendedSearch: true, -}); +import { IconWeight } from "@phosphor-icons/react"; type Props = { - onClose: Function; - alignment?: "left" | "right" | "bottom" | "top"; + alignment?: "left" | "right"; color: string; setColor: Function; - iconName: string; + iconName?: string; setIconName: Function; - weight: "light" | "regular" | "bold" | "fill" | "duotone"; + weight: "light" | "regular" | "bold" | "fill" | "duotone" | "thin"; setWeight: Function; + reset: Function; className?: string; }; const IconPicker = ({ - onClose, alignment, color, setColor, @@ -34,8 +29,17 @@ const IconPicker = ({ weight, setWeight, className, + reset, }: Props) => { + const fuse = new Fuse(icons, { + keys: [{ name: "name", weight: 4 }, "tags", "categories"], + threshold: 0.2, + useExtendedSearch: true, + }); + + const { t } = useTranslation(); const [query, setQuery] = useState(""); + const [iconPicker, setIconPicker] = useState(false); const filteredQueryResultsSelector = useMemo(() => { if (!query) { @@ -45,67 +49,87 @@ const IconPicker = ({ }, [query]); return ( - -
    -
    -
    - - - -
    - setColor(e)} /> -
    - - setQuery(e.target.value)} - /> - -
    - {filteredQueryResultsSelector.map((icon) => { - const IconComponent = icon.Icon; - return ( -
    setIconName(icon.pascal_name)} - className={`cursor-pointer btn p-1 box-border ${ - icon.pascal_name === iconName - ? "outline outline-1 outline-primary" - : "" - }`} - > - -
    - ); - })} -
    +
    +
    setIconPicker(!iconPicker)} + className="btn btn-square w-20 h-20" + > + {iconName ? ( + + ) : ( + + )}
    - + {iconPicker && ( + setIconPicker(false)} + className={ + className + + " fade-in bg-base-200 border border-neutral-content p-2 h-44 w-[22.5rem] rounded-lg backdrop-blur-sm bg-opacity-90 top-20 left-0 lg:-translate-x-1/3" + } + > +
    +
    +
    } + > + {t("reset")} +
    + + setColor(e)} /> +
    + +
    + setQuery(e.target.value)} + /> + +
    + {filteredQueryResultsSelector.map((icon) => { + const IconComponent = icon.Icon; + return ( +
    setIconName(icon.pascal_name)} + className={`cursor-pointer btn p-1 box-border bg-base-100 border-none aspect-square ${ + icon.pascal_name === iconName + ? "outline outline-1 outline-primary" + : "" + }`} + > + +
    + ); + })} +
    +
    +
    +
    + )} +
    ); }; diff --git a/components/ModalContent/EditCollectionModal.tsx b/components/ModalContent/EditCollectionModal.tsx index 76833e7..20a6cdd 100644 --- a/components/ModalContent/EditCollectionModal.tsx +++ b/components/ModalContent/EditCollectionModal.tsx @@ -1,6 +1,5 @@ import React, { useState } from "react"; import TextInput from "@/components/TextInput"; -import { HexColorPicker } from "react-colorful"; import { CollectionIncludingMembersAndLinkCount } from "@/types/global"; import Modal from "../Modal"; import { useTranslation } from "next-i18next"; @@ -8,6 +7,7 @@ import { useUpdateCollection } from "@/hooks/store/collections"; import toast from "react-hot-toast"; import IconPicker from "../IconPicker"; import Icon from "../Icon"; +import { IconWeight } from "@phosphor-icons/react"; type Props = { onClose: Function; @@ -22,7 +22,6 @@ export default function EditCollectionModal({ const [collection, setCollection] = useState(activeCollection); - const [iconPicker, setIconPicker] = useState(false); const [submitLoader, setSubmitLoader] = useState(false); const updateCollection = useUpdateCollection(); @@ -59,10 +58,32 @@ export default function EditCollectionModal({
    -
    -
    -

    {t("name")}

    -
    +
    +
    + + setCollection({ ...collection, color }) + } + weight={(collection.iconWeight || "regular") as IconWeight} + setWeight={(iconWeight: string) => + setCollection({ ...collection, iconWeight }) + } + iconName={collection.icon as string} + setIconName={(icon: string) => + setCollection({ ...collection, icon }) + } + reset={() => + setCollection({ + ...collection, + color: "#0ea5e9", + icon: "", + iconWeight: "", + }) + } + /> +
    +

    {t("name")}

    -
    -

    {t("color")}

    -
    -
    - setIconPicker(true)} - /> - {iconPicker && ( - setIconPicker(false)} - className="top-20" - color={collection.color as string} - setColor={(color: string) => - setCollection({ ...collection, color }) - } - weight={collection.iconWeight as any} - setWeight={(iconWeight: string) => - setCollection({ ...collection, iconWeight }) - } - iconName={collection.icon as string} - setIconName={(icon: string) => - setCollection({ ...collection, icon }) - } - /> - )} -
    - setCollection({ ...collection, color: "#0ea5e9" }) - } - > - {t("reset")} -
    -
    -
    -
    diff --git a/pages/_app.tsx b/pages/_app.tsx index dc6603a..c99d26a 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -88,13 +88,10 @@ function App({ {icon} {message} {t.type !== "loading" && ( - + >
    )}
    )} diff --git a/styles/globals.css b/styles/globals.css index 022c160..28a0105 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -144,15 +144,16 @@ /* For react-colorful */ .color-picker .react-colorful { - width: 100%; - height: 7.5rem; + height: 7rem; + width: 7rem; } .color-picker .react-colorful__hue { height: 1rem; } .color-picker .react-colorful__pointer { - width: 1.3rem; - height: 1.3rem; + width: 1rem; + height: 1rem; + border-width: 1px; } /* For the Link banner */ From 6df2e44213ce028026d9b0c1266999e79bc51daf Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Tue, 20 Aug 2024 18:11:20 -0400 Subject: [PATCH 17/58] added translation to icon picker component + other fixes and improvements --- components/IconGrid.tsx | 45 +++++++++++++++++++++++++++ components/IconPicker.tsx | 57 +++++++++++------------------------ public/locales/en/common.json | 9 +++++- 3 files changed, 70 insertions(+), 41 deletions(-) create mode 100644 components/IconGrid.tsx diff --git a/components/IconGrid.tsx b/components/IconGrid.tsx new file mode 100644 index 0000000..edb2362 --- /dev/null +++ b/components/IconGrid.tsx @@ -0,0 +1,45 @@ +import { icons } from "@/lib/client/icons"; +import Fuse from "fuse.js"; +import { useMemo } from "react"; + +const fuse = new Fuse(icons, { + keys: [{ name: "name", weight: 4 }, "tags", "categories"], + threshold: 0.2, + useExtendedSearch: true, +}); + +type Props = { + query: string; + color: string; + weight: "light" | "regular" | "bold" | "fill" | "duotone" | "thin"; + iconName?: string; + setIconName: Function; +}; + +const IconGrid = ({ query, color, weight, iconName, setIconName }: Props) => { + const filteredQueryResultsSelector = useMemo(() => { + if (!query) { + return icons; + } + return fuse.search(query).map((result) => result.item); + }, [query]); + + return filteredQueryResultsSelector.map((icon) => { + const IconComponent = icon.Icon; + return ( +
    setIconName(icon.pascal_name)} + className={`cursor-pointer btn p-1 box-border bg-base-100 border-none w-full ${ + icon.pascal_name === iconName + ? "outline outline-1 outline-primary" + : "" + }`} + > + +
    + ); + }); +}; + +export default IconGrid; diff --git a/components/IconPicker.tsx b/components/IconPicker.tsx index 513da2c..a712262 100644 --- a/components/IconPicker.tsx +++ b/components/IconPicker.tsx @@ -1,12 +1,11 @@ -import { icons } from "@/lib/client/icons"; -import React, { useMemo, useState, lazy, Suspense } from "react"; -import Fuse from "fuse.js"; +import React, { useState } from "react"; import TextInput from "./TextInput"; import Popover from "./Popover"; import { HexColorPicker } from "react-colorful"; import { useTranslation } from "next-i18next"; import Icon from "./Icon"; import { IconWeight } from "@phosphor-icons/react"; +import IconGrid from "./IconGrid"; type Props = { alignment?: "left" | "right"; @@ -31,23 +30,10 @@ const IconPicker = ({ className, reset, }: Props) => { - const fuse = new Fuse(icons, { - keys: [{ name: "name", weight: 4 }, "tags", "categories"], - threshold: 0.2, - useExtendedSearch: true, - }); - const { t } = useTranslation(); const [query, setQuery] = useState(""); const [iconPicker, setIconPicker] = useState(false); - const filteredQueryResultsSelector = useMemo(() => { - if (!query) { - return icons; - } - return fuse.search(query).map((result) => result.item); - }, [query]); - return (
    setWeight(e.target.value)} > - - - - - - + + + + + + setColor(e)} />
    -
    +
    setQuery(e.target.value)} />
    - {filteredQueryResultsSelector.map((icon) => { - const IconComponent = icon.Icon; - return ( -
    setIconName(icon.pascal_name)} - className={`cursor-pointer btn p-1 box-border bg-base-100 border-none aspect-square ${ - icon.pascal_name === iconName - ? "outline outline-1 outline-primary" - : "" - }`} - > - -
    - ); - })} +
    diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c2e9a82..6165806 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -372,5 +372,12 @@ "demo_desc": "This is only a demo instance of Linkwarden and uploads are disabled.", "demo_desc_2": "If you want to try out the full version, you can sign up for a free trial at:", "demo_button": "Login as demo user", - "notes": "Notes" + "notes": "Notes", + "regular": "Regular", + "thin": "Thin", + "bold": "Bold", + "fill": "Fill", + "duotone": "Duotone", + "light_icon": "Light", + "search": "Search" } \ No newline at end of file From bf1a6efd2e20887241c1174a1cca4538b71e179b Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Tue, 20 Aug 2024 19:25:35 -0400 Subject: [PATCH 18/58] custom icons fully implemented for collections --- components/CollectionListing.tsx | 27 +++++++-- components/LinkDetails.tsx | 19 ++++-- .../LinkComponents/LinkCollection.tsx | 19 ++++-- .../ModalContent/EditCollectionModal.tsx | 1 - .../ModalContent/NewCollectionModal.tsx | 58 +++++++++---------- components/ReadableView.tsx | 21 +++++-- .../collectionId/updateCollectionById.ts | 4 +- .../controllers/collections/postCollection.ts | 2 + pages/collections/[id].tsx | 21 +++++-- .../migration.sql | 9 +++ prisma/schema.prisma | 2 +- 11 files changed, 127 insertions(+), 56 deletions(-) create mode 100644 prisma/migrations/20240820230618_add_default_value_for_collection_color/migration.sql diff --git a/components/CollectionListing.tsx b/components/CollectionListing.tsx index f3a722e..1533c3b 100644 --- a/components/CollectionListing.tsx +++ b/components/CollectionListing.tsx @@ -17,6 +17,8 @@ import toast from "react-hot-toast"; import { useTranslation } from "next-i18next"; import { useCollections, useUpdateCollection } from "@/hooks/store/collections"; import { useUpdateUser, useUser } from "@/hooks/store/user"; +import Icon from "./Icon"; +import { IconWeight } from "@phosphor-icons/react"; interface ExtendedTreeItem extends TreeItem { data: Collection; @@ -256,7 +258,7 @@ const renderItem = ( : "hover:bg-neutral/20" } duration-100 flex gap-1 items-center pr-2 pl-1 rounded-md`} > - {Icon(item as ExtendedTreeItem, onExpand, onCollapse)} + {Dropdown(item as ExtendedTreeItem, onExpand, onCollapse)} - + {collection.icon ? ( + + ) : ( + + )} +

    {collection.name}

    {collection.isPublic && ( @@ -288,7 +301,7 @@ const renderItem = ( ); }; -const Icon = ( +const Dropdown = ( item: ExtendedTreeItem, onExpand: (id: ItemId) => void, onCollapse: (id: ItemId) => void @@ -332,6 +345,8 @@ const buildTreeFromCollections = ( name: collection.name, description: collection.description, color: collection.color, + icon: collection.icon, + iconWeight: collection.iconWeight, isPublic: collection.isPublic, ownerId: collection.ownerId, createdAt: collection.createdAt, diff --git a/components/LinkDetails.tsx b/components/LinkDetails.tsx index 0f237ca..0b7ca14 100644 --- a/components/LinkDetails.tsx +++ b/components/LinkDetails.tsx @@ -20,6 +20,8 @@ import { useGetLink } from "@/hooks/store/links"; import LinkIcon from "./LinkViews/LinkComponents/LinkIcon"; import CopyButton from "./CopyButton"; import { useRouter } from "next/router"; +import Icon from "./Icon"; +import { IconWeight } from "@phosphor-icons/react"; type Props = { className?: string; @@ -163,10 +165,19 @@ export default function LinkDetails({ className, link }: Props) { >

    {link.collection.name}

    - + {link.collection.icon ? ( + + ) : ( + + )}
    diff --git a/components/LinkViews/LinkComponents/LinkCollection.tsx b/components/LinkViews/LinkComponents/LinkCollection.tsx index f6e508f..45c1725 100644 --- a/components/LinkViews/LinkComponents/LinkCollection.tsx +++ b/components/LinkViews/LinkComponents/LinkCollection.tsx @@ -1,7 +1,9 @@ +import Icon from "@/components/Icon"; import { CollectionIncludingMembersAndLinkCount, LinkIncludingShortenedCollectionAndTags, } from "@/types/global"; +import { IconWeight } from "@phosphor-icons/react"; import Link from "next/link"; import React from "react"; @@ -22,10 +24,19 @@ export default function LinkCollection({ className="flex items-center gap-1 max-w-full w-fit hover:opacity-70 duration-100 select-none" title={collection?.name} > - + {link.collection.icon ? ( + + ) : ( + + )}

    {collection?.name}

    diff --git a/components/ModalContent/EditCollectionModal.tsx b/components/ModalContent/EditCollectionModal.tsx index 20a6cdd..e5f81da 100644 --- a/components/ModalContent/EditCollectionModal.tsx +++ b/components/ModalContent/EditCollectionModal.tsx @@ -6,7 +6,6 @@ import { useTranslation } from "next-i18next"; import { useUpdateCollection } from "@/hooks/store/collections"; import toast from "react-hot-toast"; import IconPicker from "../IconPicker"; -import Icon from "../Icon"; import { IconWeight } from "@phosphor-icons/react"; type Props = { diff --git a/components/ModalContent/NewCollectionModal.tsx b/components/ModalContent/NewCollectionModal.tsx index 32cb5f9..292a8d0 100644 --- a/components/ModalContent/NewCollectionModal.tsx +++ b/components/ModalContent/NewCollectionModal.tsx @@ -1,12 +1,13 @@ import React, { useEffect, useState } from "react"; import TextInput from "@/components/TextInput"; -import { HexColorPicker } from "react-colorful"; import { Collection } from "@prisma/client"; import Modal from "../Modal"; import { CollectionIncludingMembersAndLinkCount } from "@/types/global"; import { useTranslation } from "next-i18next"; import { useCreateCollection } from "@/hooks/store/collections"; import toast from "react-hot-toast"; +import IconPicker from "../IconPicker"; +import { IconWeight } from "@phosphor-icons/react"; type Props = { onClose: Function; @@ -72,10 +73,32 @@ export default function NewCollectionModal({ onClose, parent }: Props) {
    -
    -
    -

    {t("name")}

    -
    +
    +
    + + setCollection({ ...collection, color }) + } + weight={(collection.iconWeight || "regular") as IconWeight} + setWeight={(iconWeight: string) => + setCollection({ ...collection, iconWeight }) + } + iconName={collection.icon as string} + setIconName={(icon: string) => + setCollection({ ...collection, icon }) + } + reset={() => + setCollection({ + ...collection, + color: "#0ea5e9", + icon: "", + iconWeight: "", + }) + } + /> +
    +

    {t("name")}

    -
    -

    {t("color")}

    -
    - - setCollection({ ...collection, color }) - } - /> -
    - -
    - setCollection({ ...collection, color: "#0ea5e9" }) - } - > - {t("reset")} -
    -
    -
    -
    diff --git a/components/ReadableView.tsx b/components/ReadableView.tsx index 11954b6..b352489 100644 --- a/components/ReadableView.tsx +++ b/components/ReadableView.tsx @@ -16,6 +16,8 @@ import LinkActions from "./LinkViews/LinkComponents/LinkActions"; import { useTranslation } from "next-i18next"; import { useCollections } from "@/hooks/store/collections"; import { useGetLink } from "@/hooks/store/links"; +import { IconWeight } from "@phosphor-icons/react"; +import Icon from "./Icon"; type LinkContent = { title: string; @@ -203,10 +205,21 @@ export default function ReadableView({ link }: Props) { href={`/collections/${link?.collection.id}`} className="flex items-center gap-1 cursor-pointer hover:opacity-60 duration-100 mr-2 z-10" > - + {link.collection.icon ? ( + + ) : ( + + )}

    - + {activeCollection.icon ? ( + + ) : ( + + )}

    {activeCollection?.name} diff --git a/prisma/migrations/20240820230618_add_default_value_for_collection_color/migration.sql b/prisma/migrations/20240820230618_add_default_value_for_collection_color/migration.sql new file mode 100644 index 0000000..f634b68 --- /dev/null +++ b/prisma/migrations/20240820230618_add_default_value_for_collection_color/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - Made the column `color` on table `Collection` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "Collection" ALTER COLUMN "color" SET NOT NULL, +ALTER COLUMN "color" SET DEFAULT '#0ea5e9'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 664dec5..922480f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -96,7 +96,7 @@ model Collection { description String @default("") icon String? iconWeight String? - color String? + color String @default("#0ea5e9") parentId Int? parent Collection? @relation("SubCollections", fields: [parentId], references: [id]) subCollections Collection[] @relation("SubCollections") From 9fe829771d2b6fa5102ebc1e4fa496d1d5e3a573 Mon Sep 17 00:00:00 2001 From: Isaac Wise Date: Thu, 22 Aug 2024 17:09:14 -0500 Subject: [PATCH 19/58] Add new collection drop down --- pages/collections/index.tsx | 38 +++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/pages/collections/index.tsx b/pages/collections/index.tsx index 5975923..b1dbb8b 100644 --- a/pages/collections/index.tsx +++ b/pages/collections/index.tsx @@ -10,6 +10,7 @@ import PageHeader from "@/components/PageHeader"; import getServerSideProps from "@/lib/client/getServerSideProps"; import { useTranslation } from "next-i18next"; import { useCollections } from "@/hooks/store/collections"; +import { dropdownTriggerer } from "@/lib/client/utils"; export default function Collections() { const { t } = useTranslation(); @@ -29,12 +30,37 @@ export default function Collections() {

    - - +
    + +
    +
    +
    + +
    +
      +
    • +
      setNewCollectionModal(true)} + className="whitespace-nowrap" + > + {t("new_collection")} +
      +
    • +
    +
    +
    +
    From fae9e95fa9962abf80e90585c2706cda58d03da2 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Sat, 24 Aug 2024 15:50:29 -0400 Subject: [PATCH 20/58] added custom icons for links --- components/IconPicker.tsx | 9 ++- components/LinkDetails.tsx | 4 +- .../LinkViews/LinkComponents/LinkIcon.tsx | 66 +++++++++---------- .../LinkViews/LinkComponents/LinkList.tsx | 2 +- components/ModalContent/EditLinkModal.tsx | 25 +++++++ .../links/linkId/updateLinkById.ts | 3 + public/locales/en/common.json | 3 +- 7 files changed, 72 insertions(+), 40 deletions(-) diff --git a/components/IconPicker.tsx b/components/IconPicker.tsx index a712262..f561137 100644 --- a/components/IconPicker.tsx +++ b/components/IconPicker.tsx @@ -8,13 +8,14 @@ import { IconWeight } from "@phosphor-icons/react"; import IconGrid from "./IconGrid"; type Props = { - alignment?: "left" | "right"; + alignment?: string; color: string; setColor: Function; iconName?: string; setIconName: Function; weight: "light" | "regular" | "bold" | "fill" | "duotone" | "thin"; setWeight: Function; + hideDefaultIcon?: boolean; reset: Function; className?: string; }; @@ -27,6 +28,7 @@ const IconPicker = ({ setIconName, weight, setWeight, + hideDefaultIcon, className, reset, }: Props) => { @@ -47,6 +49,8 @@ const IconPicker = ({ weight={(weight || "regular") as IconWeight} color={color || "#0ea5e9"} /> + ) : !iconName && hideDefaultIcon ? ( +

    {t("set_custom_icon")}

    ) : ( setIconPicker(false)} className={ className + - " fade-in bg-base-200 border border-neutral-content p-2 h-44 w-[22.5rem] rounded-lg backdrop-blur-sm bg-opacity-90 top-20 left-0 lg:-translate-x-1/3" + " fade-in bg-base-200 border border-neutral-content p-2 h-44 w-[22.5rem] rounded-lg backdrop-blur-sm bg-opacity-90 " + + (alignment || " lg:-translate-x-1/3 top-20 left-0 ") } >
    diff --git a/components/LinkDetails.tsx b/components/LinkDetails.tsx index 0b7ca14..84aabe9 100644 --- a/components/LinkDetails.tsx +++ b/components/LinkDetails.tsx @@ -127,7 +127,9 @@ export default function LinkDetails({ className, link }: Props) { return (
    - +
    + +
    {link.name &&

    {link.name}

    } diff --git a/components/LinkViews/LinkComponents/LinkIcon.tsx b/components/LinkViews/LinkComponents/LinkIcon.tsx index e60c23a..2ebc530 100644 --- a/components/LinkViews/LinkComponents/LinkIcon.tsx +++ b/components/LinkViews/LinkComponents/LinkIcon.tsx @@ -2,34 +2,24 @@ import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; import Image from "next/image"; import isValidUrl from "@/lib/shared/isValidUrl"; import React from "react"; +import Icon from "@/components/Icon"; +import { IconWeight } from "@phosphor-icons/react"; +import clsx from "clsx"; export default function LinkIcon({ link, className, - size, + hideBackground, }: { link: LinkIncludingShortenedCollectionAndTags; className?: string; - size?: "small" | "medium"; + hideBackground?: boolean; }) { - let iconClasses: string = - "bg-white shadow rounded-md border-[2px] flex item-center justify-center border-white select-none z-10 " + - (className || ""); - - let dimension; - - switch (size) { - case "small": - dimension = " w-8 h-8"; - break; - case "medium": - dimension = " w-12 h-12"; - break; - default: - size = "medium"; - dimension = " w-12 h-12"; - break; - } + let iconClasses: string = clsx( + "rounded-md flex item-center justify-center select-none z-10 w-12 h-12", + !hideBackground && "bg-white backdrop-blur-lg bg-opacity-50 p-1", + className + ); const url = isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; @@ -38,14 +28,22 @@ export default function LinkIcon({ return ( <> - {link.type === "url" && url ? ( + {link.icon ? ( + + ) : link.type === "url" && url ? ( showFavicon ? ( { setShowFavicon(false); @@ -53,22 +51,22 @@ export default function LinkIcon({ /> ) : ( ) ) : link.type === "pdf" ? ( ) : link.type === "image" ? ( ) : // : link.type === "monolith" ? ( // { return ( -
    +
    ); }; + +// `text-black aspect-square text-4xl ${iconClasses}` diff --git a/components/LinkViews/LinkComponents/LinkList.tsx b/components/LinkViews/LinkComponents/LinkList.tsx index 31a214b..2eddc05 100644 --- a/components/LinkViews/LinkComponents/LinkList.tsx +++ b/components/LinkViews/LinkComponents/LinkList.tsx @@ -111,7 +111,7 @@ export default function LinkCardCompact({ } >
    - +
    diff --git a/components/ModalContent/EditLinkModal.tsx b/components/ModalContent/EditLinkModal.tsx index 07911f4..fa33870 100644 --- a/components/ModalContent/EditLinkModal.tsx +++ b/components/ModalContent/EditLinkModal.tsx @@ -9,6 +9,8 @@ import Modal from "../Modal"; import { useTranslation } from "next-i18next"; import { useUpdateLink } from "@/hooks/store/links"; import toast from "react-hot-toast"; +import IconPicker from "../IconPicker"; +import { IconWeight } from "@phosphor-icons/react"; type Props = { onClose: Function; @@ -138,6 +140,29 @@ export default function EditLinkModal({ onClose, activeLink }: Props) { className="resize-none w-full rounded-md p-2 border-neutral-content bg-base-200 focus:border-sky-300 dark:focus:border-sky-600 border-solid border outline-none duration-100" />
    + +
    + setLink({ ...link, color })} + weight={(link.iconWeight || "regular") as IconWeight} + setWeight={(iconWeight: string) => + setLink({ ...link, iconWeight }) + } + iconName={link.icon as string} + setIconName={(icon: string) => setLink({ ...link, icon })} + reset={() => + setLink({ + ...link, + color: "", + icon: "", + iconWeight: "", + }) + } + alignment="-top-10 translate-x-20" + /> +
    diff --git a/lib/api/controllers/links/linkId/updateLinkById.ts b/lib/api/controllers/links/linkId/updateLinkById.ts index 306696a..53daa87 100644 --- a/lib/api/controllers/links/linkId/updateLinkById.ts +++ b/lib/api/controllers/links/linkId/updateLinkById.ts @@ -96,6 +96,9 @@ export default async function updateLinkById( data: { name: data.name, description: data.description, + icon: data.icon, + iconWeight: data.iconWeight, + color: data.color, collection: { connect: { id: data.collection.id, diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 6165806..9a9cc86 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -379,5 +379,6 @@ "fill": "Fill", "duotone": "Duotone", "light_icon": "Light", - "search": "Search" + "search": "Search", + "set_custom_icon": "Set Custom Icon" } \ No newline at end of file From f368c2aa81c4700e056fbfb9772edb71c6f19e24 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Mon, 26 Aug 2024 16:11:02 -0400 Subject: [PATCH 21/58] less padding for list view --- components/LinkViews/LinkComponents/LinkIcon.tsx | 14 +++----------- components/LinkViews/LinkComponents/LinkList.tsx | 13 ++++--------- components/LinkViews/Links.tsx | 14 +++++++------- components/ToggleDarkMode.tsx | 6 ++++-- pages/public/links/[id].tsx | 4 +--- 5 files changed, 19 insertions(+), 32 deletions(-) diff --git a/components/LinkViews/LinkComponents/LinkIcon.tsx b/components/LinkViews/LinkComponents/LinkIcon.tsx index 2ebc530..c7c42dd 100644 --- a/components/LinkViews/LinkComponents/LinkIcon.tsx +++ b/components/LinkViews/LinkComponents/LinkIcon.tsx @@ -16,8 +16,8 @@ export default function LinkIcon({ hideBackground?: boolean; }) { let iconClasses: string = clsx( - "rounded-md flex item-center justify-center select-none z-10 w-12 h-12", - !hideBackground && "bg-white backdrop-blur-lg bg-opacity-50 p-1", + "rounded flex item-center justify-center select-none z-10 w-12 h-12", + !hideBackground && "rounded-md bg-white backdrop-blur-lg bg-opacity-50 p-1", className ); @@ -50,23 +50,17 @@ export default function LinkIcon({ }} /> ) : ( - + ) ) : link.type === "pdf" ? ( ) : link.type === "image" ? ( ) : // : link.type === "monolith" ? ( // { return (
    diff --git a/components/LinkViews/LinkComponents/LinkList.tsx b/components/LinkViews/LinkComponents/LinkList.tsx index 2eddc05..d4e32c7 100644 --- a/components/LinkViews/LinkComponents/LinkList.tsx +++ b/components/LinkViews/LinkComponents/LinkList.tsx @@ -93,9 +93,9 @@ export default function LinkCardCompact({ return ( <>
    selectable ? handleCheckboxClick(link) @@ -143,12 +143,7 @@ export default function LinkCardCompact({ flipDropdown={flipDropdown} />
    -
    +
    ); } diff --git a/components/LinkViews/Links.tsx b/components/LinkViews/Links.tsx index 511c1d2..c8de1e5 100644 --- a/components/LinkViews/Links.tsx +++ b/components/LinkViews/Links.tsx @@ -142,7 +142,7 @@ export function ListView({ placeHolderRef?: any; }) { return ( -
    +
    {links?.map((e, i) => { return ( -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    ); diff --git a/components/ToggleDarkMode.tsx b/components/ToggleDarkMode.tsx index 28631b9..6a9a5dd 100644 --- a/components/ToggleDarkMode.tsx +++ b/components/ToggleDarkMode.tsx @@ -1,12 +1,14 @@ import useLocalSettingsStore from "@/store/localSettings"; import { useEffect, useState, ChangeEvent } from "react"; import { useTranslation } from "next-i18next"; +import clsx from "clsx"; type Props = { className?: string; + align?: "left" | "right"; }; -export default function ToggleDarkMode({ className }: Props) { +export default function ToggleDarkMode({ className, align }: Props) { const { t } = useTranslation(); const { settings, updateSettings } = useLocalSettingsStore(); @@ -26,7 +28,7 @@ export default function ToggleDarkMode({ className }: Props) { return (
    { const router = useRouter(); const { id } = router.query; - useState; - const getLink = useGetLink(); useEffect(() => { From 642374c2e5f768098ae8c209ad378d31ff532189 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Mon, 26 Aug 2024 16:22:59 -0400 Subject: [PATCH 22/58] remove commented code --- components/ViewDropdown.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/components/ViewDropdown.tsx b/components/ViewDropdown.tsx index 6eba3fc..2333970 100644 --- a/components/ViewDropdown.tsx +++ b/components/ViewDropdown.tsx @@ -56,17 +56,6 @@ export default function ViewDropdown({ viewMode, setViewMode }: Props) { > - - {/* */}
    ); } From 9ae9c7c81ab348c32eb80dba4647a93c0e4a12a3 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Mon, 26 Aug 2024 18:47:10 -0400 Subject: [PATCH 23/58] refactored view dropdown --- .../LinkViews/LinkComponents/LinkCard.tsx | 13 +- .../LinkViews/LinkComponents/LinkMasonry.tsx | 8 +- components/ViewDropdown.tsx | 133 +++++++++++++----- lib/client/icons.ts | 6 +- public/locales/en/common.json | 7 +- store/localSettings.ts | 106 ++++++++++---- 6 files changed, 191 insertions(+), 82 deletions(-) diff --git a/components/LinkViews/LinkComponents/LinkCard.tsx b/components/LinkViews/LinkComponents/LinkCard.tsx index f1e32df..285437a 100644 --- a/components/LinkViews/LinkComponents/LinkCard.tsx +++ b/components/LinkViews/LinkComponents/LinkCard.tsx @@ -23,6 +23,7 @@ import { useCollections } from "@/hooks/store/collections"; import { useUser } from "@/hooks/store/user"; import { useGetLink, useLinks } from "@/hooks/store/links"; import { useRouter } from "next/router"; +import useLocalSettingsStore from "@/store/localSettings"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -41,6 +42,10 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) { const { setSelectedLinks, selectedLinks } = useLinkStore(); + const { + settings: { show }, + } = useLocalSettingsStore(); + const { data: { data: links = [] }, } = useLinks(); @@ -166,11 +171,9 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) { ) : (
    )} - {link.type !== "image" && ( -
    - -
    - )} +
    + +

    diff --git a/components/LinkViews/LinkComponents/LinkMasonry.tsx b/components/LinkViews/LinkComponents/LinkMasonry.tsx index 4dd70f1..b63019a 100644 --- a/components/LinkViews/LinkComponents/LinkMasonry.tsx +++ b/components/LinkViews/LinkComponents/LinkMasonry.tsx @@ -155,11 +155,9 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) { ) : link.preview === "unavailable" ? null : (
    )} - {link.type !== "image" && ( -
    - -
    - )} +
    + +
    {link.preview !== "unavailable" && ( diff --git a/components/ViewDropdown.tsx b/components/ViewDropdown.tsx index 2333970..a945065 100644 --- a/components/ViewDropdown.tsx +++ b/components/ViewDropdown.tsx @@ -1,7 +1,8 @@ import React, { Dispatch, SetStateAction, useEffect } from "react"; import useLocalSettingsStore from "@/store/localSettings"; - import { ViewMode } from "@/types/global"; +import { dropdownTriggerer } from "@/lib/client/utils"; +import { useTranslation } from "next-i18next"; type Props = { viewMode: ViewMode; @@ -9,53 +10,111 @@ type Props = { }; export default function ViewDropdown({ viewMode, setViewMode }: Props) { - const { updateSettings } = useLocalSettingsStore(); + const { settings, updateSettings } = useLocalSettingsStore((state) => state); + const { t } = useTranslation(); const onChangeViewMode = ( e: React.MouseEvent, - viewMode: ViewMode + mode: ViewMode ) => { - setViewMode(viewMode); + setViewMode(mode); + }; + + const toggleShowSetting = (setting: keyof typeof settings.show) => { + const newShowSettings = { + ...settings.show, + [setting]: !settings.show[setting], + }; + updateSettings({ show: newShowSettings }); }; useEffect(() => { updateSettings({ viewMode }); - }, [viewMode]); + }, [viewMode, updateSettings]); return ( -
    - - - - - + {viewMode === ViewMode.Card ? ( + + ) : viewMode === ViewMode.Masonry ? ( + + ) : ( + + )} +
    +
      +

      {t("view")}

      +
      + + + +
      +

      {t("show")}

      + {Object.entries(settings.show) + .filter((e) => + settings.viewMode === ViewMode.List // Hide tags and image checkbox in list view + ? e[0] !== "tags" && e[0] !== "image" + : settings.viewMode === ViewMode.Card // Hide tags checkbox in card view + ? e[0] !== "tags" + : true + ) + .map(([key, value]) => ( +
    • + +
    • + ))} +
    ); } diff --git a/lib/client/icons.ts b/lib/client/icons.ts index 2083bd0..e133df5 100644 --- a/lib/client/icons.ts +++ b/lib/client/icons.ts @@ -11,8 +11,8 @@ export const icons: ReadonlyArray = iconData.map((entry) => ({ Icon: Icons[entry.pascal_name as keyof typeof Icons] as Icons.Icon, })); -if (process.env.NODE_ENV === "development") { - console.log(`${icons.length} icons`); -} +// if (process.env.NODE_ENV === "development") { +// console.log(`${icons.length} icons`); +// } export const iconCount = Intl.NumberFormat("en-US").format(icons.length * 6); diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 9a9cc86..e514148 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -380,5 +380,10 @@ "duotone": "Duotone", "light_icon": "Light", "search": "Search", - "set_custom_icon": "Set Custom Icon" + "set_custom_icon": "Set Custom Icon", + "view": "View", + "show": "Show", + "image": "Image", + "icon": "Icon", + "date": "Date" } \ No newline at end of file diff --git a/store/localSettings.ts b/store/localSettings.ts index 864fa28..cd8e6ca 100644 --- a/store/localSettings.ts +++ b/store/localSettings.ts @@ -2,14 +2,24 @@ import { Sort } from "@/types/global"; import { create } from "zustand"; type LocalSettings = { - theme?: string; - viewMode?: string; + theme: string; + viewMode: string; + show: { + link: boolean; + title: boolean; + description: boolean; + image: boolean; + tags: boolean; + icon: boolean; + collection: boolean; + date: boolean; + }; sortBy?: Sort; }; type LocalSettingsStore = { settings: LocalSettings; - updateSettings: (settings: LocalSettings) => void; + updateSettings: (settings: Partial) => void; setSettings: () => void; }; @@ -17,50 +27,84 @@ const useLocalSettingsStore = create((set) => ({ settings: { theme: "", viewMode: "", + show: { + link: true, + title: true, + description: true, + image: true, + tags: true, + icon: true, + collection: true, + date: true, + }, sortBy: Sort.DateNewestFirst, }, - updateSettings: async (newSettings) => { - if ( - newSettings.theme !== undefined && - newSettings.theme !== localStorage.getItem("theme") - ) { - localStorage.setItem("theme", newSettings.theme); + updateSettings: (newSettings) => { + const { theme, viewMode, sortBy, show } = newSettings; - const localTheme = localStorage.getItem("theme") || ""; - - document.querySelector("html")?.setAttribute("data-theme", localTheme); + if (theme !== undefined && theme !== localStorage.getItem("theme")) { + localStorage.setItem("theme", theme); + document.querySelector("html")?.setAttribute("data-theme", theme); } if ( - newSettings.viewMode !== undefined && - newSettings.viewMode !== localStorage.getItem("viewMode") + viewMode !== undefined && + viewMode !== localStorage.getItem("viewMode") ) { - localStorage.setItem("viewMode", newSettings.viewMode); - - // const localTheme = localStorage.getItem("viewMode") || ""; + localStorage.setItem("viewMode", viewMode); } - if ( - newSettings.sortBy !== undefined && - newSettings.sortBy !== Number(localStorage.getItem("sortBy")) - ) { - localStorage.setItem("sortBy", newSettings.sortBy.toString()); + if (sortBy !== undefined) { + localStorage.setItem("sortBy", sortBy.toString()); } - set((state) => ({ settings: { ...state.settings, ...newSettings } })); - }, - setSettings: async () => { - if (!localStorage.getItem("theme")) { - localStorage.setItem("theme", "dark"); - } + const currentShowString = localStorage.getItem("show"); + const newShowString = show ? JSON.stringify(show) : currentShowString; - const localTheme = localStorage.getItem("theme") || ""; + if (newShowString !== currentShowString) { + localStorage.setItem("show", newShowString || ""); + } set((state) => ({ - settings: { ...state.settings, theme: localTheme }, + settings: { + ...state.settings, + ...newSettings, + show: show ? { ...state.settings.show, ...show } : state.settings.show, + }, })); + }, + setSettings: () => { + const theme = localStorage.getItem("theme") || "dark"; + localStorage.setItem("theme", theme); - document.querySelector("html")?.setAttribute("data-theme", localTheme); + const viewMode = localStorage.getItem("viewMode") || "card"; + localStorage.setItem("viewMode", viewMode); + + const storedShow = localStorage.getItem("show"); + const show = storedShow + ? JSON.parse(storedShow) + : { + link: true, + name: true, + description: true, + image: true, + tags: true, + icon: true, + collection: true, + date: true, + }; + localStorage.setItem("show", JSON.stringify(show)); + + set({ + settings: { + theme, + viewMode, + show, + sortBy: useLocalSettingsStore.getState().settings.sortBy, + }, + }); + + document.querySelector("html")?.setAttribute("data-theme", theme); }, })); From 0371695eb3635d8dd427b2538dd699a0472c6931 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Mon, 26 Aug 2024 19:56:04 -0400 Subject: [PATCH 24/58] choose to show which detail in each views --- components/Icon.tsx | 6 +- components/LinkDetails.tsx | 2 +- .../LinkViews/LinkComponents/LinkCard.tsx | 105 ++++++++-------- .../LinkViews/LinkComponents/LinkList.tsx | 35 +++--- .../LinkViews/LinkComponents/LinkMasonry.tsx | 117 +++++++++++------- components/ViewDropdown.tsx | 11 +- store/localSettings.ts | 4 +- 7 files changed, 158 insertions(+), 122 deletions(-) diff --git a/components/Icon.tsx b/components/Icon.tsx index 178b9e6..b179066 100644 --- a/components/Icon.tsx +++ b/components/Icon.tsx @@ -5,12 +5,12 @@ type Props = { icon: string; } & Icons.IconProps; -const Icon = forwardRef(({ icon, ...rest }) => { +const Icon = forwardRef(({ icon, ...rest }, ref) => { const IconComponent: any = Icons[icon as keyof typeof Icons]; if (!IconComponent) { - return <>; - } else return ; + return null; + } else return ; }); export default Icon; diff --git a/components/LinkDetails.tsx b/components/LinkDetails.tsx index 84aabe9..c9b1e59 100644 --- a/components/LinkDetails.tsx +++ b/components/LinkDetails.tsx @@ -192,7 +192,7 @@ export default function LinkDetails({ className, link }: Props) {

    {t("tags")}

    -
    +
    {link.tags.map((tag) => isPublicRoute ? (
    diff --git a/components/LinkViews/LinkComponents/LinkCard.tsx b/components/LinkViews/LinkComponents/LinkCard.tsx index 285437a..7d13b54 100644 --- a/components/LinkViews/LinkComponents/LinkCard.tsx +++ b/components/LinkViews/LinkComponents/LinkCard.tsx @@ -24,6 +24,7 @@ import { useUser } from "@/hooks/store/user"; import { useGetLink, useLinks } from "@/hooks/store/links"; import { useRouter } from "next/router"; import useLocalSettingsStore from "@/store/localSettings"; +import clsx from "clsx"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -148,64 +149,70 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) { !editMode && window.open(generateLinkHref(link, user), "_blank") } > -
    -
    - {previewAvailable(link) ? ( - { - const target = e.target as HTMLElement; - target.style.display = "none"; - }} - /> - ) : link.preview === "unavailable" ? ( -
    - ) : ( -
    - )} -
    - -
    -
    -
    -
    - -
    -
    -

    - {unescapeString(link.name)} -

    - - -
    - + {show.image && (
    -
    - -
    -
    - {collection && ( - - )} -
    - +
    + {previewAvailable(link) ? ( + { + const target = e.target as HTMLElement; + target.style.display = "none"; + }} + /> + ) : link.preview === "unavailable" ? ( +
    + ) : ( +
    + )} + {show.icon && ( +
    + +
    + )}
    +
    + )} + +
    +
    + {show.name && ( +

    + {unescapeString(link.name)} +

    + )} + + {show.link && } +
    + + {(show.collection || show.date) && ( +
    +
    + +
    + {show.collection && ( +
    + +
    + )} + {show.date && } +
    +
    + )}
    diff --git a/components/LinkViews/LinkComponents/LinkList.tsx b/components/LinkViews/LinkComponents/LinkList.tsx index d4e32c7..90baa8b 100644 --- a/components/LinkViews/LinkComponents/LinkList.tsx +++ b/components/LinkViews/LinkComponents/LinkList.tsx @@ -18,6 +18,7 @@ import { useTranslation } from "next-i18next"; import { useCollections } from "@/hooks/store/collections"; import { useUser } from "@/hooks/store/user"; import { useLinks } from "@/hooks/store/links"; +import useLocalSettingsStore from "@/store/localSettings"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -39,6 +40,10 @@ export default function LinkCardCompact({ const { data: user = {} } = useUser(); const { setSelectedLinks, selectedLinks } = useLinkStore(); + const { + settings: { show }, + } = useLocalSettingsStore(); + const { links } = useLinks(); useEffect(() => { @@ -105,33 +110,31 @@ export default function LinkCardCompact({ } >
    !editMode && window.open(generateLinkHref(link, user), "_blank") } > -
    - -
    + {show.icon && ( +
    + +
    + )}
    -

    - {link.name ? ( - unescapeString(link.name) - ) : ( -

    - -
    - )} -

    + {show.name && ( +

    + {unescapeString(link.name)} +

    + )}
    - {collection && ( + {show.link && } + {show.collection && ( )} - {link.name && } - + {show.date && }
    diff --git a/components/LinkViews/LinkComponents/LinkMasonry.tsx b/components/LinkViews/LinkComponents/LinkMasonry.tsx index b63019a..b59233d 100644 --- a/components/LinkViews/LinkComponents/LinkMasonry.tsx +++ b/components/LinkViews/LinkComponents/LinkMasonry.tsx @@ -22,6 +22,8 @@ import { useTranslation } from "next-i18next"; import { useCollections } from "@/hooks/store/collections"; import { useUser } from "@/hooks/store/user"; import { useGetLink, useLinks } from "@/hooks/store/links"; +import useLocalSettingsStore from "@/store/localSettings"; +import clsx from "clsx"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -39,6 +41,10 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) { const { setSelectedLinks, selectedLinks } = useLinkStore(); + const { + settings: { show }, + } = useLocalSettingsStore(); + const { links } = useLinks(); const getLink = useGetLink(); @@ -129,55 +135,64 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) { : undefined } > -
    - !editMode && window.open(generateLinkHref(link, user), "_blank") - } - > -
    - {previewAvailable(link) ? ( - { - const target = e.target as HTMLElement; - target.style.display = "none"; - }} - /> - ) : link.preview === "unavailable" ? null : ( -
    - )} -
    - -
    -
    +
    + {show.image && previewAvailable(link) && ( +
    + !editMode && window.open(generateLinkHref(link, user), "_blank") + } + > +
    + {previewAvailable(link) ? ( + { + const target = e.target as HTMLElement; + target.style.display = "none"; + }} + /> + ) : link.preview === "unavailable" ? null : ( +
    + )} + {show.icon && ( +
    + +
    + )} +
    - {link.preview !== "unavailable" && ( -
    +
    +
    )} -
    -

    - {unescapeString(link.name)} -

    +
    + {show.name && ( +

    + {unescapeString(link.name)} +

    + )} - + {show.link && } - {link.description && ( -

    + {show.description && link.description && ( +

    {unescapeString(link.description)}

    )} - {link.tags && link.tags[0] && ( + {show.tags && link.tags && link.tags[0] && (
    {link.tags.map((e, i) => ( -
    + {(show.collection || show.date) && ( +
    +
    -
    - {collection && } - -
    +
    + {show.collection && ( +
    + +
    + )} + {show.date && } +
    +
    + )}
    diff --git a/components/ViewDropdown.tsx b/components/ViewDropdown.tsx index a945065..f06c45d 100644 --- a/components/ViewDropdown.tsx +++ b/components/ViewDropdown.tsx @@ -89,10 +89,13 @@ export default function ViewDropdown({ viewMode, setViewMode }: Props) {

    {t("show")}

    {Object.entries(settings.show) .filter((e) => - settings.viewMode === ViewMode.List // Hide tags and image checkbox in list view - ? e[0] !== "tags" && e[0] !== "image" - : settings.viewMode === ViewMode.Card // Hide tags checkbox in card view - ? e[0] !== "tags" + settings.viewMode === ViewMode.List // Hide tags, image, icon, and description checkboxes in list view + ? e[0] !== "tags" && + e[0] !== "image" && + e[0] !== "icon" && + e[0] !== "description" + : settings.viewMode === ViewMode.Card // Hide tags and description checkboxes in card view + ? e[0] !== "tags" && e[0] !== "description" : true ) .map(([key, value]) => ( diff --git a/store/localSettings.ts b/store/localSettings.ts index cd8e6ca..9b576ce 100644 --- a/store/localSettings.ts +++ b/store/localSettings.ts @@ -6,7 +6,7 @@ type LocalSettings = { viewMode: string; show: { link: boolean; - title: boolean; + name: boolean; description: boolean; image: boolean; tags: boolean; @@ -29,7 +29,7 @@ const useLocalSettingsStore = create((set) => ({ viewMode: "", show: { link: true, - title: true, + name: true, description: true, image: true, tags: true, From 6498ae794b59f677fc7f4814c9197bcc5d87da1d Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Mon, 26 Aug 2024 21:04:52 -0400 Subject: [PATCH 25/58] custom preview initial commit --- components/Drawer.tsx | 2 +- components/Modal.tsx | 2 +- .../ModalContent/EditCollectionModal.tsx | 2 +- components/ModalContent/EditLinkModal.tsx | 74 +++++++++++++------ .../ModalContent/NewCollectionModal.tsx | 2 +- components/ModalContent/NewLinkModal.tsx | 2 +- components/ModalContent/UploadFileModal.tsx | 2 +- public/locales/en/common.json | 4 +- 8 files changed, 61 insertions(+), 29 deletions(-) diff --git a/components/Drawer.tsx b/components/Drawer.tsx index 12f931e..07673fb 100644 --- a/components/Drawer.tsx +++ b/components/Drawer.tsx @@ -40,7 +40,7 @@ export default function Drawer({ dismissible && setDrawerIsOpen(false)} > - +
    dismissible && setDrawerIsOpen(false)} > - +

    {t("description")}