Skip to content

Comments

Add Google Meet integration to meeting creation#151

Open
KunalPatilCode wants to merge 2 commits intoAOSSIE-Org:mainfrom
KunalPatilCode:feature/google-meet-link
Open

Add Google Meet integration to meeting creation#151
KunalPatilCode wants to merge 2 commits intoAOSSIE-Org:mainfrom
KunalPatilCode:feature/google-meet-link

Conversation

@KunalPatilCode
Copy link

@KunalPatilCode KunalPatilCode commented Feb 6, 2026

Closes #150

📝 Description

This pull request improves the meeting creation experience by enabling Google Meet link generation directly within the Ell-ena app. Users can now create a Google Meet link on the same screen without switching to the Google Meet or Google Calendar app. The generated Meet URL is automatically filled, making the overall flow faster, smoother, and more user-friendly.

🔧 Changes Made

  • Added in-app Google Meet link generation using Google Calendar API.
  • Eliminated the need to switch to the Google Meet or Google Calendar app.
  • Automatically generates and fills the Google Meet URL on the Create Meeting screen.
  • Improved overall meeting creation flow for a smoother user experience.

📷 Screenshots or Visual Changes (if applicable)

Screen.Recording.2026-02-06.204427.mp4

🤝 Collaboration

Collaborated with: (none)

✅ Checklist

  • I have read the contributing guidelines.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added necessary documentation (if applicable).
  • Any dependent changes have been merged and published in downstream modules.

Summary by CodeRabbit

  • New Features
    • Google Meet integration: generate Meet links from the meeting creation screen. A dedicated button creates a Meet link, auto-fills the meeting URL, shows progress while creating, prompts for Google sign-in as needed, and offers retry/error feedback. Existing date/time validation and meeting creation flow remain intact.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Adds one-click Google Meet link creation: a new GoogleMeetService performs OAuth and Calendar API calls; the meeting creation screen adds a button and flow to generate, populate, and validate Meet links with loading and error handling; pubspec adds required Google packages.

Changes

Cohort / File(s) Summary
Google Meet Service
lib/services/google_meet_service.dart
New service implementing Google Sign-In, OAuth credential handling, and Google Calendar API calls to create events with Hangouts Meet conferenceData and return the generated meet URL.
Meeting Creation Screen
lib/screens/meetings/create_meeting_screen.dart
UI/state changes: new _googleMeetService field and _isCreatingMeetLink flag, _createGoogleMeetLink flow (validation, duration parsing, call to service), full-width ElevatedButton with progress state, snackbar success/error with Retry, and updates to _urlController and validation.
Dependencies
pubspec.yaml
Adds google_sign_in ^6.2.1, googleapis ^13.1.0, and googleapis_auth ^1.4.1 to enable OAuth and Calendar API integration.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as Meeting Creation Screen
    participant Service as GoogleMeetService
    participant SignIn as GoogleSignIn
    participant CalendarAPI as Google Calendar API

    User->>UI: Tap "Create Google Meet Link"
    UI->>Service: createMeetLink(title, start, duration, description)
    Service->>SignIn: signIn() / request token
    SignIn-->>Service: auth token / credentials
    Service->>CalendarAPI: POST /calendars/primary/events (with conferenceData.createRequest)
    CalendarAPI-->>Service: Event created (hangoutLink / entryPoints)
    Service-->>UI: meet URL (or null on failure)
    UI->>UI: update _urlController, validate, show snackbar or retry
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped in code to fetch a meet,
One click—no more copy-paste repeat.
Tokens flutter, calendars spin,
A tiny link, a joyful grin,
Conferences sprout — carrot cake to eat! 🥕✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding Google Meet integration to the meeting creation screen, which is the primary focus of the changeset.
Linked Issues check ✅ Passed The pull request successfully implements all primary coding objectives from issue #150: one-click Meet link creation, automatic URL population, time validation, loading states, error handling with user feedback, OAuth 2.0 implementation, and Google Calendar API integration with conferenceDataVersion=1.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue #150: GoogleMeetService implements Meet link creation, the create_meeting_screen integrates the button and UI flow, and pubspec.yaml adds necessary dependencies. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@lib/screens/meetings/create_meeting_screen.dart`:
- Around line 107-109: The code currently treats a returned meet link `link` as
valid when non-null, but `createMeetLink` can return an empty string; update the
guard in the block that sets `_urlController.text` and calls `_checkUrl` to
verify the link is both non-null and non-empty (e.g., use `link != null &&
link.isNotEmpty`) before assigning `_urlController.text = link` and invoking
`_checkUrl(link)` so you don’t populate the field with an empty string.
- Around line 470-494: The "Create Meeting" button should be disabled while a
Google Meet link is being generated; update the Create Meeting button's
onPressed handler (the widget rendering the "Create Meeting" action in
create_meeting_screen.dart) to check _isCreatingMeetLink and set onPressed to
null when true (same pattern used in the Create Google Meet Link button),
ensuring the button is non-interactive during link creation and preventing form
submission mid-generation; keep any existing visual/loading behavior intact.
- Line 529: Add unit tests for GoogleMeetService and widget tests for the
CreateMeetingScreen's _createGoogleMeetLink flow: write unit tests that mock
Calendar API interactions used by GoogleMeetService (mock successful
createConference and error paths) and assert returned meeting link or thrown
errors; write widget tests that pump CreateMeetingScreen (or the widget
containing _createGoogleMeetLink), inject a mocked GoogleMeetService, simulate
button taps to trigger the create flow, and verify button enabled/disabled
states and SnackBar messages for success and failure cases. Use the service
class name GoogleMeetService and the private flow method name
_createGoogleMeetLink (or the public handler that calls it) to locate code, and
employ mocking libraries (mockito or mocktail) and flutter_test widget testers
to assert UI feedback and interactions.

In `@lib/services/google_meet_service.dart`:
- Around line 35-36: The code currently hardcodes a 1-hour expiry when
constructing AccessCredentials/AccessToken; instead, obtain the real expiry if
available from the GoogleSignInAuthentication response (e.g., an expiresIn
field) and use that to compute the DateTime, and if no expiry is exposed fall
back to a conservative short TTL (e.g., a few minutes) or force a fresh token
request; update the AccessCredentials/AccessToken creation to use
DateTime.now().toUtc().add(Duration(seconds: expiresIn)) when expiresIn is
present (and use a short fallback when absent) so the token lifetime is not
incorrectly assumed.
- Around line 69-76: The current return expression can yield an empty string
(EntryPoint(uri: '')) which is treated as non-null by the caller
(_createGoogleMeetLink) and causes a false-success; change the fallback so that
when there is no hangoutLink and no video entry in
createdEvent.conferenceData?.entryPoints you return null instead of an
EntryPoint with an empty uri. Locate the expression using
createdEvent.hangoutLink and the firstWhere(orElse: () => cal.EntryPoint(uri:
'')) and replace the orElse/uri usage with logic that returns null when no video
entry exists (or check for existence before returning .uri) so callers see a
true null and can handle the missing link correctly.
🧹 Nitpick comments (5)
lib/services/google_meet_service.dart (4)

7-11: Redundant scope: calendarEventsScope is a subset of calendarScope.

CalendarApi.calendarScope already grants full read/write access to calendars and events. Requesting calendarEventsScope alongside it is redundant and unnecessarily broadens the displayed consent screen text.

Suggested fix
   final GoogleSignIn _googleSignIn = GoogleSignIn(
     scopes: [
       cal.CalendarApi.calendarScope,
-      cal.CalendarApi.calendarEventsScope,
     ],
   );

20-32: Calling signIn() on every link creation forces the account picker each time.

signIn() presents the interactive sign-in/account-chooser flow. For returning users who already granted consent, this is unnecessary friction. Prefer signInSilently() first, falling back to signIn() only when needed.

Suggested fix
-      final account = await _googleSignIn.signIn();
+      var account = await _googleSignIn.signInSilently();
+      account ??= await _googleSignIn.signIn();
       if (account == null) return null;

30-31: Replace print() with debugPrint() or a structured logger.

Bare print() statements are not stripped in release builds and can leak operational details. The rest of the codebase (e.g., create_meeting_screen.dart Line 216) uses debugPrint.

Suggested fix
-        print('No access token available');
+        debugPrint('No access token available');
-      print('Error creating Google Meet link: $e');
+      debugPrint('Error creating Google Meet link: $e');

You'll also need to add:

import 'package:flutter/foundation.dart';

Also applies to: 80-82


86-88: Remove dead code or wire up the signOut() method.

The signOut() method in GoogleMeetService is defined but never called anywhere in the codebase. Either remove it if disconnecting Google accounts is not a required feature, or implement the UI wiring to invoke it when users attempt to sign out.

lib/screens/meetings/create_meeting_screen.dart (1)

51-90: Duplicated date/time combining and duration parsing logic.

Lines 63–69 and 82–90 are near-identical copies of the same logic in _createMeeting (Lines 171–177 and 180–187). Extract shared helpers to reduce duplication and prevent future drift.

Example helper extraction
DateTime _buildMeetingDateTime() {
  return DateTime(
    _selectedDate!.year,
    _selectedDate!.month,
    _selectedDate!.day,
    _selectedTime!.hour,
    _selectedTime!.minute,
  );
}

int _parseDuration() {
  try {
    final d = int.parse(_durationController.text.trim());
    return d > 0 ? d : 60;
  } catch (_) {
    return 60;
  }
}

Then use _buildMeetingDateTime() and _parseDuration() in both _createGoogleMeetLink and _createMeeting.

Comment on lines +107 to +109
if (link != null) {
_urlController.text = link;
_checkUrl(link);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against empty-string Meet link from the service.

As noted in the service review, createMeetLink can return "" instead of null. Even after fixing the service, a defensive check here prevents a broken link from silently populating the URL field.

Suggested fix
-    if (link != null) {
+    if (link != null && link.isNotEmpty) {
       _urlController.text = link;
       _checkUrl(link);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (link != null) {
_urlController.text = link;
_checkUrl(link);
if (link != null && link.isNotEmpty) {
_urlController.text = link;
_checkUrl(link);
🤖 Prompt for AI Agents
In `@lib/screens/meetings/create_meeting_screen.dart` around lines 107 - 109, The
code currently treats a returned meet link `link` as valid when non-null, but
`createMeetLink` can return an empty string; update the guard in the block that
sets `_urlController.text` and calls `_checkUrl` to verify the link is both
non-null and non-empty (e.g., use `link != null && link.isNotEmpty`) before
assigning `_urlController.text = link` and invoking `_checkUrl(link)` so you
don’t populate the field with an empty string.

Comment on lines +470 to +494
// Create Google Meet Link button
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _isCreatingMeetLink ? null : _createGoogleMeetLink,
icon: _isCreatingMeetLink
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Icon(Icons.video_call),
label: Text(
_isCreatingMeetLink ? 'Creating Meet Link...' : 'Create Google Meet Link',
),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade800,
padding: const EdgeInsets.symmetric(vertical: 16),
),
),
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Button UI and loading state look correct.

The button is properly disabled during link creation, shows a CircularProgressIndicator in-flight, and the label updates to indicate progress. This aligns with the issue requirement for loading states.

One minor UX note: the "Create Meeting" button (Line 512) is not disabled while _isCreatingMeetLink is true, so a user could submit the form mid-generation. Consider disabling it as well.

Suggested fix for the Create Meeting button
   SizedBox(
     width: double.infinity,
     child: ElevatedButton(
-      onPressed: _createMeeting,
+      onPressed: _isCreatingMeetLink ? null : _createMeeting,
🤖 Prompt for AI Agents
In `@lib/screens/meetings/create_meeting_screen.dart` around lines 470 - 494, The
"Create Meeting" button should be disabled while a Google Meet link is being
generated; update the Create Meeting button's onPressed handler (the widget
rendering the "Create Meeting" action in create_meeting_screen.dart) to check
_isCreatingMeetLink and set onPressed to null when true (same pattern used in
the Create Google Meet Link button), ensuring the button is non-interactive
during link creation and preventing form submission mid-generation; keep any
existing visual/loading behavior intact.

);
}
} No newline at end of file
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for test files and GoogleMeetService references
echo "=== Searching for test files with Google Meet related patterns ==="
find . -path './test*' -name '*.dart' -type f | head -50

echo -e "\n=== Searching for GoogleMeetService references in test files ==="
rg -i 'googlemeet|google_meet|createmeetlink' --type=dart -g '*test*' --max-count=20

echo -e "\n=== Checking if GoogleMeetService exists in lib ==="
rg -l 'class GoogleMeetService|GoogleMeetService' --type=dart -g '!test'

echo -e "\n=== Looking for test directory structure ==="
find . -type d -name test | head -20

Repository: AOSSIE-Org/Ell-ena

Length of output: 410


🏁 Script executed:

#!/bin/bash
# Search for any meet-related test files
echo "=== Looking for meet-related test files ==="
fd -e dart . test -i meet

echo -e "\n=== Checking all test files ==="
find . -path '*/test/*' -name '*test.dart' -o -name '*_test.dart' | wc -l

echo -e "\n=== Searching for GoogleMeetService in all dart files ==="
rg 'GoogleMeetService' --type=dart

Repository: AOSSIE-Org/Ell-ena

Length of output: 451


🏁 Script executed:

#!/bin/bash
echo "=== Contents of the only test file ==="
cat ./test/widget_test.dart

Repository: AOSSIE-Org/Ell-ena

Length of output: 1187


Add unit and widget tests for Google Meet functionality.

The codebase currently has only a basic counter smoke test. Tests for GoogleMeetService (mocking Calendar API interactions) and widget tests verifying button states and snackbar feedback for the _createGoogleMeetLink flow are missing and required per the PR objectives.

🤖 Prompt for AI Agents
In `@lib/screens/meetings/create_meeting_screen.dart` at line 529, Add unit tests
for GoogleMeetService and widget tests for the CreateMeetingScreen's
_createGoogleMeetLink flow: write unit tests that mock Calendar API interactions
used by GoogleMeetService (mock successful createConference and error paths) and
assert returned meeting link or thrown errors; write widget tests that pump
CreateMeetingScreen (or the widget containing _createGoogleMeetLink), inject a
mocked GoogleMeetService, simulate button taps to trigger the create flow, and
verify button enabled/disabled states and SnackBar messages for success and
failure cases. Use the service class name GoogleMeetService and the private flow
method name _createGoogleMeetLink (or the public handler that calls it) to
locate code, and employ mocking libraries (mockito or mocktail) and flutter_test
widget testers to assert UI feedback and interactions.

Comment on lines +35 to +36
final credentials = AccessCredentials(
AccessToken('Bearer', accessToken, DateTime.now().add(const Duration(hours: 1)).toUtc()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded token expiry is unreliable.

The actual access token lifespan is determined by Google's auth server and may differ from the assumed 1-hour window. google_sign_in does not directly expose the real expiry. If the token is already partially expired by the time this code runs, the AccessCredentials object will carry a stale estimate. This is unlikely to cause issues for a single short-lived API call, but be aware this is a best-guess.

🤖 Prompt for AI Agents
In `@lib/services/google_meet_service.dart` around lines 35 - 36, The code
currently hardcodes a 1-hour expiry when constructing
AccessCredentials/AccessToken; instead, obtain the real expiry if available from
the GoogleSignInAuthentication response (e.g., an expiresIn field) and use that
to compute the DateTime, and if no expiry is exposed fall back to a conservative
short TTL (e.g., a few minutes) or force a fresh token request; update the
AccessCredentials/AccessToken creation to use
DateTime.now().toUtc().add(Duration(seconds: expiresIn)) when expiresIn is
present (and use a short fallback when absent) so the token lifetime is not
incorrectly assumed.

Comment on lines +69 to +76
// Return the meet link
return createdEvent.hangoutLink ??
createdEvent.conferenceData?.entryPoints
?.firstWhere(
(e) => e.entryPointType == 'video',
orElse: () => cal.EntryPoint(uri: ''),
)
.uri;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fallback can return an empty string instead of null, breaking the caller's null-check.

When hangoutLink is null and entryPoints has no video entry, orElse returns EntryPoint(uri: ''). The caller (_createGoogleMeetLink) checks if (link != null) — an empty string passes that check, so the URL field gets set to "" and a "success" snackbar is shown with no usable link.

Suggested fix
-        return createdEvent.hangoutLink ??
-            createdEvent.conferenceData?.entryPoints
-                ?.firstWhere(
-                  (e) => e.entryPointType == 'video',
-                  orElse: () => cal.EntryPoint(uri: ''),
-                )
-                .uri;
+        final link = createdEvent.hangoutLink ??
+            createdEvent.conferenceData?.entryPoints
+                ?.firstWhere(
+                  (e) => e.entryPointType == 'video',
+                  orElse: () => cal.EntryPoint(),
+                )
+                .uri;
+        return (link != null && link.isNotEmpty) ? link : null;
🤖 Prompt for AI Agents
In `@lib/services/google_meet_service.dart` around lines 69 - 76, The current
return expression can yield an empty string (EntryPoint(uri: '')) which is
treated as non-null by the caller (_createGoogleMeetLink) and causes a
false-success; change the fallback so that when there is no hangoutLink and no
video entry in createdEvent.conferenceData?.entryPoints you return null instead
of an EntryPoint with an empty uri. Locate the expression using
createdEvent.hangoutLink and the firstWhere(orElse: () => cal.EntryPoint(uri:
'')) and replace the orElse/uri usage with logic that returns null when no video
entry exists (or check for existence before returning .uri) so callers see a
true null and can handle the missing link correctly.

@KunalPatilCode
Copy link
Author

Hi @SharkyBytes ,
I’ve opened a PR that adds in-app Google Meet link generation to Ell-ena, improving the meeting creation flow.
If there’s any scope for improvement or best practices I should follow, I’d really appreciate your feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FEATURE REQUEST: One-Click Google Meet Link Generation

1 participant