-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Google Contacts Initial Release #17638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Google Contacts Initial Release
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
This PR adds a comprehensive Google Contacts extension for Raycast with contact management and AI capabilities. Here are the key points to address:
-
The CHANGELOG.md contains incorrect entries for Google Tasks - it should be replaced with a single "Initial Version" entry ending with {PR_MERGE_DATE}
-
The README.md has incorrect store/GitHub links pointing to Microsoft Teams instead of Google Contacts and contains a double period in the description
-
Since there are
view
commands in package.json, ametadata
folder with screenshots should be added per Raycast guidelines -
The
tools
section in package.json needs to includeai
withevals
as required by Raycast's AI extension guidelines -
The
launchCommand
in create-contact.tsx should be wrapped in a try-catch block per Raycast best practices
The extension implementation itself appears solid with comprehensive features and good error handling, but these documentation and configuration issues should be addressed before merging.
π‘ (2/5) Greptile learns from your feedback when you react with π/π!
18 file(s) reviewed, 43 comment(s)
Edit PR Review Bot Settings | Greptile
google-contacts/package.json
Outdated
"query": "John Smith" | ||
}, | ||
{ | ||
"query": "acme corporation", | ||
"limit": 3 | ||
} | ||
], | ||
"arguments": [ | ||
{ | ||
"name": "query", | ||
"title": "Search Query", | ||
"required": true, | ||
"description": "The search term to look for in contacts (name, email, company, etc.)" | ||
}, | ||
{ | ||
"name": "limit", | ||
"title": "Result Limit", | ||
"required": false, | ||
"description": "Maximum number of contacts to return (default: 5)" | ||
} | ||
] | ||
}, | ||
{ | ||
"path": "./src/tools/create-contact", | ||
"name": "create-contact", | ||
"title": "Create Google Contact", | ||
"description": "Create a new contact in your Google Contacts.", | ||
"examples": [ | ||
{ | ||
"firstName": "John", | ||
"lastName": "Smith", | ||
"email": "[email protected]", | ||
"phone": "+1 555-123-4567" | ||
}, | ||
{ | ||
"firstName": "Jane", | ||
"lastName": "Doe", | ||
"company": "Acme Corporation", | ||
"jobTitle": "Marketing Manager", | ||
"email": "[email protected]" | ||
} | ||
], | ||
"arguments": [ | ||
{ | ||
"name": "firstName", | ||
"title": "First Name", | ||
"required": true, | ||
"description": "The contact's first name" | ||
}, | ||
{ | ||
"name": "lastName", | ||
"title": "Last Name", | ||
"required": false, | ||
"description": "The contact's last name" | ||
}, | ||
{ | ||
"name": "email", | ||
"title": "Email", | ||
"required": false, | ||
"description": "The contact's email address" | ||
}, | ||
{ | ||
"name": "phone", | ||
"title": "Phone", | ||
"required": false, | ||
"description": "The contact's phone number" | ||
}, | ||
{ | ||
"name": "company", | ||
"title": "Company", | ||
"required": false, | ||
"description": "The contact's company or organization" | ||
}, | ||
{ | ||
"name": "jobTitle", | ||
"title": "Job Title", | ||
"required": false, | ||
"description": "The contact's job title or position" | ||
}, | ||
{ | ||
"name": "address", | ||
"title": "Address", | ||
"required": false, | ||
"description": "The contact's physical address" | ||
}, | ||
{ | ||
"name": "birthday", | ||
"title": "Birthday", | ||
"required": false, | ||
"description": "The contact's birthday in DD.MM.YYYY format" | ||
}, | ||
{ | ||
"name": "notes", | ||
"title": "Notes", | ||
"required": false, | ||
"description": "Additional notes about the contact" | ||
} | ||
] | ||
} | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: AI tools are defined but missing required 'ai' field with 'evals'. Please add evals to test the AI functionality. See: https://developers.raycast.com/ai/write-evals-for-your-ai-extension
google-contacts/package.json
Outdated
"name": "view-contacts", | ||
"title": "List Contacts", | ||
"description": "View and manage your Google Contacts", | ||
"mode": "view" | ||
}, | ||
{ | ||
"name": "create-contact", | ||
"title": "Create Contact", | ||
"description": "Create a new Google Contact", | ||
"mode": "view" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Consider adding 'subtitle' field to commands to show 'Google Contacts' as service name for better context
google-contacts/CHANGELOG.md
Outdated
@@ -0,0 +1,14 @@ | |||
# Google Tasks Changelog |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: This should be 'Google Contacts Changelog' instead of 'Google Tasks Changelog' since this is for the Google Contacts extension
# Google Tasks Changelog | |
# Google Contacts Changelog |
google-contacts/CHANGELOG.md
Outdated
## [Sort tasks by due date] - 2023-07-23 | ||
|
||
## [Update] - 2023-05-12 | ||
|
||
- Increase the maximum tasks from 20 to 100. | ||
- Modify the filtering logic. This will now display up to 100 open or completed tasks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: These entries appear to be for Google Tasks and should be removed since this is a new Google Contacts extension
google-contacts/CHANGELOG.md
Outdated
## [Initial Version] - 2022-09-16 | ||
|
||
Initial version code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: For a new extension, there should be a single entry like:
## [Initial Version] - 2022-09-16 | |
Initial version code | |
## [Initial Version] {PR_MERGE_DATE} | |
- Initial version of Google Contacts extension |
google-contacts/src/utils.tsx
Outdated
// Format a birthday from a ContactBirthday object | ||
export function formatBirthday(birthday: ContactBirthday): string { | ||
// If there's a text representation, use that | ||
if (birthday.text) { | ||
return birthday.text; | ||
} | ||
|
||
// If there's date information, format it | ||
if (birthday.date) { | ||
const { year, month, day } = birthday.date; | ||
|
||
// Format: dd.MM.yyyy or dd.MM depending on if year is available | ||
if (month && day) { | ||
const monthStr = month.toString().padStart(2, '0'); | ||
const dayStr = day.toString().padStart(2, '0'); | ||
|
||
if (year) { | ||
return `${dayStr}.${monthStr}.${year}`; | ||
} else { | ||
return `${dayStr}.${monthStr}`; | ||
} | ||
} | ||
} | ||
|
||
return "Unknown"; | ||
} | ||
|
||
// Get birthday info from a contact | ||
export function getBirthdayInfo(contact: Contact): string | undefined { | ||
if (!contact.birthdays || contact.birthdays.length === 0) { | ||
return undefined; | ||
} | ||
|
||
// Try to find the primary birthday if there are multiple | ||
const primaryBirthday = contact.birthdays.find(birthday => birthday.metadata?.primary); | ||
|
||
// If primary birthday exists, use it, otherwise use the first birthday | ||
const birthdayToUse = primaryBirthday || contact.birthdays[0]; | ||
|
||
return formatBirthday(birthdayToUse); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: formatBirthday and getBirthdayInfo functions are incorrectly indented - they should be at the root level
// Format a birthday from a ContactBirthday object | |
export function formatBirthday(birthday: ContactBirthday): string { | |
// If there's a text representation, use that | |
if (birthday.text) { | |
return birthday.text; | |
} | |
// If there's date information, format it | |
if (birthday.date) { | |
const { year, month, day } = birthday.date; | |
// Format: dd.MM.yyyy or dd.MM depending on if year is available | |
if (month && day) { | |
const monthStr = month.toString().padStart(2, '0'); | |
const dayStr = day.toString().padStart(2, '0'); | |
if (year) { | |
return `${dayStr}.${monthStr}.${year}`; | |
} else { | |
return `${dayStr}.${monthStr}`; | |
} | |
} | |
} | |
return "Unknown"; | |
} | |
// Get birthday info from a contact | |
export function getBirthdayInfo(contact: Contact): string | undefined { | |
if (!contact.birthdays || contact.birthdays.length === 0) { | |
return undefined; | |
} | |
// Try to find the primary birthday if there are multiple | |
const primaryBirthday = contact.birthdays.find(birthday => birthday.metadata?.primary); | |
// If primary birthday exists, use it, otherwise use the first birthday | |
const birthdayToUse = primaryBirthday || contact.birthdays[0]; | |
return formatBirthday(birthdayToUse); | |
} | |
// Format a birthday from a ContactBirthday object | |
export function formatBirthday(birthday: ContactBirthday): string { | |
// If there's a text representation, use that | |
if (birthday.text) { | |
return birthday.text; | |
} | |
// If there's date information, format it | |
if (birthday.date) { | |
const { year, month, day } = birthday.date; | |
// Format: dd.MM.yyyy or dd.MM depending on if year is available | |
if (month && day) { | |
const monthStr = month.toString().padStart(2, '0'); | |
const dayStr = day.toString().padStart(2, '0'); | |
if (year) { | |
return `${dayStr}.${monthStr}.${year}`; | |
} else { | |
return `${dayStr}.${monthStr}`; | |
} | |
} | |
} | |
return "Unknown"; | |
} | |
// Get birthday info from a contact | |
export function getBirthdayInfo(contact: Contact): string | undefined { | |
if (!contact.birthdays || contact.birthdays.length === 0) { | |
return undefined; | |
} | |
// Try to find the primary birthday if there are multiple | |
const primaryBirthday = contact.birthdays.find(birthday => birthday.metadata?.primary); | |
// If primary birthday exists, use it, otherwise use the first birthday | |
const birthdayToUse = primaryBirthday || contact.birthdays[0]; | |
return formatBirthday(birthdayToUse); | |
} |
// Cache keys | ||
const CONTACTS_CACHE_KEY = "google_contacts_cached_data"; | ||
const CACHE_TIMESTAMP_KEY = "google_contacts_cache_timestamp"; | ||
const CACHE_EXPIRY_TIME = 60 + 24 * 60 * 1000; // 1 Day in milliseconds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Cache expiry time calculation is incorrect - should be 24 * 60 * 60 * 1000
for 1 day in milliseconds
const CACHE_EXPIRY_TIME = 60 + 24 * 60 * 1000; // 1 Day in milliseconds | |
const CACHE_EXPIRY_TIME = 24 * 60 * 60 * 1000; // 1 Day in milliseconds |
await google.authorize(); | ||
const allContacts = await fetchContacts(1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: google.authorize() should be wrapped in try/catch to handle auth failures gracefully
// Only show error toast if we don't have cached contacts to show | ||
if (contacts.length === 0) { | ||
setIsLoading(false); | ||
showToast({ style: Toast.Style.Failure, title: String(error) }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Could be simplified using showFailureToast from @raycast/utils
showToast({ style: Toast.Style.Failure, title: String(error) }); | |
showFailureToast(String(error)); |
if (isLoading && contacts.length === 0) { | ||
return <Detail isLoading={true} markdown="Loading contacts..." />; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Consider using List with isLoading prop instead of Detail to avoid empty state flicker
if (isLoading && contacts.length === 0) { | |
return <Detail isLoading={true} markdown="Loading contacts..." />; | |
} | |
if (isLoading && contacts.length === 0) { | |
return <ContactsView initialContacts={[]} filter={Filter.All} isLoading={true} />; | |
} |
Congratulations on your new Raycast extension! π Due to our current reduced availability, the initial review may take up to 10-15 business days Once the PR is approved and merged, the extension will be available on our Store. |
Hey @d0rN834 π You can run Let me know if you run into any further issues. |
Description
Manage your Google Contacts directly from Raycast with complete integration, including AI capabilities.
Core Features
Screencast
See Screenshots. Won't share personal data.
Checklist
npm run build
and tested this distribution build in Raycastassets
folder are used by the extension itselfREADME
are located outside the metadata folder if they were not generated with our metadata tool