Skip to content

Commit

Permalink
Add a way to check for updates, implement the menu bar
Browse files Browse the repository at this point in the history
  • Loading branch information
blopker committed Apr 14, 2024
1 parent 60e7716 commit 83a2102
Show file tree
Hide file tree
Showing 10 changed files with 332 additions and 25 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Drag and drop images into the window, images will automatically start compressin

## Privacy

All compression is done locally on your machine. Alic also does not have any analytics or tracking, including error reporting. Alic does not send any data to the internet. Because of this, Alic will not automatically update. You will need to check the [releases page][project-release-url] for updates.
All compression is done locally on your machine. Alic also does not have any analytics or tracking, including error reporting. Alic does not passively send any data to the internet. Because of this, Alic will not automatically update. You will need to check the [releases page][project-release-url] for updates, or by clicking "Check for Updates" in the app menu bar.

## Differences from ImageOptim

Expand All @@ -70,6 +70,7 @@ All compression is done locally on your machine. Alic also does not have any ana
## Roadmap

- [ ] Get the app signed with an Apple Developer ID
- [x] Add a way to check for updates
- [x] Add support for different optimization levels
- [ ] Add support for lossless compression
- [x] Add support for dropping directories
Expand Down
49 changes: 26 additions & 23 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import 'package:window_manager/window_manager.dart';

import './config.dart';
import 'config.dart';
import 'glass.dart';
import 'menubar.dart';
import 'theme.dart';

void main() async {
Expand Down Expand Up @@ -59,29 +60,31 @@ class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ImageDropRegion(
onDrop: (files) {
debugPrint('Dropped: $files');
ImageFiles.add(files);
},
dropOverlay: Container(
color: Colors.transparent,
child: const Center(
child: Icon(
Icons.file_download,
size: 400,
body: MacMenuBar(
child: ImageDropRegion(
onDrop: (files) {
debugPrint('Dropped: $files');
ImageFiles.add(files);
},
dropOverlay: Container(
color: Colors.transparent,
child: const Center(
child: Icon(
Icons.file_download,
size: 400,
),
)).asGlass(
tintColor: Colors.transparent,
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: FilesTable(),
),
)).asGlass(
tintColor: Colors.transparent,
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: FilesTable(),
),
BottomBar(),
],
BottomBar(),
],
),
),
),
);
Expand Down
131 changes: 131 additions & 0 deletions lib/menubar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';

import 'strings.dart';
import 'update.dart';
import 'widgets.dart';

enum MenuSelection {
about,
updates,
showMessage,
}

class MacMenuBar extends StatefulWidget {
const MacMenuBar({super.key, required this.child});

final Widget child;

@override
State<MacMenuBar> createState() => _MacMenuBarState();
}

class _MacMenuBarState extends State<MacMenuBar> {
void _checkForUpdates() async {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Checking for updates...'),
),
);

// Check for updates
Update? update;
String? error;
try {
update = await checkForUpdate();
} catch (e) {
error = e.toString();
}
if (!mounted) return;
ScaffoldMessenger.of(context).removeCurrentSnackBar();
if (update != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: const Duration(seconds: 20),
showCloseIcon: true,
content: Text('Update available: ${update.version}'),
action: SnackBarAction(
label: 'Download',
onPressed: () {
update!.open();
},
),
),
);
} else if (error != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
showCloseIcon: true,
content: Text('Error: $error'),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
showCloseIcon: true,
content: Text('No updates available'),
),
);
}
}

void _handleMenuSelection(MenuSelection value) async {
final packageInfo = await PackageInfo.fromPlatform();
if (!mounted) return;
switch (value) {
case MenuSelection.about:
showAboutDialog(
context: context,
applicationName: packageInfo.appName,
applicationVersion:
"${packageInfo.version}+${packageInfo.buildNumber}",
children: const [
TextLink(
url: Strings.repoURL,
text: 'View on GitHub',
)
],
);
case MenuSelection.showMessage:
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Hello from the menu!'),
),
);
case MenuSelection.updates:
_checkForUpdates();
}
}

@override
Widget build(BuildContext context) {
return PlatformMenuBar(
menus: [
PlatformMenu(
label: 'Alic',
menus: [
PlatformMenuItemGroup(
members: [
PlatformMenuItem(
label: 'About',
onSelected: () {
_handleMenuSelection(MenuSelection.about);
},
),
PlatformMenuItem(
label: 'Check for Updates...',
onSelected: () {
_handleMenuSelection(MenuSelection.updates);
},
),
],
),
const PlatformProvidedMenuItem(
type: PlatformProvidedMenuItemType.quit),
],
),
],
child: widget.child,
);
}
}
7 changes: 7 additions & 0 deletions lib/strings.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Strings {
static const downloadURL =
'https://github.com/blopker/alic/releases/latest/download/Alic.Image.Compressor.dmg';
static const githubAPI =
'https://api.github.com/repos/blopker/alic/tags?per_page=10';
static const repoURL = 'https://github.com/blopker/alic/';
}
66 changes: 66 additions & 0 deletions lib/update.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';

import 'strings.dart';

@immutable
class Update {
final String version;
final int buildNumber;
final String url;

const Update(
{required this.version, required this.url, required this.buildNumber});

void open() {
launchUrl(Uri.parse(url));
}
}

Future<Update> getLatestBuildNumber() async {
final uri = Uri.parse(Strings.githubAPI);
final response =
await http.get(uri, headers: {"Accept": "application/vnd.github+json"});

if (response.statusCode == 200) {
final List decodedData = jsonDecode(response.body);
final tags = decodedData.map((e) => e['name'].toString()).toList();
final latestBuild = tags.firstWhere((element) => element.contains('+'));
final buildNumber = int.parse(latestBuild.split('+').last);
return Update(
version: latestBuild,
buildNumber: buildNumber,
url: Strings.downloadURL,
);
} else {
throw Exception('Failed to fetch tags: ${response.statusCode}');
}
}

Future<Update?> checkForUpdate({bool force = false}) async {
// 1. Get your current build number
PackageInfo packageInfo = await PackageInfo.fromPlatform();
int currentBuildNumber = int.parse(packageInfo.buildNumber);

// 2. Get the latest build number
final update = await getLatestBuildNumber();
int latestBuildNumber = update.buildNumber;

// 3. Compare
if (latestBuildNumber > currentBuildNumber || force) {
debugPrint('Update available: ${update.version}');
return update;
} else {
debugPrint('You have the latest version');
return null;
}
}

void main() async {
final buildNumber = await getLatestBuildNumber();
print('Latest Build Number: $buildNumber');
}
25 changes: 25 additions & 0 deletions lib/widgets.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

class TextLink extends StatelessWidget {
const TextLink({super.key, required this.url, required this.text});

final String url;
final String text;

@override
Widget build(BuildContext context) {
return RichText(
text: TextSpan(
text: text,
style: const TextStyle(
color: Colors.blue,
),
recognizer: TapGestureRecognizer()
..onTap = () {
launchUrl(Uri.parse(url));
},
));
}
}
2 changes: 2 additions & 0 deletions macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import open_file_macos
import package_info_plus
import screen_retriever
import super_native_extensions
import url_launcher_macos
import window_manager

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
Expand All @@ -22,5 +23,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
}
6 changes: 6 additions & 0 deletions macos/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ PODS:
- FlutterMacOS
- super_native_extensions (0.0.1):
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
- window_manager (0.2.0):
- FlutterMacOS

Expand All @@ -29,6 +31,7 @@ DEPENDENCIES:
- rust_builder (from `Flutter/ephemeral/.symlinks/plugins/rust_builder/macos`)
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
- super_native_extensions (from `Flutter/ephemeral/.symlinks/plugins/super_native_extensions/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)

EXTERNAL SOURCES:
Expand All @@ -50,6 +53,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
super_native_extensions:
:path: Flutter/ephemeral/.symlinks/plugins/super_native_extensions/macos
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
window_manager:
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos

Expand All @@ -63,6 +68,7 @@ SPEC CHECKSUMS:
rust_builder: d6115f3c96b081d2c66f05771d1c2509559e2073
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
super_native_extensions: 85efee3a7495b46b04befcfc86ed12069264ebf3
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8

PODFILE CHECKSUM: f0c21717cb7ee9112f915044c74bfceb5b12e02a
Expand Down
Loading

0 comments on commit 83a2102

Please sign in to comment.