From dbd096ab76edca485f4e03b9c8c772fc3bab964e Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Sun, 3 Nov 2024 03:27:52 -0500 Subject: [PATCH] Revert "undo commit" This reverts commit 9103f67db5e80b318196e068f203939ca869ba89. --- .env.sample | 2 - .github/workflows/playwright-tests.yml | 6 +- .github/workflows/release-container.yml | 4 +- Dockerfile | 44 +- components/CollectionCard.tsx | 50 +- components/CollectionListing.tsx | 35 +- components/CopyButton.tsx | 32 - components/DashboardItem.tsx | 2 +- components/Drawer.tsx | 88 --- components/Dropdown.tsx | 82 ++- components/Icon.tsx | 18 - components/IconGrid.tsx | 49 -- components/IconPicker.tsx | 83 --- components/IconPopover.tsx | 142 ---- .../InputSelect/CollectionSelection.tsx | 10 +- components/InputSelect/TagSelection.tsx | 14 +- components/InputSelect/styles.ts | 21 +- components/LinkDetails.tsx | 663 ------------------ .../LinkViews/LinkComponents/LinkActions.tsx | 274 ++++---- .../LinkViews/LinkComponents/LinkCard.tsx | 205 +++--- .../LinkComponents/LinkCollection.tsx | 19 +- .../LinkViews/LinkComponents/LinkIcon.tsx | 70 +- .../LinkViews/LinkComponents/LinkList.tsx | 84 ++- .../LinkViews/LinkComponents/LinkMasonry.tsx | 210 +++--- .../LinkViews/LinkComponents/LinkPin.tsx | 34 - components/LinkViews/Links.tsx | 144 +--- components/MobileNavigation.tsx | 12 +- components/Modal.tsx | 4 +- .../ModalContent/BulkEditLinksModal.tsx | 3 +- .../ModalContent/DeleteCollectionModal.tsx | 3 +- components/ModalContent/DeleteUserModal.tsx | 30 +- .../ModalContent/EditCollectionModal.tsx | 63 +- .../EditCollectionSharingModal.tsx | 62 +- components/ModalContent/EditLinkModal.tsx | 154 ++++ components/ModalContent/InviteModal.tsx | 125 ---- components/ModalContent/LinkModal.tsx | 183 ----- .../ModalContent/NewCollectionModal.tsx | 63 +- components/ModalContent/NewLinkModal.tsx | 56 +- components/ModalContent/NewTokenModal.tsx | 27 +- components/ModalContent/NewUserModal.tsx | 12 +- .../ModalContent/PreservedFormatsModal.tsx | 248 +++++++ components/ModalContent/UploadFileModal.tsx | 50 +- components/Navbar.tsx | 16 +- components/NoLinksFound.tsx | 4 +- components/Popover.tsx | 21 - components/PreserverdFormatRow.tsx | 15 +- components/ProfileDropdown.tsx | 19 +- components/ProfilePhoto.tsx | 2 +- components/ReadableView.tsx | 58 +- components/SettingsSidebar.tsx | 5 +- components/ToggleDarkMode.tsx | 20 +- components/UserListing.tsx | 4 +- components/ViewDropdown.tsx | 182 ++--- components/ui/Divider.tsx | 12 - e2e/data/user.ts | 14 +- hooks/store/admin/users.tsx | 5 + hooks/store/dashboardData.tsx | 7 +- hooks/store/links.tsx | 144 +--- hooks/store/publicTags.tsx | 30 - hooks/store/tokens.tsx | 3 - layouts/AuthRedirect.tsx | 7 +- layouts/CenteredForm.tsx | 8 +- layouts/MainLayout.tsx | 4 +- layouts/SettingsLayout.tsx | 4 +- lib/api/checkSubscriptionByEmail.ts | 53 ++ .../collectionId/updateCollectionById.ts | 40 +- .../controllers/collections/postCollection.ts | 35 +- .../controllers/dashboard/getDashboardData.ts | 4 +- .../dashboard/getDashboardDataV2.ts | 17 +- lib/api/controllers/links/bulk/updateLinks.ts | 5 +- lib/api/controllers/links/getLinks.ts | 4 +- .../links/linkId/updateLinkById.ts | 79 +-- lib/api/controllers/links/postLink.ts | 49 +- lib/api/controllers/migration/exportData.ts | 13 + .../migration/importFromHTMLFile.ts | 71 +- .../migration/importFromLinkwarden.ts | 50 +- .../migration/importFromWallabag.ts | 45 +- .../links/getPublicLinksUnderCollection.ts | 4 +- .../controllers/public/users/getPublicUser.ts | 16 +- lib/api/controllers/session/createSession.ts | 2 +- lib/api/controllers/tags/getTags.ts | 30 +- .../controllers/tags/tagId/updeteTagById.ts | 25 +- lib/api/controllers/tokens/postToken.ts | 34 +- lib/api/controllers/users/getUsers.ts | 78 +-- lib/api/controllers/users/postUser.ts | 155 ++-- .../users/userId/deleteUserById.ts | 153 ++-- .../controllers/users/userId/getUserById.ts | 15 +- .../users/userId/updateUserById.ts | 89 +-- lib/api/db.ts | 2 +- lib/api/generatePreview.ts | 2 +- ...thenticatedRequest.ts => isServerAdmin.ts} | 19 +- lib/api/paymentCheckout.ts | 22 +- lib/api/sendInvitationRequest.ts | 56 -- lib/api/setLinkCollection.ts | 13 +- lib/api/storage/moveFile.ts | 2 +- lib/api/stripe/checkSubscriptionByEmail.ts | 31 - lib/api/stripe/handleSubscription.ts | 91 --- lib/api/stripe/updateSeats.ts | 27 - lib/api/stripe/verifySubscription.ts | 70 -- lib/api/{stripe => }/updateCustomerEmail.ts | 0 lib/api/verifyByCredentials.ts | 3 +- lib/api/verifyCapacity.ts | 76 -- lib/api/verifySubscription.ts | 73 ++ lib/api/verifyUser.ts | 3 +- lib/client/addMemberToCollection.ts | 25 +- lib/client/icons.ts | 18 - lib/client/pinLink.ts | 47 -- lib/client/resizeImage.ts | 2 +- lib/client/utils.ts | 9 +- lib/shared/getArchiveValidity.ts | 4 +- lib/shared/schemaValidation.ts | 210 ------ next-i18next.config.js | 15 +- package.json | 10 +- pages/_app.tsx | 7 +- pages/admin.tsx | 7 +- pages/api/v1/archives/[linkId].ts | 126 +--- pages/api/v1/auth/[...nextauth].ts | 106 +-- pages/api/v1/auth/forgot-password.ts | 15 +- pages/api/v1/auth/reset-password.ts | 16 +- pages/api/v1/auth/verify-email.ts | 13 +- pages/api/v1/links/[id]/archive/index.ts | 49 +- pages/api/v1/links/index.ts | 1 - pages/api/v1/public/collections/tags/index.ts | 42 -- pages/api/v1/session/index.ts | 13 +- pages/api/v1/tags/[id].ts | 5 - pages/api/v1/tags/index.ts | 4 +- pages/api/v1/tokens/index.ts | 2 +- pages/api/v1/users/[id].ts | 27 +- pages/api/v1/users/index.ts | 5 +- pages/api/v1/webhook/index.ts | 119 ---- pages/collections/[id].tsx | 44 +- pages/collections/index.tsx | 46 +- pages/dashboard.tsx | 121 ++-- pages/links/[id].tsx | 42 -- pages/login.tsx | 10 +- pages/member-onboarding.tsx | 150 ---- pages/preserved/[id].tsx | 2 +- pages/public/collections/[id].tsx | 363 ++++------ pages/public/links/[id].tsx | 40 -- pages/public/preserved/[id].tsx | 17 +- pages/register.tsx | 22 +- pages/settings/access-tokens.tsx | 20 +- pages/settings/account.tsx | 48 +- pages/settings/billing.tsx | 228 +----- pages/settings/delete.tsx | 6 +- pages/settings/password.tsx | 3 +- pages/settings/preference.tsx | 3 +- pages/subscribe.tsx | 17 +- pages/tags/[id].tsx | 50 +- playwright.config.ts | 2 +- .../migration.sql | 8 - .../migration.sql | 3 - .../migration.sql | 9 - .../migration.sql | 2 - .../migration.sql | 56 -- .../migration.sql | 37 - .../20241027093300_remove_field/migration.sql | 11 - .../20241027104510_remove_field/migration.sql | 8 - .../migration.sql | 17 - prisma/schema.prisma | 21 +- public/locales/de/common.json | 420 ----------- public/locales/en/common.json | 55 +- public/locales/es/common.json | 375 ---------- public/locales/it/common.json | 3 +- public/locales/ja/common.json | 397 ----------- public/locales/nl/common.json | 375 ---------- public/locales/pt-BR/common.json | 400 ----------- public/locales/tr/common.json | 377 ---------- public/locales/uk/common.json | 397 ----------- public/locales/zh-TW/common.json | 374 ---------- scripts/worker.ts | 4 +- store/localSettings.ts | 114 +-- styles/globals.css | 38 +- templates/acceptInvitation.html | 445 ------------ types/global.ts | 5 +- yarn.lock | 61 +- 176 files changed, 2362 insertions(+), 9401 deletions(-) delete mode 100644 components/CopyButton.tsx delete mode 100644 components/Drawer.tsx delete mode 100644 components/Icon.tsx delete mode 100644 components/IconGrid.tsx delete mode 100644 components/IconPicker.tsx delete mode 100644 components/IconPopover.tsx delete mode 100644 components/LinkDetails.tsx delete mode 100644 components/LinkViews/LinkComponents/LinkPin.tsx create mode 100644 components/ModalContent/EditLinkModal.tsx delete mode 100644 components/ModalContent/InviteModal.tsx delete mode 100644 components/ModalContent/LinkModal.tsx create mode 100644 components/ModalContent/PreservedFormatsModal.tsx delete mode 100644 components/Popover.tsx delete mode 100644 components/ui/Divider.tsx delete mode 100644 hooks/store/publicTags.tsx create mode 100644 lib/api/checkSubscriptionByEmail.ts rename lib/api/{isAuthenticatedRequest.ts => isServerAdmin.ts} (69%) delete mode 100644 lib/api/sendInvitationRequest.ts delete mode 100644 lib/api/stripe/checkSubscriptionByEmail.ts delete mode 100644 lib/api/stripe/handleSubscription.ts delete mode 100644 lib/api/stripe/updateSeats.ts delete mode 100644 lib/api/stripe/verifySubscription.ts rename lib/api/{stripe => }/updateCustomerEmail.ts (100%) delete mode 100644 lib/api/verifyCapacity.ts create mode 100644 lib/api/verifySubscription.ts delete mode 100644 lib/client/icons.ts delete mode 100644 lib/client/pinLink.ts delete mode 100644 lib/shared/schemaValidation.ts delete mode 100644 pages/api/v1/public/collections/tags/index.ts delete mode 100644 pages/api/v1/webhook/index.ts delete mode 100644 pages/links/[id].tsx delete mode 100644 pages/member-onboarding.tsx delete mode 100644 pages/public/links/[id].tsx delete mode 100644 prisma/migrations/20240819003838_add_icon_related_fields_to_link_and_collection_model/migration.sql delete mode 100644 prisma/migrations/20240819005010_remove_collection_model_default_color/migration.sql delete mode 100644 prisma/migrations/20240820230618_add_default_value_for_collection_color/migration.sql delete mode 100644 prisma/migrations/20240924235035_add_quantity_to_subscriptions/migration.sql delete mode 100644 prisma/migrations/20241021175802_add_child_subscription_support/migration.sql delete mode 100644 prisma/migrations/20241026161909_assign_createdby_to_collection_owners_and_make_field_required/migration.sql delete mode 100644 prisma/migrations/20241027093300_remove_field/migration.sql delete mode 100644 prisma/migrations/20241027104510_remove_field/migration.sql delete mode 100644 prisma/migrations/20241030200844_createdby_fields_can_be_null/migration.sql delete mode 100644 public/locales/de/common.json delete mode 100644 public/locales/es/common.json delete mode 100644 public/locales/ja/common.json delete mode 100644 public/locales/nl/common.json delete mode 100644 public/locales/pt-BR/common.json delete mode 100644 public/locales/tr/common.json delete mode 100644 public/locales/uk/common.json delete mode 100644 public/locales/zh-TW/common.json delete mode 100644 templates/acceptInvitation.html diff --git a/.env.sample b/.env.sample index 775b05d7..3cedb1df 100644 --- a/.env.sample +++ b/.env.sample @@ -35,8 +35,6 @@ READABILITY_MAX_BUFFER= PREVIEW_MAX_BUFFER= IMPORT_LIMIT= PLAYWRIGHT_LAUNCH_OPTIONS_EXECUTABLE_PATH= -MAX_WORKERS= -DISABLE_INVITES= # AWS S3 Settings SPACES_KEY= diff --git a/.github/workflows/playwright-tests.yml b/.github/workflows/playwright-tests.yml index 4edd49f9..2d226af1 100644 --- a/.github/workflows/playwright-tests.yml +++ b/.github/workflows/playwright-tests.yml @@ -59,10 +59,10 @@ jobs: --health-retries 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v2 - name: Use Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v3 with: node-version: "18" cache: 'yarn' @@ -135,7 +135,7 @@ jobs: - name: Run Tests run: npx playwright test --grep ${{ matrix.test_case }} - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v3 if: always() with: name: playwright-report diff --git a/.github/workflows/release-container.yml b/.github/workflows/release-container.yml index 5f897455..e9949132 100644 --- a/.github/workflows/release-container.yml +++ b/.github/workflows/release-container.yml @@ -27,7 +27,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Log in to the Container registry - uses: docker/login-action@v3 + uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -40,7 +40,7 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v3 with: context: . push: true diff --git a/Dockerfile b/Dockerfile index d999194b..d51b65d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,4 @@ -# Stage: monolith-builder -# Purpose: Uses the Rust image to build monolith -# Notes: -# - Fine to leave extra here, as only the resulting binary is copied out -FROM docker.io/rust:1.80-bullseye AS monolith-builder - -RUN set -eux && cargo install --locked monolith - -# Stage: main-app -# Purpose: Compiles the frontend and -# Notes: -# - Nothing extra should be left here. All commands should cleanup -FROM node:18.18-bullseye-slim AS main-app +FROM node:18.18-bullseye-slim ARG DEBIAN_FRONTEND=noninteractive @@ -20,23 +8,33 @@ WORKDIR /data COPY ./package.json ./yarn.lock ./playwright.config.ts ./ -RUN --mount=type=cache,sharing=locked,target=/usr/local/share/.cache/yarn \ - set -eux && \ - yarn install --network-timeout 10000000 +RUN --mount=type=cache,sharing=locked,target=/usr/local/share/.cache/yarn yarn install --network-timeout 10000000 -# Copy the compiled monolith binary from the builder stage -COPY --from=monolith-builder /usr/local/cargo/bin/monolith /usr/local/bin/monolith +RUN apt-get update -RUN set -eux && \ - npx playwright install --with-deps chromium && \ +RUN apt-get install -y \ + build-essential \ + curl \ + libssl-dev \ + pkg-config + +RUN apt-get update + +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y + +ENV PATH="/root/.cargo/bin:${PATH}" + +RUN cargo install monolith + +RUN npx playwright install-deps && \ apt-get clean && \ yarn cache clean +RUN yarn playwright install + COPY . . RUN yarn prisma generate && \ yarn build -EXPOSE 3000 - -CMD yarn prisma migrate deploy && yarn start +CMD yarn prisma migrate deploy && yarn start \ No newline at end of file diff --git a/components/CollectionCard.tsx b/components/CollectionCard.tsx index 4ff96774..432425c2 100644 --- a/components/CollectionCard.tsx +++ b/components/CollectionCard.tsx @@ -1,8 +1,5 @@ import Link from "next/link"; -import { - AccountSettings, - CollectionIncludingMembersAndLinkCount, -} from "@/types/global"; +import { CollectionIncludingMembersAndLinkCount } from "@/types/global"; import React, { useEffect, useState } from "react"; import ProfilePhoto from "./ProfilePhoto"; import usePermissions from "@/hooks/usePermissions"; @@ -15,11 +12,12 @@ import { dropdownTriggerer } from "@/lib/client/utils"; import { useTranslation } from "next-i18next"; import { useUser } from "@/hooks/store/user"; -export default function CollectionCard({ - collection, -}: { +type Props = { collection: CollectionIncludingMembersAndLinkCount; -}) { + className?: string; +}; + +export default function CollectionCard({ collection, className }: Props) { const { t } = useTranslation(); const { settings } = useLocalSettingsStore(); const { data: user = {} } = useUser(); @@ -35,9 +33,15 @@ export default function CollectionCard({ const permissions = usePermissions(collection.id as number); - const [collectionOwner, setCollectionOwner] = useState< - Partial - >({}); + 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, + }); useEffect(() => { const fetchOwner = async () => { @@ -128,12 +132,12 @@ export default function CollectionCard({ 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) => { @@ -147,13 +151,13 @@ export default function CollectionCard({ ); }) .slice(0, 3)} - {collection.members.length - 3 > 0 && ( + {collection.members.length - 3 > 0 ? (
+{collection.members.length - 3}
- )} + ) : null}
- {collection.isPublic && ( + {collection.isPublic ? ( - )} + ) : undefined}
- {editCollectionModal && ( + {editCollectionModal ? ( setEditCollectionModal(false)} activeCollection={collection} /> - )} - {editCollectionSharingModal && ( + ) : undefined} + {editCollectionSharingModal ? ( setEditCollectionSharingModal(false)} activeCollection={collection} /> - )} - {deleteCollectionModal && ( + ) : undefined} + {deleteCollectionModal ? ( setDeleteCollectionModal(false)} activeCollection={collection} /> - )} + ) : undefined}
); } diff --git a/components/CollectionListing.tsx b/components/CollectionListing.tsx index 3ff24151..e83d388a 100644 --- a/components/CollectionListing.tsx +++ b/components/CollectionListing.tsx @@ -17,8 +17,6 @@ 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; @@ -45,7 +43,6 @@ const CollectionListing = () => { return buildTreeFromCollections( collections, router, - tree, user.collectionOrder ); } else return undefined; @@ -259,7 +256,7 @@ const renderItem = ( : "hover:bg-neutral/20" } duration-100 flex gap-1 items-center pr-2 pl-1 rounded-md`} > - {Dropdown(item as ExtendedTreeItem, onExpand, onCollapse)} + {Icon(item as ExtendedTreeItem, onExpand, onCollapse)} - {collection.icon ? ( - - ) : ( - - )} - +

{collection.name}

- {collection.isPublic && ( + {collection.isPublic ? ( - )} + ) : undefined}
{collection._count?.links}
@@ -302,7 +288,7 @@ const renderItem = ( ); }; -const Dropdown = ( +const Icon = ( item: ExtendedTreeItem, onExpand: (id: ItemId) => void, onCollapse: (id: ItemId) => void @@ -325,7 +311,6 @@ const Dropdown = ( const buildTreeFromCollections = ( collections: CollectionIncludingMembersAndLinkCount[], router: ReturnType, - tree?: TreeData, order?: number[] ): TreeData => { if (order) { @@ -340,15 +325,13 @@ const buildTreeFromCollections = ( id: collection.id, children: [], hasChildren: false, - isExpanded: tree?.items[collection.id as number]?.isExpanded || false, + isExpanded: false, data: { id: collection.id, parentId: collection.parentId, 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/CopyButton.tsx b/components/CopyButton.tsx deleted file mode 100644 index b9494758..00000000 --- a/components/CopyButton.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { useState } from "react"; - -type Props = { - text: string; -}; - -const CopyButton: React.FC = ({ text }) => { - const [copied, setCopied] = useState(false); - - const handleCopy = async () => { - try { - await navigator.clipboard.writeText(text); - setCopied(true); - setTimeout(() => { - setCopied(false); - }, 1000); - } catch (err) { - console.log(err); - } - }; - - return ( -
- ); -}; - -export default CopyButton; diff --git a/components/DashboardItem.tsx b/components/DashboardItem.tsx index efc1303a..337fc367 100644 --- a/components/DashboardItem.tsx +++ b/components/DashboardItem.tsx @@ -14,7 +14,7 @@ export default function dashboardItem({

{name}

-

{value || 0}

+

{value}

); diff --git a/components/Drawer.tsx b/components/Drawer.tsx deleted file mode 100644 index 49cd9eb2..00000000 --- a/components/Drawer.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, { ReactNode, useEffect } from "react"; -import ClickAwayHandler from "@/components/ClickAwayHandler"; -import { Drawer as D } from "vaul"; - -type Props = { - toggleDrawer: Function; - children: ReactNode; - className?: string; - dismissible?: boolean; -}; - -export default function Drawer({ - toggleDrawer, - className, - children, - dismissible = true, -}: Props) { - const [drawerIsOpen, setDrawerIsOpen] = React.useState(true); - - useEffect(() => { - if (window.innerWidth >= 640) { - document.body.style.overflow = "hidden"; - document.body.style.position = "relative"; - return () => { - document.body.style.overflow = "auto"; - document.body.style.position = ""; - }; - } - }, []); - - if (window.innerWidth < 640) { - return ( - dismissible && setTimeout(() => toggleDrawer(), 350)} - dismissible={dismissible} - > - - - dismissible && setDrawerIsOpen(false)} - > - -
-
- {children} -
- - - - - ); - } else { - return ( - dismissible && setTimeout(() => toggleDrawer(), 350)} - dismissible={dismissible} - direction="right" - > - - - dismissible && setDrawerIsOpen(false)} - className="z-30" - > - -
- {children} -
-
-
-
-
- ); - } -} diff --git a/components/Dropdown.tsx b/components/Dropdown.tsx index 90371d8e..4a48ab76 100644 --- a/components/Dropdown.tsx +++ b/components/Dropdown.tsx @@ -60,49 +60,47 @@ export default function Dropdown({ } }, [points, dropdownHeight]); - return ( - (!points || pos) && ( - { - 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 && ( -
-
-

{e.name}

-
+ return !points || pos ? ( + { + 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 && ( +
+
+

{e.name}

- ); +
+ ); - return e && e.href ? ( - + return e && e.href ? ( + + {inner} + + ) : ( + e && ( +
{inner} - - ) : ( - e && ( -
- {inner} -
- ) - ); - })} - - ) - ); +
+ ) + ); + })} +
+ ) : null; } diff --git a/components/Icon.tsx b/components/Icon.tsx deleted file mode 100644 index 1830f0ba..00000000 --- a/components/Icon.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, { forwardRef } from "react"; -import * as Icons from "@phosphor-icons/react"; - -type Props = { - icon: string; -} & Icons.IconProps; - -const Icon = forwardRef(({ icon, ...rest }, ref) => { - const IconComponent: any = Icons[icon as keyof typeof Icons]; - - if (!IconComponent) { - return null; - } else return ; -}); - -Icon.displayName = "Icon"; - -export default Icon; diff --git a/components/IconGrid.tsx b/components/IconGrid.tsx deleted file mode 100644 index b6333a72..00000000 --- a/components/IconGrid.tsx +++ /dev/null @@ -1,49 +0,0 @@ -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 deleted file mode 100644 index 82643a8c..00000000 --- a/components/IconPicker.tsx +++ /dev/null @@ -1,83 +0,0 @@ -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"; -import IconPopover from "./IconPopover"; -import clsx from "clsx"; - -type Props = { - 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; -}; - -const IconPicker = ({ - alignment, - color, - setColor, - iconName, - setIconName, - weight, - setWeight, - hideDefaultIcon, - className, - reset, -}: Props) => { - const { t } = useTranslation(); - const [iconPicker, setIconPicker] = useState(false); - - return ( -
-
setIconPicker(!iconPicker)} - className="btn btn-square w-20 h-20" - > - {iconName ? ( - - ) : !iconName && hideDefaultIcon ? ( -

{t("set_custom_icon")}

- ) : ( - - )} -
- {iconPicker && ( - setIconPicker(false)} - className={clsx( - className, - alignment || "lg:-translate-x-1/3 top-20 left-0" - )} - /> - )} -
- ); -}; - -export default IconPicker; diff --git a/components/IconPopover.tsx b/components/IconPopover.tsx deleted file mode 100644 index cdbe8f62..00000000 --- a/components/IconPopover.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import React, { useState } from "react"; -import TextInput from "./TextInput"; -import Popover from "./Popover"; -import { HexColorPicker } from "react-colorful"; -import { useTranslation } from "next-i18next"; -import IconGrid from "./IconGrid"; -import clsx from "clsx"; - -type Props = { - alignment?: string; - color: string; - setColor: Function; - iconName?: string; - setIconName: Function; - weight: "light" | "regular" | "bold" | "fill" | "duotone" | "thin"; - setWeight: Function; - reset: Function; - className?: string; - onClose: Function; -}; - -const IconPopover = ({ - alignment, - color, - setColor, - iconName, - setIconName, - weight, - setWeight, - reset, - className, - onClose, -}: Props) => { - const { t } = useTranslation(); - const [query, setQuery] = useState(""); - - return ( - onClose()} - className={clsx( - className, - "fade-in bg-base-200 border border-neutral-content p-2 w-[22.5rem] rounded-lg shadow-md" - )} - > -
-
- setQuery(e.target.value)} - /> - -
- -
- -
- setColor(e)} /> - -
- - - - - - -
-
-
} - > - {t("reset_defaults")} -
-
-
-
- ); -}; - -export default IconPopover; diff --git a/components/InputSelect/CollectionSelection.tsx b/components/InputSelect/CollectionSelection.tsx index f53df37f..81bef390 100644 --- a/components/InputSelect/CollectionSelection.tsx +++ b/components/InputSelect/CollectionSelection.tsx @@ -16,8 +16,6 @@ type Props = { } | undefined; creatable?: boolean; - autoFocus?: boolean; - onBlur?: any; }; export default function CollectionSelection({ @@ -25,8 +23,6 @@ export default function CollectionSelection({ defaultValue, showDefaultValue = true, creatable = true, - autoFocus, - onBlur, }: Props) { const { data: collections = [] } = useCollections(); @@ -80,7 +76,7 @@ export default function CollectionSelection({ return (
{data.label} @@ -108,8 +104,6 @@ export default function CollectionSelection({ onChange={onChange} options={options} styles={styles} - autoFocus={autoFocus} - onBlur={onBlur} defaultValue={showDefaultValue ? defaultValue : null} components={{ Option: customOption, @@ -126,9 +120,7 @@ export default function CollectionSelection({ onChange={onChange} options={options} styles={styles} - autoFocus={autoFocus} defaultValue={showDefaultValue ? defaultValue : null} - onBlur={onBlur} components={{ Option: customOption, }} diff --git a/components/InputSelect/TagSelection.tsx b/components/InputSelect/TagSelection.tsx index cf03e6f0..efd246b7 100644 --- a/components/InputSelect/TagSelection.tsx +++ b/components/InputSelect/TagSelection.tsx @@ -7,19 +7,12 @@ import { useTags } from "@/hooks/store/tags"; type Props = { onChange: any; defaultValue?: { - value?: number; + value: number; label: string; }[]; - autoFocus?: boolean; - onBlur?: any; }; -export default function TagSelection({ - onChange, - defaultValue, - autoFocus, - onBlur, -}: Props) { +export default function TagSelection({ onChange, defaultValue }: Props) { const { data: tags = [] } = useTags(); const [options, setOptions] = useState([]); @@ -41,9 +34,8 @@ export default function TagSelection({ options={options} styles={styles} defaultValue={defaultValue} + // menuPosition="fixed" isMulti - autoFocus={autoFocus} - onBlur={onBlur} /> ); } diff --git a/components/InputSelect/styles.ts b/components/InputSelect/styles.ts index f05f4a51..96aad6d9 100644 --- a/components/InputSelect/styles.ts +++ b/components/InputSelect/styles.ts @@ -14,7 +14,7 @@ export const styles: StylesConfig = { ? "oklch(var(--p))" : "oklch(var(--nc))", }, - transition: "all 100ms", + transition: "all 50ms", }), control: (styles, state) => ({ ...styles, @@ -50,28 +50,19 @@ export const styles: StylesConfig = { multiValue: (styles) => { return { ...styles, - backgroundColor: "oklch(var(--b2))", - color: "oklch(var(--bc))", - display: "flex", - alignItems: "center", - gap: "0.1rem", - marginRight: "0.4rem", + backgroundColor: "#0ea5e9", + color: "white", }; }, multiValueLabel: (styles) => ({ ...styles, - color: "oklch(var(--bc))", + color: "white", }), multiValueRemove: (styles) => ({ ...styles, - height: "1.2rem", - width: "1.2rem", - borderRadius: "100px", - transition: "all 100ms", - color: "oklch(var(--w))", ":hover": { - color: "red", - backgroundColor: "oklch(var(--nc))", + color: "white", + backgroundColor: "#38bdf8", }, }), menuPortal: (base) => ({ ...base, zIndex: 9999 }), diff --git a/components/LinkDetails.tsx b/components/LinkDetails.tsx deleted file mode 100644 index e7450553..00000000 --- a/components/LinkDetails.tsx +++ /dev/null @@ -1,663 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { - LinkIncludingShortenedCollectionAndTags, - ArchivedFormat, -} from "@/types/global"; -import Link from "next/link"; -import { - pdfAvailable, - readabilityAvailable, - monolithAvailable, - screenshotAvailable, - previewAvailable, -} from "@/lib/shared/getArchiveValidity"; -import PreservedFormatRow from "@/components/PreserverdFormatRow"; -import getPublicUserData from "@/lib/client/getPublicUserData"; -import { useTranslation } from "next-i18next"; -import { BeatLoader } from "react-spinners"; -import { useUser } from "@/hooks/store/user"; -import { - useGetLink, - useUpdateLink, - useUpdatePreview, -} 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"; -import Image from "next/image"; -import clsx from "clsx"; -import toast from "react-hot-toast"; -import CollectionSelection from "./InputSelect/CollectionSelection"; -import TagSelection from "./InputSelect/TagSelection"; -import unescapeString from "@/lib/client/unescapeString"; -import IconPopover from "./IconPopover"; -import TextInput from "./TextInput"; -import usePermissions from "@/hooks/usePermissions"; - -type Props = { - className?: string; - activeLink: LinkIncludingShortenedCollectionAndTags; - standalone?: boolean; - mode?: "view" | "edit"; - setMode?: Function; -}; - -export default function LinkDetails({ - className, - activeLink, - standalone, - mode = "view", - setMode, -}: Props) { - const [link, setLink] = - useState(activeLink); - - useEffect(() => { - setLink(activeLink); - }, [activeLink]); - - const permissions = usePermissions(link.collection.id as number); - - const { t } = useTranslation(); - const getLink = useGetLink(); - const { data: user = {} } = useUser(); - - 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, - }); - - useEffect(() => { - const fetchOwner = async () => { - if (link.collection.ownerId !== user.id) { - const owner = await getPublicUserData( - link.collection.ownerId as number - ); - setCollectionOwner(owner); - } else if (link.collection.ownerId === user.id) { - setCollectionOwner({ - id: user.id as number, - name: user.name, - username: user.username as string, - image: user.image as string, - archiveAsScreenshot: user.archiveAsScreenshot as boolean, - archiveAsMonolith: user.archiveAsScreenshot as boolean, - archiveAsPDF: user.archiveAsPDF as boolean, - }); - } - }; - - fetchOwner(); - }, [link.collection.ownerId]); - - const isReady = () => { - return ( - link && - (collectionOwner.archiveAsScreenshot === true - ? link.pdf && link.pdf !== "pending" - : true) && - (collectionOwner.archiveAsMonolith === true - ? link.monolith && link.monolith !== "pending" - : true) && - (collectionOwner.archiveAsPDF === true - ? link.pdf && link.pdf !== "pending" - : true) && - link.readable && - link.readable !== "pending" - ); - }; - - const atLeastOneFormatAvailable = () => { - return ( - screenshotAvailable(link) || - pdfAvailable(link) || - readabilityAvailable(link) || - monolithAvailable(link) - ); - }; - - useEffect(() => { - (async () => { - await getLink.mutateAsync({ - id: link.id as number, - }); - })(); - - let interval: any; - - if (!isReady()) { - interval = setInterval(async () => { - await getLink.mutateAsync({ - id: link.id as number, - }); - }, 5000); - } else { - if (interval) { - clearInterval(interval); - } - } - - return () => { - if (interval) { - clearInterval(interval); - } - }; - }, [link.monolith]); - - const router = useRouter(); - - const isPublicRoute = router.pathname.startsWith("/public") ? true : false; - - const updateLink = useUpdateLink(); - const updatePreview = useUpdatePreview(); - - const submit = async (e?: any) => { - e?.preventDefault(); - - const { updatedAt: b, ...oldLink } = activeLink; - const { updatedAt: a, ...newLink } = link; - - if (JSON.stringify(oldLink) === JSON.stringify(newLink)) { - return; - } - - const load = toast.loading(t("updating")); - - await updateLink.mutateAsync(link, { - onSettled: (data, error) => { - toast.dismiss(load); - - if (error) { - toast.error(error.message); - } else { - toast.success(t("updated")); - setMode && setMode("view"); - setLink(data); - } - }, - }); - }; - - const setCollection = (e: any) => { - if (e?.__isNew__) e.value = null; - setLink({ - ...link, - collection: { id: e?.value, name: e?.label, ownerId: e?.ownerId }, - }); - }; - - const setTags = (e: any) => { - const tagNames = e.map((e: any) => ({ name: e.label })); - setLink({ ...link, tags: tagNames }); - }; - - const [iconPopover, setIconPopover] = useState(false); - - return ( -
-
-
- {previewAvailable(link) ? ( - { - const target = e.target as HTMLElement; - target.style.display = "none"; - }} - /> - ) : link.preview === "unavailable" ? ( -
- ) : ( -
- )} - - {!standalone && (permissions === true || permissions?.canUpdate) && ( -
- -
- )} -
- - {!standalone && (permissions === true || permissions?.canUpdate) ? ( -
-
- setIconPopover(true)} - /> -
- {iconPopover && ( - 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: "", - }) - } - className="top-12" - onClose={() => { - setIconPopover(false); - submit(); - }} - /> - )} -
- ) : ( -
- setIconPopover(true)} /> -
- )} - -
- {mode === "view" && ( -
-

- {link.name || t("untitled")} -

-
- )} - - {mode === "edit" && ( - <> -
- -
-

- {t("name")} -

- setLink({ ...link, name: e.target.value })} - placeholder={t("placeholder_example_link")} - className="bg-base-200" - /> -
- - )} - - {link.url && mode === "view" ? ( - <> -
- -

{t("link")}

- -
-
- - {link.url} - -
- -
-
-
- - ) : activeLink.url ? ( - <> -
- -
-

- {t("link")} -

- setLink({ ...link, url: e.target.value })} - placeholder={t("placeholder_example_link")} - className="bg-base-200" - /> -
- - ) : undefined} - -
- -
-

- {t("collection")} -

- - {mode === "view" ? ( -
- -

{link.collection.name}

-
- {link.collection.icon ? ( - - ) : ( - - )} -
- -
- ) : ( - - )} -
- -
- -
-

- {t("tags")} -

- - {mode === "view" ? ( -
- {link.tags && link.tags[0] ? ( - link.tags.map((tag) => - isPublicRoute ? ( -
- {tag.name} -
- ) : ( - - {tag.name} - - ) - ) - ) : ( -
{t("no_tags")}
- )} -
- ) : ( - ({ - label: e.name, - value: e.id, - }))} - /> - )} -
- -
- -
-

- {t("description")} -

- - {mode === "view" ? ( -
- {link.description ? ( -

{link.description}

- ) : ( -

{t("no_description_provided")}

- )} -
- ) : ( -