Skip to content

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 Google Meet links directly within the meeting creation interface. A new button creates a Meet link and automatically populates the meeting URL field. Includes validation, progress indication, and error handling with retry options.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

This pull request integrates Google Meet link creation directly into the meeting creation screen. A new GoogleMeetService handles OAuth authentication and Calendar API calls to generate Meet links, while the UI adds a button to trigger link creation with loading feedback and validation.

Changes

Cohort / File(s) Summary
Google Meet Service
lib/services/google_meet_service.dart
New service implementing OAuth 2.0 sign-in, Google Calendar API integration with Hangouts Meet conferenceData, access token management, and error handling to create meeting links and retrieve their URLs.
Meeting Creation Screen Integration
lib/screens/meetings/create_meeting_screen.dart
Adds GoogleMeetService initialization, new state flag for creation progress, full-width button with icon triggering link creation, CircularProgressIndicator during creation, date/time validation, and snackbar feedback for success/failure with retry capability.
Dependencies
pubspec.yaml
Adds three new packages: google_sign_in ^6.2.1, googleapis ^13.1.0, and googleapis_auth ^1.4.1 to support Google authentication and Calendar API interactions.

Sequence Diagram

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

    User->>UI: Tap "Create Google Meet Link"
    UI->>Service: createMeetLink(title, description, dateTime, duration)
    Service->>GoogleSignIn: signIn()
    GoogleSignIn-->>Service: Authentication token
    Service->>CalendarAPI: POST /calendars/primary/events<br/>(with conferenceData.createRequest)
    CalendarAPI-->>Service: Event created with hangoutLink
    Service-->>UI: Meet link URL
    UI->>UI: Update _urlController & validate
    UI->>User: Show success snackbar with link
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 A button taps, and Meet links spring to life,
No more copy-paste strife!
Google Calendar dances with our app,
OAuth tokens seal the gap—
One click, and conferences bloom! ✨

🚥 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 PR title 'Add Google Meet integration to meeting creation' accurately summarizes the main objective: introducing Google Meet link generation to the meeting creation screen.
Linked Issues check ✅ Passed The implementation satisfies all coding objectives from issue #150: Google Meet link creation via button, automatic URL population, meeting time validation, loading states, error handling with user feedback, OAuth 2.0 integration, and Google Calendar API with Hangouts Meet conference creation.
Out of Scope Changes check ✅ Passed All changes align with requirements: GoogleMeetService implements the API integration, create_meeting_screen.dart adds the button UI and link creation flow, and pubspec.yaml adds necessary dependencies (google_sign_in, googleapis, googleapis_auth).
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