[WEB-3797] fix: remove leading slash from URL to copy (#6890)
* fix: remove prefix slash if present * chore: make use of URL class to generate a valid URL
This commit is contained in:
parent
27cec64c56
commit
37699362ad
9 changed files with 24 additions and 45 deletions
|
|
@ -86,8 +86,11 @@ export const copyTextToClipboard = async (text: string): Promise<void> => {
|
||||||
* await copyUrlToClipboard("issues/123") // copies "https://example.com/issues/123"
|
* await copyUrlToClipboard("issues/123") // copies "https://example.com/issues/123"
|
||||||
*/
|
*/
|
||||||
export const copyUrlToClipboard = async (path: string) => {
|
export const copyUrlToClipboard = async (path: string) => {
|
||||||
const originUrl = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
// get origin or default to empty string if not in browser
|
||||||
await copyTextToClipboard(`${originUrl}/${path}`);
|
const originUrl = typeof window !== "undefined" ? window.location.origin : "";
|
||||||
|
// create URL object and ensure proper path formatting
|
||||||
|
const url = new URL(path, originUrl);
|
||||||
|
await copyTextToClipboard(url.toString());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React, { FC, useState } from "react";
|
import React, { FC, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
// plane imports
|
||||||
|
import { copyUrlToClipboard } from "@plane/utils";
|
||||||
// helpers
|
// helpers
|
||||||
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssueDetail, useProject } from "@/hooks/store";
|
import { useIssueDetail, useProject } from "@/hooks/store";
|
||||||
|
|
||||||
|
|
@ -41,7 +42,7 @@ export const CreateIssueToastActionItems: FC<TCreateIssueToastActionItems> = obs
|
||||||
|
|
||||||
const copyToClipboard = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
const copyToClipboard = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||||
try {
|
try {
|
||||||
await copyUrlToClipboard(workItemLink, false);
|
await copyUrlToClipboard(workItemLink);
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
setTimeout(() => setCopied(false), 3000);
|
setTimeout(() => setCopied(false), 3000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,16 @@ import omit from "lodash/omit";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { Copy, ExternalLink, Link, Pencil, Trash2 } from "lucide-react";
|
import { Copy, ExternalLink, Link, Pencil, Trash2 } from "lucide-react";
|
||||||
// types
|
// plane imports
|
||||||
import { ARCHIVABLE_STATE_GROUPS, EIssuesStoreType } from "@plane/constants";
|
import { ARCHIVABLE_STATE_GROUPS, EIssuesStoreType } from "@plane/constants";
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// ui
|
|
||||||
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
|
import { copyUrlToClipboard } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useProject, useProjectState } from "@/hooks/store";
|
import { useEventTracker, useProject, useProjectState } from "@/hooks/store";
|
||||||
// types
|
// types
|
||||||
|
|
@ -62,7 +61,7 @@ export const AllIssueQuickActions: React.FC<IQuickActionProps> = observer((props
|
||||||
|
|
||||||
const handleOpenInNewTab = () => window.open(workItemLink, "_blank");
|
const handleOpenInNewTab = () => window.open(workItemLink, "_blank");
|
||||||
const handleCopyIssueLink = () =>
|
const handleCopyIssueLink = () =>
|
||||||
copyUrlToClipboard(workItemLink, false).then(() =>
|
copyUrlToClipboard(workItemLink).then(() =>
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: "Link copied",
|
title: "Link copied",
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,16 @@ import omit from "lodash/omit";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { Copy, ExternalLink, Link, Pencil, Trash2, XCircle } from "lucide-react";
|
import { Copy, ExternalLink, Link, Pencil, Trash2, XCircle } from "lucide-react";
|
||||||
// types
|
// plane imports
|
||||||
import { ARCHIVABLE_STATE_GROUPS, EIssuesStoreType, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
import { ARCHIVABLE_STATE_GROUPS, EIssuesStoreType, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// ui
|
|
||||||
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
|
import { copyUrlToClipboard } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useIssues, useProject, useProjectState, useUserPermissions } from "@/hooks/store";
|
import { useEventTracker, useIssues, useProject, useProjectState, useUserPermissions } from "@/hooks/store";
|
||||||
// types
|
// types
|
||||||
|
|
@ -70,7 +69,7 @@ export const CycleIssueQuickActions: React.FC<IQuickActionProps> = observer((pro
|
||||||
const handleOpenInNewTab = () => window.open(workItemLink, "_blank");
|
const handleOpenInNewTab = () => window.open(workItemLink, "_blank");
|
||||||
|
|
||||||
const handleCopyIssueLink = () =>
|
const handleCopyIssueLink = () =>
|
||||||
copyUrlToClipboard(workItemLink, false).then(() =>
|
copyUrlToClipboard(workItemLink).then(() =>
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: "Link copied",
|
title: "Link copied",
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,16 @@ import omit from "lodash/omit";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { Copy, ExternalLink, Link, Pencil, Trash2, XCircle } from "lucide-react";
|
import { Copy, ExternalLink, Link, Pencil, Trash2, XCircle } from "lucide-react";
|
||||||
// types
|
// plane imports
|
||||||
import { ARCHIVABLE_STATE_GROUPS, EIssuesStoreType, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
import { ARCHIVABLE_STATE_GROUPS, EIssuesStoreType, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// ui
|
|
||||||
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
|
import { copyUrlToClipboard } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssues, useEventTracker, useProjectState, useUserPermissions, useProject } from "@/hooks/store";
|
import { useIssues, useEventTracker, useProjectState, useUserPermissions, useProject } from "@/hooks/store";
|
||||||
// types
|
// types
|
||||||
|
|
@ -70,7 +69,7 @@ export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = observer((pr
|
||||||
const handleOpenInNewTab = () => window.open(workItemLink, "_blank");
|
const handleOpenInNewTab = () => window.open(workItemLink, "_blank");
|
||||||
|
|
||||||
const handleCopyIssueLink = () =>
|
const handleCopyIssueLink = () =>
|
||||||
copyUrlToClipboard(workItemLink, false).then(() =>
|
copyUrlToClipboard(workItemLink).then(() =>
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: "Link copied",
|
title: "Link copied",
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,17 @@ import omit from "lodash/omit";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname } from "next/navigation";
|
||||||
import { Copy, ExternalLink, Link, Pencil, Trash2 } from "lucide-react";
|
import { Copy, ExternalLink, Link, Pencil, Trash2 } from "lucide-react";
|
||||||
|
// plane imports
|
||||||
import { ARCHIVABLE_STATE_GROUPS, EIssuesStoreType, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
import { ARCHIVABLE_STATE_GROUPS, EIssuesStoreType, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
// types
|
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// ui
|
|
||||||
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
|
import { copyUrlToClipboard } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useIssues, useProject, useProjectState, useUserPermissions } from "@/hooks/store";
|
import { useEventTracker, useIssues, useProject, useProjectState, useUserPermissions } from "@/hooks/store";
|
||||||
// types
|
// types
|
||||||
|
|
@ -75,7 +74,7 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCopyIssueLink = () =>
|
const handleCopyIssueLink = () =>
|
||||||
copyUrlToClipboard(workItemLink, false).then(() =>
|
copyUrlToClipboard(workItemLink).then(() =>
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: "Link copied",
|
title: "Link copied",
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,8 @@ import Link from "next/link";
|
||||||
import { ArchiveRestoreIcon, Link2, MoveDiagonal, MoveRight, Trash2 } from "lucide-react";
|
import { ArchiveRestoreIcon, Link2, MoveDiagonal, MoveRight, Trash2 } from "lucide-react";
|
||||||
// plane imports
|
// plane imports
|
||||||
import { ARCHIVABLE_STATE_GROUPS } from "@plane/constants";
|
import { ARCHIVABLE_STATE_GROUPS } from "@plane/constants";
|
||||||
// i18n
|
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
// types
|
|
||||||
import { TNameDescriptionLoader } from "@plane/types";
|
import { TNameDescriptionLoader } from "@plane/types";
|
||||||
// ui
|
|
||||||
import {
|
import {
|
||||||
ArchiveIcon,
|
ArchiveIcon,
|
||||||
CenterPanelIcon,
|
CenterPanelIcon,
|
||||||
|
|
@ -21,12 +18,12 @@ import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
setToast,
|
setToast,
|
||||||
} from "@plane/ui";
|
} from "@plane/ui";
|
||||||
|
import { copyUrlToClipboard } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
import { IssueSubscription, NameDescriptionUpdateStatus } from "@/components/issues";
|
import { IssueSubscription, NameDescriptionUpdateStatus } from "@/components/issues";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
|
||||||
// store hooks
|
// store hooks
|
||||||
import { useIssueDetail, useProject, useProjectState, useUser } from "@/hooks/store";
|
import { useIssueDetail, useProject, useProjectState, useUser } from "@/hooks/store";
|
||||||
// hooks
|
// hooks
|
||||||
|
|
@ -110,7 +107,7 @@ export const IssuePeekOverviewHeader: FC<PeekOverviewHeaderProps> = observer((pr
|
||||||
const handleCopyText = (e: React.MouseEvent<HTMLButtonElement>) => {
|
const handleCopyText = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
copyUrlToClipboard(workItemLink, false).then(() => {
|
copyUrlToClipboard(workItemLink).then(() => {
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: t("common.link_copied"),
|
title: t("common.link_copied"),
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
// plane constants
|
// plane imports
|
||||||
import { IS_FAVORITE_MENU_OPEN } from "@plane/constants";
|
import { IS_FAVORITE_MENU_OPEN } from "@plane/constants";
|
||||||
// plane editor
|
|
||||||
import { EditorRefApi } from "@plane/editor";
|
import { EditorRefApi } from "@plane/editor";
|
||||||
// plane types
|
|
||||||
import { EPageAccess } from "@plane/types/src/enums";
|
import { EPageAccess } from "@plane/types/src/enums";
|
||||||
// plane ui
|
|
||||||
import { setToast, TOAST_TYPE } from "@plane/ui";
|
import { setToast, TOAST_TYPE } from "@plane/ui";
|
||||||
// helpers
|
import { copyUrlToClipboard } from "@plane/utils";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useCollaborativePageActions } from "@/hooks/use-collaborative-page-actions";
|
import { useCollaborativePageActions } from "@/hooks/use-collaborative-page-actions";
|
||||||
// store types
|
// store types
|
||||||
|
|
|
||||||
|
|
@ -65,20 +65,6 @@ export const copyTextToClipboard = async (text: string) => {
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @description: This function copies the url to clipboard after prepending the origin URL to it
|
|
||||||
* @param {string} path
|
|
||||||
* @param {boolean} addSlash
|
|
||||||
* @example:
|
|
||||||
* const text = copyUrlToClipboard("path");
|
|
||||||
* copied URL: origin_url/path
|
|
||||||
*/
|
|
||||||
export const copyUrlToClipboard = async (path: string, addSlash: boolean = true) => {
|
|
||||||
const originUrl = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
|
||||||
|
|
||||||
await copyTextToClipboard(`${originUrl}${addSlash ? "/" : ""}${path}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateRandomColor = (string: string): string => {
|
export const generateRandomColor = (string: string): string => {
|
||||||
if (!string) return "rgb(var(--color-primary-100))";
|
if (!string) return "rgb(var(--color-primary-100))";
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue