Compare commits

..

No commits in common. "9311bfc3b566d8a05b520f9af251f32ec0803200" and "21a1d4a2ad649b4fc47b74ae19bcc7ed4839eb55" have entirely different histories.

12 changed files with 182 additions and 183 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 KiB

View File

@ -89,14 +89,19 @@ void main() async {
await EasyLocalization.ensureInitialized(); await EasyLocalization.ensureInitialized();
if (!kIsWeb && !Platform.isLinux) { if (!kIsWeb && !Platform.isLinux) {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
} }
GoRouter.optionURLReflectsImperativeAPIs = true; GoRouter.optionURLReflectsImperativeAPIs = true;
usePathUrlStrategy(); usePathUrlStrategy();
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) { if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
Workmanager().initialize(appBackgroundDispatcher, isInDebugMode: kDebugMode); Workmanager().initialize(
appBackgroundDispatcher,
isInDebugMode: kDebugMode,
);
if (Platform.isAndroid) { if (Platform.isAndroid) {
Workmanager().registerPeriodicTask( Workmanager().registerPeriodicTask(
"widget-update-random-post", "widget-update-random-post",
@ -109,7 +114,8 @@ void main() async {
} }
if (!kIsWeb && Platform.isAndroid) { if (!kIsWeb && Platform.isAndroid) {
final ImagePickerPlatform imagePickerImplementation = ImagePickerPlatform.instance; final ImagePickerPlatform imagePickerImplementation =
ImagePickerPlatform.instance;
if (imagePickerImplementation is ImagePickerAndroid) { if (imagePickerImplementation is ImagePickerAndroid) {
imagePickerImplementation.useAndroidPhotoPicker = true; imagePickerImplementation.useAndroidPhotoPicker = true;
} }
@ -126,7 +132,12 @@ class SolianApp extends StatelessWidget {
return ResponsiveBreakpoints.builder( return ResponsiveBreakpoints.builder(
child: EasyLocalization( child: EasyLocalization(
path: 'assets/translations', path: 'assets/translations',
supportedLocales: [Locale('en', 'US'), Locale('zh', 'CN'), Locale('zh', 'TW'), Locale('zh', 'HK')], supportedLocales: [
Locale('en', 'US'),
Locale('zh', 'CN'),
Locale('zh', 'TW'),
Locale('zh', 'HK'),
],
fallbackLocale: Locale('en', 'US'), fallbackLocale: Locale('en', 'US'),
useFallbackTranslations: true, useFallbackTranslations: true,
assetLoader: JsonAssetLoader(), assetLoader: JsonAssetLoader(),
@ -201,7 +212,10 @@ class _AppDelegate extends StatelessWidget {
], ],
routerConfig: appRouter, routerConfig: appRouter,
builder: (context, child) { builder: (context, child) {
return _AppSplashScreen(key: const Key('global-splash-screen'), child: child!); return _AppSplashScreen(
key: const Key('global-splash-screen'),
child: child!,
);
}, },
); );
} }
@ -225,7 +239,8 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
if (prefs.containsKey('first_boot_time')) { if (prefs.containsKey('first_boot_time')) {
final rawTime = prefs.getString('first_boot_time'); final rawTime = prefs.getString('first_boot_time');
final time = DateTime.tryParse(rawTime ?? ''); final time = DateTime.tryParse(rawTime ?? '');
if (time != null && time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) { if (time != null &&
time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) {
final inAppReview = InAppReview.instance; final inAppReview = InAppReview.instance;
if (prefs.getBool('rating_requested') == true) return; if (prefs.getBool('rating_requested') == true) return;
if (await inAppReview.isAvailable()) { if (await inAppReview.isAvailable()) {
@ -246,17 +261,30 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
final info = await PackageInfo.fromPlatform(); final info = await PackageInfo.fromPlatform();
final localVersionString = '${info.version}+${info.buildNumber}'; final localVersionString = '${info.version}+${info.buildNumber}';
final resp = await Dio( final resp = await Dio(
BaseOptions(sendTimeout: const Duration(seconds: 60), receiveTimeout: const Duration(seconds: 60)), BaseOptions(
).get('https://api.github.com/repos/Solsynth/HyperNet.Surface/releases/latest'); sendTimeout: const Duration(seconds: 60),
receiveTimeout: const Duration(seconds: 60),
),
).get(
'https://api.github.com/repos/Solsynth/HyperNet.Surface/releases/latest',
);
final remoteVersionString = resp.data?['tag_name'] ?? '0.0.0+0'; final remoteVersionString = resp.data?['tag_name'] ?? '0.0.0+0';
final remoteVersion = Version.parse(remoteVersionString.split('+').first); final remoteVersion = Version.parse(remoteVersionString.split('+').first);
final localVersion = Version.parse(localVersionString.split('+').first); final localVersion = Version.parse(localVersionString.split('+').first);
final remoteBuildNumber = int.tryParse(remoteVersionString.split('+').last) ?? 0; final remoteBuildNumber =
final localBuildNumber = int.tryParse(localVersionString.split('+').last) ?? 0; int.tryParse(remoteVersionString.split('+').last) ?? 0;
logging.info("[Update] Local: $localVersionString, Remote: $remoteVersionString"); final localBuildNumber =
if ((remoteVersion > localVersion || remoteBuildNumber > localBuildNumber) && mounted) { int.tryParse(localVersionString.split('+').last) ?? 0;
logging.info(
"[Update] Local: $localVersionString, Remote: $remoteVersionString");
if ((remoteVersion > localVersion ||
remoteBuildNumber > localBuildNumber) &&
mounted) {
final config = context.read<ConfigProvider>(); final config = context.read<ConfigProvider>();
config.setUpdate(remoteVersionString, resp.data?['body'] ?? 'No changelog'); config.setUpdate(
remoteVersionString,
resp.data?['body'] ?? 'No changelog',
);
logging.info("[Update] Update available: $remoteVersionString"); logging.info("[Update] Update available: $remoteVersionString");
} }
} catch (e) { } catch (e) {
@ -294,21 +322,19 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
_setPhaseText('websocket'); _setPhaseText('websocket');
final ws = context.read<WebSocketProvider>(); final ws = context.read<WebSocketProvider>();
await ws.tryConnect(); await ws.tryConnect();
if (!mounted) return;
_setPhaseText('notification');
final notify = context.read<NotificationProvider>();
notify.listen();
await notify.registerPushNotifications();
if (!mounted) return;
_setPhaseText('keyPair');
final kp = context.read<KeyPairProvider>();
try { try {
if (!mounted) return;
_setPhaseText('keyPair');
final kp = context.read<KeyPairProvider>();
await kp.reloadActive(); await kp.reloadActive();
kp.listen(); kp.listen();
} catch (_) {} } catch (_) {}
if (ua.isAuthorized) { if (ua.isAuthorized) {
if (!mounted) return;
_setPhaseText('notification');
final notify = context.read<NotificationProvider>();
notify.listen();
try {
await notify.registerPushNotifications();
} catch (_) {}
if (!mounted) return; if (!mounted) return;
_setPhaseText('stickers'); _setPhaseText('stickers');
final sticker = context.read<SnStickerProvider>(); final sticker = context.read<SnStickerProvider>();
@ -344,19 +370,35 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
final Menu _appTrayMenu = Menu( final Menu _appTrayMenu = Menu(
items: [ items: [
MenuItem(key: 'version_label', label: 'Solian', disabled: true), MenuItem(
key: 'version_label',
label: 'Solian',
disabled: true,
),
MenuItem.separator(), MenuItem.separator(),
MenuItem.checkbox(checked: false, key: 'mute_notification', label: 'trayMenuMuteNotification'.tr()), MenuItem.checkbox(
checked: false,
key: 'mute_notification',
label: 'trayMenuMuteNotification'.tr(),
),
MenuItem.separator(), MenuItem.separator(),
MenuItem(key: 'window_show', label: 'trayMenuShow'.tr()), MenuItem(
MenuItem(key: 'exit', label: 'trayMenuExit'.tr()), key: 'window_show',
label: 'trayMenuShow'.tr(),
),
MenuItem(
key: 'exit',
label: 'trayMenuExit'.tr(),
),
], ],
); );
Future<void> _trayInitialization() async { Future<void> _trayInitialization() async {
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return; if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
final icon = Platform.isWindows ? 'assets/icon/tray-icon.ico' : 'assets/icon/tray-icon.png'; final icon = Platform.isWindows
? 'assets/icon/tray-icon.ico'
: 'assets/icon/tray-icon.png';
final appVersion = await PackageInfo.fromPlatform(); final appVersion = await PackageInfo.fromPlatform();
trayManager.addListener(this); trayManager.addListener(this);
@ -374,7 +416,10 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
Future<void> _notifyInitialization() async { Future<void> _notifyInitialization() async {
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return; if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
await localNotifier.setup(appName: 'Solian', shortcutPolicy: ShortcutPolicy.requireCreate); await localNotifier.setup(
appName: 'Solian',
shortcutPolicy: ShortcutPolicy.requireCreate,
);
} }
AppLifecycleListener? _appLifecycleListener; AppLifecycleListener? _appLifecycleListener;
@ -385,7 +430,9 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
_isBusy = true; _isBusy = true;
if (!kIsWeb && !(Platform.isIOS || Platform.isAndroid)) { if (!kIsWeb && !(Platform.isIOS || Platform.isAndroid)) {
_appLifecycleListener = AppLifecycleListener(onExitRequested: _onExitRequested); _appLifecycleListener = AppLifecycleListener(
onExitRequested: _onExitRequested,
);
} }
_trayInitialization(); _trayInitialization();
@ -485,49 +532,43 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
} }
}); });
return SizeChangedLayoutNotifier( return SizeChangedLayoutNotifier(
child: child: _isBusy
_isBusy ? Material(
? Material( key: Key('app-splash-screen-$_isBusy'),
key: Key('app-splash-screen-$_isBusy'), child: Stack(
child: Stack( children: [
children: [ Center(
Container( child: Container(
decoration: BoxDecoration( constraints: const BoxConstraints(
image: DecorationImage( maxWidth: 240,
image: AssetImage('assets/icon/kanban-1st.jpg'), ),
fit: BoxFit.cover, child: Column(
opacity: 0.1, mainAxisSize: MainAxisSize.min,
), children: [
color: Theme.of(context).colorScheme.surface, Image.asset(
backgroundBlendMode: BlendMode.darken, 'assets/icon/icon.png',
width: 64,
height: 64,
color:
Theme.of(context).colorScheme.onSurface,
),
Text('Solar Network').bold(),
AppVersionLabel(),
Gap(8),
Text(
_phaseText,
textAlign: TextAlign.center,
),
Gap(16),
const LinearProgressIndicator(),
],
), ),
), ),
Center( ),
child: Container( ],
constraints: const BoxConstraints(maxWidth: 240), ),
child: Column( )
mainAxisSize: MainAxisSize.min, : widget.child,
children: [
Image.asset(
'assets/icon/icon.png',
width: 64,
height: 64,
color: Theme.of(context).colorScheme.onSurface,
),
Text('Solar Network').bold(),
AppVersionLabel(),
Gap(8),
Text(_phaseText, textAlign: TextAlign.center),
Gap(16),
const LinearProgressIndicator(),
],
),
),
),
],
),
)
: widget.child,
); );
}, },
), ),

View File

@ -48,11 +48,13 @@ class NotificationProvider extends ChangeNotifier {
var deviceUuid = await FlutterUdid.consistentUdid; var deviceUuid = await FlutterUdid.consistentUdid;
if (deviceUuid.isEmpty) { if (deviceUuid.isEmpty) {
logging.warning('[Push Notification] Unable to active push notifications, couldn\'t get device uuid'); logging.warning(
'[Push Notification] Unable to active push notifications, couldn\'t get device uuid');
return; return;
} else { } else {
logging.info('[Push Notification] Device UUID is $deviceUuid'); logging.info('[Push Notification] Device UUID is $deviceUuid');
logging.info('[Push Notification] Registering device push notifications...'); logging
.info('[Push Notification] Registering device push notifications...');
} }
if (Platform.isIOS || Platform.isMacOS) { if (Platform.isIOS || Platform.isMacOS) {
@ -64,14 +66,14 @@ class NotificationProvider extends ChangeNotifier {
} }
logging.info('[Push Notification] Device Push Token is $token'); logging.info('[Push Notification] Device Push Token is $token');
try { await _sn.client.post(
await _sn.client.post( '/cgi/id/notifications/subscription',
'/cgi/id/notifications/subscription', data: {
data: {'provider': provider, 'device_token': token, 'device_id': deviceUuid}, 'provider': provider,
); 'device_token': token,
} catch (err) { 'device_id': deviceUuid,
logging.error('[Push Notification] Unable to register push notifications: $err'); },
} );
} }
int showingCount = 0; int showingCount = 0;
@ -89,7 +91,8 @@ class NotificationProvider extends ChangeNotifier {
final doHaptic = _cfg.prefs.getBool(kAppNotifyWithHaptic) ?? true; final doHaptic = _cfg.prefs.getBool(kAppNotifyWithHaptic) ?? true;
if (doHaptic) HapticFeedback.mediumImpact(); if (doHaptic) HapticFeedback.mediumImpact();
if (notification.topic == 'messaging.message' && skippableNotifyChannel != null) { if (notification.topic == 'messaging.message' &&
skippableNotifyChannel != null) {
if (notification.metadata['channel_id'] != null && if (notification.metadata['channel_id'] != null &&
notification.metadata['channel_id'] == skippableNotifyChannel) { notification.metadata['channel_id'] == skippableNotifyChannel) {
return; return;

View File

@ -4,7 +4,8 @@ import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:surface/logger.dart'; import 'package:surface/logger.dart';
const kTranslateApiBaseUrl = 'https://translate.solsynth.dev'; // TODO self host translate api
const kTranslateApiBaseUrl = 'https://translate.disroot.org';
class SnTranslator { class SnTranslator {
final Dio client = Dio( final Dio client = Dio(

View File

@ -306,7 +306,9 @@ class _UnauthorizedAccountScreen extends StatelessWidget {
GoRouter.of(context).pushNamed('authLogin').then((value) { GoRouter.of(context).pushNamed('authLogin').then((value) {
if (value == true && context.mounted) { if (value == true && context.mounted) {
final ua = context.read<UserProvider>(); final ua = context.read<UserProvider>();
ua.refreshUser(); context.showSnackbar('loginSuccess'.tr(args: [
'@${ua.user?.name} (${ua.user?.nick})',
]));
} }
}); });
}, },

View File

@ -43,7 +43,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
final captchaTk = await Navigator.of(context, rootNavigator: true).push( final captchaTk = await Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => CaptchaScreen(), builder: (context) => TurnstileScreen(),
), ),
); );
if (captchaTk == null) return; if (captchaTk == null) return;

View File

@ -1,67 +1,23 @@
import 'dart:html' as html;
import 'dart:ui_web' as ui;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/providers/config.dart'; import 'package:surface/providers/config.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
class CaptchaScreen extends StatefulWidget { class TurnstileScreen extends StatefulWidget {
const CaptchaScreen({ const TurnstileScreen({
super.key, super.key,
}); });
@override @override
State<CaptchaScreen> createState() => _CaptchaScreenState(); State<TurnstileScreen> createState() => _TurnstileScreenState();
} }
class _CaptchaScreenState extends State<CaptchaScreen> { class _TurnstileScreenState extends State<TurnstileScreen> {
@override
void initState() {
super.initState();
if (kIsWeb) {
_setupWebListener();
}
}
void _setupWebListener() {
html.window.onMessage.listen((event) {
if (event.data != null && event.data is String) {
final message = event.data as String;
if (message.startsWith("captcha_tk=")) {
String token = message.replaceFirst("captcha_tk=", "");
Navigator.pop(context, token);
}
}
});
// Create an iframe for the captcha page
final iframe = html.IFrameElement()
..src = '${context.read<ConfigProvider>().serverUrl}/captcha?redirect_uri=solink://captcha'
..style.border = 'none'
..width = '100%'
..height = '100%';
html.document.body!.append(iframe);
ui.platformViewRegistry.registerViewFactory(
'captcha-iframe',
(int viewId) => iframe,
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final cfg = context.read<ConfigProvider>(); final cfg = context.read<ConfigProvider>();
if (kIsWeb) {
return AppScaffold(
appBar: AppBar(title: Text("reCaptcha").tr()),
body: HtmlElementView(viewType: 'captcha-iframe'),
);
}
return AppScaffold( return AppScaffold(
appBar: AppBar(title: Text("reCaptcha").tr()), appBar: AppBar(title: Text("reCaptcha").tr()),
body: InAppWebView( body: InAppWebView(

View File

@ -511,7 +511,7 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
Future<void> _doCheckIn() async { Future<void> _doCheckIn() async {
final captchaTk = await Navigator.of(context, rootNavigator: true).push( final captchaTk = await Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => CaptchaScreen(), builder: (context) => TurnstileScreen(),
), ),
); );
if (captchaTk == null) return; if (captchaTk == null) return;

View File

@ -16,12 +16,7 @@ class ConnectionIndicator extends StatelessWidget {
final ws = context.watch<WebSocketProvider>(); final ws = context.watch<WebSocketProvider>();
final cfg = context.watch<ConfigProvider>(); final cfg = context.watch<ConfigProvider>();
final marginLeft = final marginLeft = cfg.drawerIsCollapsed ? 0.0 : cfg.drawerIsExpanded ? 304.0 : 80.0;
cfg.drawerIsCollapsed
? 0.0
: cfg.drawerIsExpanded
? 304.0
: 80.0;
return ListenableBuilder( return ListenableBuilder(
listenable: ws, listenable: ws,
@ -37,39 +32,37 @@ class ConnectionIndicator extends StatelessWidget {
elevation: 2, elevation: 2,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
color: Theme.of(context).colorScheme.secondaryContainer, color: Theme.of(context).colorScheme.secondaryContainer,
child: child: ua.isAuthorized
ua.isAuthorized ? Row(
? Row( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, children: [
children: [ if (ws.isBusy)
if (ws.isBusy) Text('serverConnecting').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
Text( else if (!ws.isConnected)
'serverConnecting', Text('serverDisconnected')
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer) .tr()
else if (!ws.isConnected) .textColor(Theme.of(context).colorScheme.onSecondaryContainer)
Text( else
'serverDisconnected', Text('serverConnected').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer),
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer) const Gap(8),
else if (ws.isBusy)
Text( const CircularProgressIndicator(strokeWidth: 2.5)
'serverConnected', .width(12)
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer), .height(12)
const Gap(8), .padding(horizontal: 4, right: 4)
if (ws.isBusy) else if (!ws.isConnected)
const CircularProgressIndicator( const Icon(Symbols.power_off, size: 18)
strokeWidth: 2.5, else
padding: EdgeInsets.zero, const Icon(Symbols.power, size: 18),
).width(12).height(12).padding(horizontal: 4, right: 4) ],
else if (!ws.isConnected) ).padding(horizontal: 8, vertical: 4)
const Icon(Symbols.power_off, size: 18) : const SizedBox.shrink(),
else ).opacity(show ? 1 : 0, animate: true).animate(
const Icon(Symbols.power, size: 18), const Duration(milliseconds: 300),
], Curves.easeInOut,
).padding(horizontal: 8, vertical: 4) ),
: const SizedBox.shrink(),
).opacity(show ? 1 : 0, animate: true).animate(const Duration(milliseconds: 300), Curves.easeInOut),
onTap: () { onTap: () {
if (!ws.isConnected && !ws.isBusy) { if (!ws.isConnected && !ws.isBusy) {
ws.connect(); ws.connect();

View File

@ -67,7 +67,6 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
return Drawer( return Drawer(
elevation: widget.elevation, elevation: widget.elevation,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(0))),
child: Column( child: Column(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@ -2238,10 +2238,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: tray_manager name: tray_manager
sha256: c2da0f0f1ddb455e721cf68d05d1281fec75cf5df0a1d3cb67b6ca0bdfd5709d sha256: "80be6c508159a6f3c57983de795209ac13453e9832fd574143b06dceee188ed2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.4.0" version: "0.3.2"
tuple: tuple:
dependency: transitive dependency: transitive
description: description:
@ -2525,10 +2525,11 @@ packages:
workmanager: workmanager:
dependency: "direct main" dependency: "direct main"
description: description:
name: workmanager path: workmanager
sha256: ed13530cccd28c5c9959ad42d657cd0666274ca74c56dea0ca183ddd527d3a00 ref: main
url: "https://pub.dev" resolved-ref: "4ce065135dc1b91fee918f81596b42a56850391d"
source: hosted url: "https://github.com/fluttercommunity/flutter_workmanager.git"
source: git
version: "0.5.2" version: "0.5.2"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive

View File

@ -103,7 +103,11 @@ dependencies:
flutter_svg: ^2.0.16 flutter_svg: ^2.0.16
home_widget: ^0.7.0 home_widget: ^0.7.0
receive_sharing_intent: ^1.8.1 receive_sharing_intent: ^1.8.1
workmanager: ^0.5.2 workmanager:
git:
url: https://github.com/fluttercommunity/flutter_workmanager.git
path: workmanager
ref: main
flutter_app_update: ^3.2.2 flutter_app_update: ^3.2.2
in_app_review: ^2.0.10 in_app_review: ^2.0.10
version: ^3.0.2 version: ^3.0.2
@ -116,7 +120,7 @@ dependencies:
flutter_inappwebview: ^6.1.5 flutter_inappwebview: ^6.1.5
html: ^0.15.5 html: ^0.15.5
xml: ^6.5.0 xml: ^6.5.0
tray_manager: ^0.4.0 tray_manager: ^0.3.2
hotkey_manager: ^0.2.3 hotkey_manager: ^0.2.3
image_picker_android: ^0.8.12+20 image_picker_android: ^0.8.12+20
cached_network_image_platform_interface: ^4.1.1 cached_network_image_platform_interface: ^4.1.1
@ -175,7 +179,6 @@ flutter:
- assets/icon/icon-light-radius.png - assets/icon/icon-light-radius.png
- assets/icon/tray-icon.ico - assets/icon/tray-icon.ico
- assets/icon/tray-icon.png - assets/icon/tray-icon.png
- assets/icon/kanban-1st.jpg
- assets/translations/ - assets/translations/
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see