Merge pull request #1424 from linkwarden/dev

Dev
This commit is contained in:
Daniel
2025-09-26 14:29:11 -04:00
committed by GitHub
17 changed files with 278 additions and 91 deletions

View File

@@ -24,10 +24,17 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useUser } from "@linkwarden/router/user";
import Link from "next/link";
const STRIPE_ENABLED = process.env.NEXT_PUBLIC_STRIPE === "true";
const TRIAL_PERIOD_DAYS =
Number(process.env.NEXT_PUBLIC_TRIAL_PERIOD_DAYS) || 14;
export default function Navbar() {
const { t } = useTranslation();
const router = useRouter();
const { data: user } = useUser();
const [sidebar, setSidebar] = useState(false);
@@ -50,87 +57,126 @@ export default function Navbar() {
const [newCollectionModal, setNewCollectionModal] = useState(false);
const [uploadFileModal, setUploadFileModal] = useState(false);
const [daysLeft, setDaysLeft] = useState<number>(0);
const [isTrialling, setIsTrialling] = useState<boolean>(false);
useEffect(() => {
if (user?.createdAt) {
const trialEndTime =
new Date(user.createdAt).getTime() +
(1 + Number(TRIAL_PERIOD_DAYS)) * 86400000; // Add 1 to account for the current day
setDaysLeft(Math.floor((trialEndTime - Date.now()) / 86400000));
}
}, [user]);
useEffect(() => {
const isTrialling =
user?.id &&
!user?.subscription?.active &&
!user.parentSubscription?.active;
setIsTrialling(Boolean(isTrialling));
}, [user, daysLeft]);
return (
<div className="flex justify-between gap-2 items-center pl-3 pr-4 py-2 border-solid border-b-neutral-content border-b">
<Button
variant="ghost"
size="icon"
className="text-neutral lg:hidden sm:inline-flex"
onClick={() => {
setSidebar(true);
document.body.style.overflow = "hidden";
}}
>
<i className="bi-list text-xl leading-none" />
</Button>
<>
{STRIPE_ENABLED && isTrialling && (
<Link
href="/subscribe"
className="w-full text-sm cursor-pointer select-none bg-base-200"
>
<p className="w-full text-center flex items-center justify-center gap-1 underline decoration-dotted underline-offset-4 hover:opacity-70 duration-200 py-1 px-2">
<i className="bi-clock text-primary" />
{daysLeft === 1
? t("trial_left_singular")
: t("trial_left_plural", { count: daysLeft })}
</p>
</Link>
)}
<div className="flex justify-between gap-2 items-center pl-3 pr-4 py-2 border-solid border-b-neutral-content border-b">
<Button
variant="ghost"
size="icon"
className="text-neutral lg:hidden sm:inline-flex"
onClick={() => {
setSidebar(true);
document.body.style.overflow = "hidden";
}}
>
<i className="bi-list text-xl leading-none" />
</Button>
<SearchBar />
<SearchBar />
<div className="flex items-center gap-2">
<ToggleDarkMode hideInMobile />
<div className="flex items-center gap-2">
<ToggleDarkMode hideInMobile />
<DropdownMenu>
<DropdownMenuTrigger className="hidden sm:inline-grid">
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Button
variant="accent"
size="sm"
className="min-w-[3.4rem] h-[2rem] relative"
>
<span>
<i className="bi-plus text-4xl absolute -top-[0.3rem] left-0 pointer-events-none" />
</span>
<span>
<i className="bi-caret-down-fill text-xs absolute top-[0.6rem] right-[0.4rem] pointer-events-none" />
</span>
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{t("create_new")}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</DropdownMenuTrigger>
<DropdownMenu>
<DropdownMenuTrigger className="hidden sm:inline-grid">
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Button
variant="accent"
size="sm"
className="min-w-[3.4rem] h-[2rem] relative"
>
<span>
<i className="bi-plus text-4xl absolute -top-[0.3rem] left-0 pointer-events-none" />
</span>
<span>
<i className="bi-caret-down-fill text-xs absolute top-[0.6rem] right-[0.4rem] pointer-events-none" />
</span>
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{t("create_new")}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onSelect={() => setNewLinkModal(true)}>
<i className="bi-link-45deg" />
{t("new_link")}
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => setUploadFileModal(true)}>
<i className="bi-file-earmark-arrow-up" />
{t("upload_file")}
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => setNewCollectionModal(true)}>
<i className="bi-folder" />
{t("new_collection")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenuContent align="end">
<DropdownMenuItem onSelect={() => setNewLinkModal(true)}>
<i className="bi-link-45deg" />
{t("new_link")}
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => setUploadFileModal(true)}>
<i className="bi-file-earmark-arrow-up" />
{t("upload_file")}
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => setNewCollectionModal(true)}>
<i className="bi-folder" />
{t("new_collection")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<ProfileDropdown />
</div>
<MobileNavigation />
{sidebar && (
<div className="fixed top-0 bottom-0 right-0 left-0 bg-black bg-opacity-10 backdrop-blur-sm flex items-center fade-in z-40">
<ClickAwayHandler className="h-full" onClickOutside={toggleSidebar}>
<div className="slide-right h-full shadow-lg">
<Sidebar />
</div>
</ClickAwayHandler>
<ProfileDropdown />
</div>
)}
{newLinkModal && <NewLinkModal onClose={() => setNewLinkModal(false)} />}
{newCollectionModal && (
<NewCollectionModal onClose={() => setNewCollectionModal(false)} />
)}
{uploadFileModal && (
<UploadFileModal onClose={() => setUploadFileModal(false)} />
)}
</div>
<MobileNavigation />
{sidebar && (
<div className="fixed top-0 bottom-0 right-0 left-0 bg-black bg-opacity-10 backdrop-blur-sm flex items-center fade-in z-40">
<ClickAwayHandler className="h-full" onClickOutside={toggleSidebar}>
<div className="slide-right h-full shadow-lg">
<Sidebar />
</div>
</ClickAwayHandler>
</div>
)}
{newLinkModal && (
<NewLinkModal onClose={() => setNewLinkModal(false)} />
)}
{newCollectionModal && (
<NewCollectionModal onClose={() => setNewCollectionModal(false)} />
)}
{uploadFileModal && (
<UploadFileModal onClose={() => setUploadFileModal(false)} />
)}
</div>
</>
);
}

View File

@@ -9,7 +9,6 @@ import getServerSideProps from "@/lib/client/getServerSideProps";
import { Trans, useTranslation } from "next-i18next";
import { useUser } from "@linkwarden/router/user";
import { Separator } from "@/components/ui/separator";
import Link from "next/link";
import { cn } from "@/lib/utils";
const TRIAL_PERIOD_DAYS =

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -523,5 +523,7 @@
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
"shrink_sidebar": "Shrink Sidebar",
"trial_left_plural": "Trial ends in {{count}} days. Subscribe.",
"trial_left_singular": "Trial ends in 1 day. Subscribe."
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}

View File

@@ -513,5 +513,15 @@
"rename_tag_instruction": "Please provide a name for the final tag.",
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links."
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email.",
"expand_sidebar": "Expand Sidebar",
"shrink_sidebar": "Shrink Sidebar"
}