-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update u301-url-shortener extension (#13096)
* Update u301-url-shortener extension - Merge branch \'contributions/merge-1717948270228818000\' - Pull contributions - feat(u301-url-shortener): support API key * docs: add CHAELOG * feat(u301-url-shortener): support custom domain * update * fix: lint * feat(u301-url-shortener): shorten using clipboard * fix: set accept response type * feat: icon for differrent status * refactor: remove unused `@raycast/utils` * Pull contributions * Pull contributions * fix: keep the same name --------- Co-authored-by: Per Nielsen Tikær <per@raycast.com>
- Loading branch information
1 parent
7ca5f92
commit c5d96ac
Showing
6 changed files
with
185 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,101 @@ | ||
import { getSelectedText, Clipboard, showToast, Toast, getPreferenceValues } from "@raycast/api"; | ||
import fetch, { Headers } from "node-fetch"; | ||
import { Action, ActionPanel, Icon, List } from "@raycast/api"; | ||
import { useEffect, useState } from "react"; | ||
import { Clipboard, showToast, Toast, open } from "@raycast/api"; | ||
import { isValidURL, shortenURL, uniqueArray } from "./util"; | ||
|
||
function isValidURL(string: string) { | ||
const res = string.match( | ||
/(http(s)?:\/\/.)(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g, | ||
); | ||
return res !== null; | ||
} | ||
|
||
async function reportError({ message }: { message: string }) { | ||
await showToast(Toast.Style.Failure, "Error", message.toString()); | ||
interface Result { | ||
status: "init" | "shortened" | "error"; | ||
url: string; | ||
shortened: string; | ||
errorMessage?: string; | ||
} | ||
export default function Command() { | ||
const [items, setItems] = useState<Result[]>(); | ||
const [isLoading, setLoading] = useState(false); | ||
|
||
export default async function Command() { | ||
try { | ||
const preferences = getPreferenceValues(); | ||
const selectedText = await getSelectedText(); | ||
if (!isValidURL(selectedText)) { | ||
return reportError({ message: "Selected text is not a valid URL" }); | ||
} | ||
|
||
showToast({ | ||
const startShortening = async () => { | ||
const toast = showToast({ | ||
style: Toast.Style.Animated, | ||
title: "Shortening", | ||
}); | ||
const headers = new Headers({ | ||
"Content-Type": "application/json", | ||
}); | ||
if (preferences.accessToken) { | ||
headers.append("Authorization", `Bearer ${preferences.accessToken}`); | ||
} | ||
const res = await fetch(`https://api.u301.com/v2/shorten?url=${encodeURIComponent(selectedText)}`, { | ||
headers, | ||
const content = await Clipboard.readText(); | ||
const lines: Result[] = uniqueArray(content?.split("\n").filter((line) => isValidURL(line))).map((line) => { | ||
return { | ||
url: line.trim(), | ||
shortened: "", | ||
status: "init", | ||
}; | ||
}); | ||
const { shortened, message = "Failed to shorten" } = (await res.json()) as { shortened: string; message?: string }; | ||
if (!shortened) { | ||
return reportError({ message }); | ||
if (lines) { | ||
setLoading(true); | ||
for (const i in lines) { | ||
lines[i].status = "shortened"; | ||
try { | ||
const { shortened, message } = await shortenURL(lines[i].url); | ||
if (shortened) { | ||
lines[i].status = "shortened"; | ||
lines[i].shortened = shortened; | ||
} else { | ||
lines[i].status = "error"; | ||
lines[i].errorMessage = message; | ||
} | ||
} catch (error) { | ||
lines[i].status = "error"; | ||
lines[i].errorMessage = (error as Error).message; | ||
} | ||
setItems(lines); | ||
} | ||
setLoading(false); | ||
const resultURLs = lines | ||
.map((line) => { | ||
if (line.status === "shortened") { | ||
return line.shortened; | ||
} else { | ||
return line.url; | ||
} | ||
}) | ||
.join("\n"); | ||
|
||
await Clipboard.copy(resultURLs); | ||
await showToast(Toast.Style.Success, "Success", "Copied shortened URLs to clipboard"); | ||
} | ||
if (preferences.clipboard === "clipboard") { | ||
await Clipboard.copy(shortened); | ||
} else { | ||
await Clipboard.paste(shortened); | ||
(await toast).hide(); | ||
}; | ||
|
||
useEffect(() => { | ||
startShortening(); | ||
}, []); | ||
|
||
const getIcon = (item: Result) => { | ||
if (item.status === "shortened") { | ||
return Icon.CheckCircle; | ||
} else if (item.status === "error") { | ||
return Icon.Info; | ||
} | ||
} catch (error) { | ||
return reportError({ | ||
message: "Not able to get selected text", | ||
}); | ||
} | ||
return Icon.Link; | ||
}; | ||
|
||
await showToast(Toast.Style.Success, "Success", "Copied shortened URL to clipboard"); | ||
return ( | ||
<List isLoading={isLoading}> | ||
<List.Section title="URLs"> | ||
{items?.map((item, index) => ( | ||
<List.Item | ||
actions={ | ||
<ActionPanel> | ||
<Action | ||
title="Open URL" | ||
onAction={() => open(item.status === "shortened" ? item.shortened : item.url)} | ||
/> | ||
</ActionPanel> | ||
} | ||
icon={getIcon(item)} | ||
key={index} | ||
subtitle={item.status === "shortened" ? item.url : item.errorMessage} | ||
title={item.status === "shortened" ? item.shortened : item.url} | ||
/> | ||
))} | ||
</List.Section> | ||
{!isLoading && <List.EmptyView icon={Icon.Clipboard} title="Your clipboard does not contain a URL." />} | ||
</List> | ||
); | ||
} |
30 changes: 30 additions & 0 deletions
30
extensions/u301-url-shortener/src/shorten-selected-text.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { getSelectedText, Clipboard, showToast, Toast } from "@raycast/api"; | ||
import { isValidURL, shortenURL } from "./util"; | ||
|
||
async function reportError({ message }: { message: string }) { | ||
await showToast(Toast.Style.Failure, "Error", message.toString()); | ||
} | ||
|
||
export default async function Command() { | ||
try { | ||
const selectedText = await getSelectedText(); | ||
if (!isValidURL(selectedText)) { | ||
return reportError({ message: "Selected text is not a valid URL" }); | ||
} | ||
|
||
showToast({ | ||
style: Toast.Style.Animated, | ||
title: "Shortening", | ||
}); | ||
const { shortened, message = "Failed to shorten" } = await shortenURL(selectedText); | ||
if (!shortened) { | ||
return reportError({ message }); | ||
} | ||
await Clipboard.copy(shortened); | ||
await showToast(Toast.Style.Success, "Success", "Copied shortened URL to clipboard"); | ||
} catch (error) { | ||
return reportError({ | ||
message: "Not able to get selected text", | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { getPreferenceValues } from "@raycast/api"; | ||
import fetch, { Headers } from "node-fetch"; | ||
|
||
export function isValidURL(string: string) { | ||
const regex = new RegExp( | ||
/(http(s)?:\/\/.)(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g, | ||
); | ||
return string.match(regex); | ||
} | ||
|
||
export function uniqueArray(items?: string[]): string[] { | ||
if (!items) { | ||
return []; | ||
} | ||
return Array.from(new Set(items)); | ||
} | ||
|
||
export async function shortenURL(url: string) { | ||
const preferences = getPreferenceValues(); | ||
const headers = new Headers({ | ||
Accept: "application/json", | ||
}); | ||
if (preferences.accessToken) { | ||
headers.append("Authorization", `Bearer ${preferences.accessToken}`); | ||
} | ||
let URL = `https://api.u301.com/v2/shorten?url=${encodeURIComponent(url)}`; | ||
if (preferences.domainName) { | ||
URL += `&domain=${preferences.domainName}`; | ||
} | ||
const res = await fetch(URL, { | ||
headers, | ||
}); | ||
return (await res.json()) as { shortened: string; message?: string }; | ||
} |