diff --git a/extensions/google-workspace/CHANGELOG.md b/extensions/google-workspace/CHANGELOG.md index 6f1e4043827..4abec5eedd0 100644 --- a/extensions/google-workspace/CHANGELOG.md +++ b/extensions/google-workspace/CHANGELOG.md @@ -1,5 +1,9 @@ # Google Workspace Changelog +## [Add file path info to the file list] - 2024-10-21 + +- Adds the file path information to the list of accessories. + ## [Log out the user if re-authentication fails] - 2024-07-11 - Automatically log out users if re-authentication fails, instead of displaying an error message. diff --git a/extensions/google-workspace/package.json b/extensions/google-workspace/package.json index 1533f9c700b..0290e9064f5 100644 --- a/extensions/google-workspace/package.json +++ b/extensions/google-workspace/package.json @@ -11,12 +11,23 @@ "mathieudutour", "marcmagn1", "meganpearson", - "jbjanot" + "jbjanot", + "ridemountainpig" ], "categories": [ "Productivity" ], "license": "MIT", + "preferences": [ + { + "name": "displayFilePath", + "type": "checkbox", + "required": false, + "default": false, + "description": "Display the file path in the search results. ⚠️ This may slow down your search results.", + "label": "Display file path in search results" + } + ], "commands": [ { "name": "create-google-document", diff --git a/extensions/google-workspace/src/api/getFiles.ts b/extensions/google-workspace/src/api/getFiles.ts index 1e81f16fc94..5b659ed5173 100644 --- a/extensions/google-workspace/src/api/getFiles.ts +++ b/extensions/google-workspace/src/api/getFiles.ts @@ -1,3 +1,7 @@ +import { getOAuthToken } from "./googleAuth"; +import { getPreferenceValues } from "@raycast/api"; +import fetch from "node-fetch"; + export enum QueryTypes { fileName = "fileName", fullText = "fullText", @@ -19,11 +23,18 @@ export type File = { modifiedTime: string; starred: boolean; parents?: string[]; + filePath?: string; capabilities?: { canTrash: boolean; }; }; +type FileData = { + id: string; + name: string; + parents: string[]; +}; + function getParams(queryType: QueryTypes, scope: ScopeTypes, queryText = "") { const params = new URLSearchParams(); @@ -57,10 +68,57 @@ function getParams(queryType: QueryTypes, scope: ScopeTypes, queryText = "") { return params.toString(); } -export function getFilesURL(queryType: QueryTypes, scope: ScopeTypes, queryText = "") { - return `https://www.googleapis.com/drive/v3/files?${getParams(queryType, scope, queryText)}`; +export async function getFiles(queryType: QueryTypes, scope: ScopeTypes, queryText = "") { + const url = `https://www.googleapis.com/drive/v3/files?${getParams(queryType, scope, queryText)}`; + const response = await fetch(url, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getOAuthToken()}`, + }, + }); + const data = (await response.json()) as { files: File[] }; + + const { displayFilePath } = getPreferenceValues<Preferences>(); + if (displayFilePath) { + await Promise.all( + data.files.map(async (file) => { + file.filePath = await getFilePath(file.id); + }), + ); + } + + return data; +} + +async function getFilePath(fileId: string): Promise<string> { + const getFileParents = async (fileId: string) => { + const getFileUrl = `https://www.googleapis.com/drive/v3/files/${fileId}?fields=name,parents`; + const response = await fetch(getFileUrl, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getOAuthToken()}`, + }, + }); + + return await response.json(); + }; + + const getParentPath = async (fileId: string): Promise<string> => { + const fileData = (await getFileParents(fileId)) as FileData; + + if (!fileData.parents || fileData.parents.length === 0) { + return fileData.name; + } + + const parentId = fileData.parents[0]; + const parentPath = await getParentPath(parentId); + + return `${parentPath}/${fileData.name}`; + }; + + return await getParentPath(fileId); } -export function getStarredFilesURL() { - return getFilesURL(QueryTypes.starred, ScopeTypes.allDrives); +export function getStarredFiles() { + return getFiles(QueryTypes.starred, ScopeTypes.allDrives); } diff --git a/extensions/google-workspace/src/components/FileListItem.tsx b/extensions/google-workspace/src/components/FileListItem.tsx index fb0bbc8a50c..fc82e24b23f 100644 --- a/extensions/google-workspace/src/components/FileListItem.tsx +++ b/extensions/google-workspace/src/components/FileListItem.tsx @@ -1,4 +1,4 @@ -import { Action, ActionPanel, Color, Icon, List } from "@raycast/api"; +import { Action, ActionPanel, Color, Icon, List, getPreferenceValues } from "@raycast/api"; import { format } from "date-fns"; import { File } from "../api/getFiles"; import { getFileIconLink, humanFileSize } from "../helpers/files"; @@ -9,6 +9,7 @@ type FileListItemProps = { }; export default function FileListItem({ file, email }: FileListItemProps) { + const { displayFilePath } = getPreferenceValues(); const modifiedTime = new Date(file.modifiedTime); const accessories: List.Item.Accessory[] = [ @@ -18,6 +19,13 @@ export default function FileListItem({ file, email }: FileListItemProps) { }, ]; + if (displayFilePath && file.filePath) { + accessories.unshift({ + icon: { source: Icon.Folder, tintColor: Color.SecondaryText }, + tooltip: file.filePath, + }); + } + if (file.starred) { accessories.unshift({ icon: { source: Icon.Star, tintColor: Color.Yellow }, diff --git a/extensions/google-workspace/src/search-google-drive-files.tsx b/extensions/google-workspace/src/search-google-drive-files.tsx index 528b29705ef..832b490f5ea 100644 --- a/extensions/google-workspace/src/search-google-drive-files.tsx +++ b/extensions/google-workspace/src/search-google-drive-files.tsx @@ -1,12 +1,12 @@ -import { Action, ActionPanel, List, showToast, Toast } from "@raycast/api"; -import { useFetch, useCachedState } from "@raycast/utils"; +import { Action, ActionPanel, List } from "@raycast/api"; +import { useCachedPromise, useCachedState } from "@raycast/utils"; import { useState } from "react"; -import { QueryTypes, getFilesURL, File, ScopeTypes } from "./api/getFiles"; +import { QueryTypes, getFiles, ScopeTypes } from "./api/getFiles"; import FileListItem from "./components/FileListItem"; import { withGoogleAuth } from "./components/withGoogleAuth"; -import { getOAuthToken, getUserEmail } from "./api/googleAuth"; +import { getUserEmail } from "./api/googleAuth"; function SearchGoogleDriveFiles() { const [query, setQuery] = useState(""); @@ -15,17 +15,11 @@ function SearchGoogleDriveFiles() { const email = getUserEmail(); - const { data, isLoading } = useFetch<{ files: File[] }>(getFilesURL(queryType, scopeType, query), { - keepPreviousData: true, - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${getOAuthToken()}`, - }, - onError(error) { - console.error(error); - showToast({ style: Toast.Style.Failure, title: "Failed to retrieve files" }); - }, - }); + const { data, isLoading } = useCachedPromise( + async (queryType: QueryTypes, scopeType: ScopeTypes, query: string) => await getFiles(queryType, scopeType, query), + [queryType, scopeType, query], + { failureToastOptions: { title: "Failed to retrieve files" } }, + ); return ( <List diff --git a/extensions/google-workspace/src/starred-google-drive-files-menubar.tsx b/extensions/google-workspace/src/starred-google-drive-files-menubar.tsx index e91078cdbfe..e4ec644382b 100644 --- a/extensions/google-workspace/src/starred-google-drive-files-menubar.tsx +++ b/extensions/google-workspace/src/starred-google-drive-files-menubar.tsx @@ -1,17 +1,18 @@ import { Icon, LaunchType, MenuBarExtra, launchCommand, open } from "@raycast/api"; -import { useFetch } from "@raycast/utils"; -import { File, getStarredFilesURL } from "./api/getFiles"; +import { useCachedPromise } from "@raycast/utils"; +import { getStarredFiles } from "./api/getFiles"; import { withGoogleAuth } from "./components/withGoogleAuth"; import { getFileIconLink } from "./helpers/files"; import { createDocFromUrl } from "./helpers/docs"; -import { getOAuthToken } from "./api/googleAuth"; function StarredFiles() { - const { data, isLoading } = useFetch<{ files: File[] }>(getStarredFilesURL(), { - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${getOAuthToken()}`, - }, + const { data, isLoading } = useCachedPromise(async () => { + try { + return await getStarredFiles(); + } catch (error) { + console.error(error); + throw error; + } }); const MAX_ITEMS = 40; diff --git a/extensions/google-workspace/src/starred-google-drive-files.tsx b/extensions/google-workspace/src/starred-google-drive-files.tsx index e3ca6ab2cf4..08e60f5952b 100644 --- a/extensions/google-workspace/src/starred-google-drive-files.tsx +++ b/extensions/google-workspace/src/starred-google-drive-files.tsx @@ -1,23 +1,16 @@ -import { Action, ActionPanel, List, showToast, Toast } from "@raycast/api"; -import { useFetch } from "@raycast/utils"; +import { Action, ActionPanel, List } from "@raycast/api"; +import { useCachedPromise } from "@raycast/utils"; -import { getStarredFilesURL, File } from "./api/getFiles"; +import { getStarredFiles } from "./api/getFiles"; import FileListItem from "./components/FileListItem"; import { withGoogleAuth } from "./components/withGoogleAuth"; -import { getOAuthToken, getUserEmail } from "./api/googleAuth"; +import { getUserEmail } from "./api/googleAuth"; function StarredGoogleDriveFiles() { const email = getUserEmail(); - const { data, isLoading } = useFetch<{ files: File[] }>(getStarredFilesURL(), { - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${getOAuthToken()}`, - }, - onError(error) { - console.error(error); - showToast({ style: Toast.Style.Failure, title: "Failed to retrieve starred files" }); - }, + const { data, isLoading } = useCachedPromise(() => getStarredFiles(), [], { + failureToastOptions: { title: "Failed to retrieve starred files" }, }); return (