Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions app/lib/desktop/pages/settings/desktop_settings_modal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ import 'package:omi/utils/other/temp.dart';
import 'package:omi/utils/responsive/responsive_helper.dart';
import 'package:omi/services/notifications/daily_reflection_notification.dart';
import 'package:provider/provider.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';

import 'package:omi/services/desktop_update_service.dart';

enum SettingsSection {
account,
plansAndBilling,
Expand Down Expand Up @@ -2290,6 +2293,38 @@ class _DesktopSettingsModalState extends State<DesktopSettingsModal> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Software Update section
_buildSettingsGroup(
title: 'SOFTWARE UPDATE',
children: [
FutureBuilder<PackageInfo>(
future: PackageInfo.fromPlatform(),
builder: (context, snapshot) {
final version = snapshot.hasData
? 'v${snapshot.data!.version} (${snapshot.data!.buildNumber})'
: '...';
return _buildSettingsRow(
title: 'Current Version',
subtitle: version,
showChevron: false,
onTap: () {},
);
},
),
_buildSettingsRow(
title: 'Check for Updates...',
subtitle: DesktopUpdateService().isAvailable
? 'Checks every 3 hours automatically'
: 'Update service not initialized',
onTap: () {
DesktopUpdateService().checkForUpdates();
},
),
],
),

const SizedBox(height: 24),

_buildSettingsGroup(
title: context.l10n.links,
children: [
Expand Down
22 changes: 22 additions & 0 deletions app/lib/services/desktop_update_service.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:io';

import 'package:auto_updater/auto_updater.dart';
import 'package:flutter/services.dart';

import 'package:omi/env/env.dart';
import 'package:omi/utils/logger.dart';
Expand All @@ -12,6 +13,9 @@ class DesktopUpdateService {
factory DesktopUpdateService() => _instance;
DesktopUpdateService._internal();

/// Method channel for receiving update requests from native code
static const _channel = MethodChannel('com.omi/updates');

bool _initialized = false;

String get _platform {
Expand Down Expand Up @@ -41,6 +45,9 @@ class DesktopUpdateService {
await autoUpdater.setFeedURL(feedURL);
await autoUpdater.setScheduledCheckInterval(10800); // Check every 3 hours

// Set up method channel handler for native menu "Check for Updates"
_channel.setMethodCallHandler(_handleMethodCall);

// Check for updates in background on startup
await autoUpdater.checkForUpdates(inBackground: true);

Expand All @@ -50,6 +57,21 @@ class DesktopUpdateService {
}
}

/// Handle method calls from native code
Future<dynamic> _handleMethodCall(MethodCall call) async {
switch (call.method) {
case 'checkForUpdates':
Logger.debug('Check for updates triggered from native menu');
await checkForUpdates();
return null;
default:
throw PlatformException(
code: 'Unimplemented',
details: 'Method ${call.method} not implemented',
);
}
}

/// Manually check for updates
Future<void> checkForUpdates() async {
if (!_initialized) {
Expand Down
20 changes: 20 additions & 0 deletions app/macos/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import app_links
class AppDelegate: FlutterAppDelegate {
// Method channel for direct URL delivery to Flutter
private var urlChannel: FlutterMethodChannel?

// Method channel for update checking
private var updateChannel: FlutterMethodChannel?

// Required for app_links plugin to register Apple Event handler for URL schemes
override func applicationWillFinishLaunching(_ notification: Notification) {
Expand Down Expand Up @@ -66,6 +69,16 @@ class AppDelegate: FlutterAppDelegate {

super.applicationDidFinishLaunching(aNotification)

// Initialize method channels now that Flutter engine is ready
if let mainWindow = NSApp.windows.first(where: { $0 is MainFlutterWindow }) as? MainFlutterWindow,
let flutterViewController = mainWindow.contentViewController as? FlutterViewController {
updateChannel = FlutterMethodChannel(
name: "com.omi/updates",
binaryMessenger: flutterViewController.engine.binaryMessenger
)
NSLog("DEBUG: Initialized updates method channel")
}

// Delay to check if app was launched hidden (e.g., as a login item)
DispatchQueue.main.async {
if !NSApp.isHidden {
Expand Down Expand Up @@ -106,4 +119,11 @@ class AppDelegate: FlutterAppDelegate {
AppLinks.shared.handleLink(link: url.absoluteString)
}
}

// MARK: - Check for Updates

@IBAction func checkForUpdates(_ sender: Any) {
updateChannel?.invokeMethod("checkForUpdates", arguments: nil)
NSLog("DEBUG: Triggered check for updates via menu")
}
}
7 changes: 7 additions & 0 deletions app/macos/Runner/Base.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Check for Updates…" id="9fK-3T-uXw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="checkForUpdates:" target="Voe-Tx-rLC" id="kRq-8H-2mN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aWz-Qf-7bP"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
Expand Down