🐛 Fix some issues on Android and Web
This commit is contained in:
parent
21a1d4a2ad
commit
8dd6435a30
BIN
assets/icon/kanban-1st.jpg
Executable file
BIN
assets/icon/kanban-1st.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 509 KiB |
183
lib/main.dart
183
lib/main.dart
@ -89,19 +89,14 @@ void main() async {
|
|||||||
await EasyLocalization.ensureInitialized();
|
await EasyLocalization.ensureInitialized();
|
||||||
|
|
||||||
if (!kIsWeb && !Platform.isLinux) {
|
if (!kIsWeb && !Platform.isLinux) {
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||||
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(
|
Workmanager().initialize(appBackgroundDispatcher, isInDebugMode: kDebugMode);
|
||||||
appBackgroundDispatcher,
|
|
||||||
isInDebugMode: kDebugMode,
|
|
||||||
);
|
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
Workmanager().registerPeriodicTask(
|
Workmanager().registerPeriodicTask(
|
||||||
"widget-update-random-post",
|
"widget-update-random-post",
|
||||||
@ -114,8 +109,7 @@ void main() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!kIsWeb && Platform.isAndroid) {
|
if (!kIsWeb && Platform.isAndroid) {
|
||||||
final ImagePickerPlatform imagePickerImplementation =
|
final ImagePickerPlatform imagePickerImplementation = ImagePickerPlatform.instance;
|
||||||
ImagePickerPlatform.instance;
|
|
||||||
if (imagePickerImplementation is ImagePickerAndroid) {
|
if (imagePickerImplementation is ImagePickerAndroid) {
|
||||||
imagePickerImplementation.useAndroidPhotoPicker = true;
|
imagePickerImplementation.useAndroidPhotoPicker = true;
|
||||||
}
|
}
|
||||||
@ -132,12 +126,7 @@ class SolianApp extends StatelessWidget {
|
|||||||
return ResponsiveBreakpoints.builder(
|
return ResponsiveBreakpoints.builder(
|
||||||
child: EasyLocalization(
|
child: EasyLocalization(
|
||||||
path: 'assets/translations',
|
path: 'assets/translations',
|
||||||
supportedLocales: [
|
supportedLocales: [Locale('en', 'US'), Locale('zh', 'CN'), Locale('zh', 'TW'), Locale('zh', 'HK')],
|
||||||
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(),
|
||||||
@ -212,10 +201,7 @@ class _AppDelegate extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
routerConfig: appRouter,
|
routerConfig: appRouter,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return _AppSplashScreen(
|
return _AppSplashScreen(key: const Key('global-splash-screen'), child: child!);
|
||||||
key: const Key('global-splash-screen'),
|
|
||||||
child: child!,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -239,8 +225,7 @@ 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 &&
|
if (time != null && time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) {
|
||||||
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()) {
|
||||||
@ -261,30 +246,17 @@ 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(
|
BaseOptions(sendTimeout: const Duration(seconds: 60), receiveTimeout: const Duration(seconds: 60)),
|
||||||
sendTimeout: const Duration(seconds: 60),
|
).get('https://api.github.com/repos/Solsynth/HyperNet.Surface/releases/latest');
|
||||||
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 =
|
final remoteBuildNumber = int.tryParse(remoteVersionString.split('+').last) ?? 0;
|
||||||
int.tryParse(remoteVersionString.split('+').last) ?? 0;
|
final localBuildNumber = int.tryParse(localVersionString.split('+').last) ?? 0;
|
||||||
final localBuildNumber =
|
logging.info("[Update] Local: $localVersionString, Remote: $remoteVersionString");
|
||||||
int.tryParse(localVersionString.split('+').last) ?? 0;
|
if ((remoteVersion > localVersion || remoteBuildNumber > localBuildNumber) && mounted) {
|
||||||
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(
|
config.setUpdate(remoteVersionString, resp.data?['body'] ?? 'No changelog');
|
||||||
remoteVersionString,
|
|
||||||
resp.data?['body'] ?? 'No changelog',
|
|
||||||
);
|
|
||||||
logging.info("[Update] Update available: $remoteVersionString");
|
logging.info("[Update] Update available: $remoteVersionString");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -322,19 +294,21 @@ 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>();
|
||||||
@ -370,35 +344,19 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
|
|
||||||
final Menu _appTrayMenu = Menu(
|
final Menu _appTrayMenu = Menu(
|
||||||
items: [
|
items: [
|
||||||
MenuItem(
|
MenuItem(key: 'version_label', label: 'Solian', disabled: true),
|
||||||
key: 'version_label',
|
|
||||||
label: 'Solian',
|
|
||||||
disabled: true,
|
|
||||||
),
|
|
||||||
MenuItem.separator(),
|
MenuItem.separator(),
|
||||||
MenuItem.checkbox(
|
MenuItem.checkbox(checked: false, key: 'mute_notification', label: 'trayMenuMuteNotification'.tr()),
|
||||||
checked: false,
|
|
||||||
key: 'mute_notification',
|
|
||||||
label: 'trayMenuMuteNotification'.tr(),
|
|
||||||
),
|
|
||||||
MenuItem.separator(),
|
MenuItem.separator(),
|
||||||
MenuItem(
|
MenuItem(key: 'window_show', label: 'trayMenuShow'.tr()),
|
||||||
key: 'window_show',
|
MenuItem(key: 'exit', label: 'trayMenuExit'.tr()),
|
||||||
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
|
final icon = Platform.isWindows ? 'assets/icon/tray-icon.ico' : 'assets/icon/tray-icon.png';
|
||||||
? '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);
|
||||||
@ -416,10 +374,7 @@ 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(
|
await localNotifier.setup(appName: 'Solian', shortcutPolicy: ShortcutPolicy.requireCreate);
|
||||||
appName: 'Solian',
|
|
||||||
shortcutPolicy: ShortcutPolicy.requireCreate,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AppLifecycleListener? _appLifecycleListener;
|
AppLifecycleListener? _appLifecycleListener;
|
||||||
@ -430,9 +385,7 @@ 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(
|
_appLifecycleListener = AppLifecycleListener(onExitRequested: _onExitRequested);
|
||||||
onExitRequested: _onExitRequested,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_trayInitialization();
|
_trayInitialization();
|
||||||
@ -532,43 +485,49 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return SizeChangedLayoutNotifier(
|
return SizeChangedLayoutNotifier(
|
||||||
child: _isBusy
|
child:
|
||||||
? Material(
|
_isBusy
|
||||||
key: Key('app-splash-screen-$_isBusy'),
|
? Material(
|
||||||
child: Stack(
|
key: Key('app-splash-screen-$_isBusy'),
|
||||||
children: [
|
child: Stack(
|
||||||
Center(
|
children: [
|
||||||
child: Container(
|
Container(
|
||||||
constraints: const BoxConstraints(
|
decoration: BoxDecoration(
|
||||||
maxWidth: 240,
|
image: DecorationImage(
|
||||||
),
|
image: AssetImage('assets/icon/kanban-1st.jpg'),
|
||||||
child: Column(
|
fit: BoxFit.cover,
|
||||||
mainAxisSize: MainAxisSize.min,
|
opacity: 0.1,
|
||||||
children: [
|
),
|
||||||
Image.asset(
|
color: Theme.of(context).colorScheme.surface,
|
||||||
'assets/icon/icon.png',
|
backgroundBlendMode: BlendMode.darken,
|
||||||
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(
|
||||||
: widget.child,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
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,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -48,13 +48,11 @@ class NotificationProvider extends ChangeNotifier {
|
|||||||
var deviceUuid = await FlutterUdid.consistentUdid;
|
var deviceUuid = await FlutterUdid.consistentUdid;
|
||||||
|
|
||||||
if (deviceUuid.isEmpty) {
|
if (deviceUuid.isEmpty) {
|
||||||
logging.warning(
|
logging.warning('[Push Notification] Unable to active push notifications, couldn\'t get device uuid');
|
||||||
'[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
|
logging.info('[Push Notification] Registering device push notifications...');
|
||||||
.info('[Push Notification] Registering device push notifications...');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Platform.isIOS || Platform.isMacOS) {
|
if (Platform.isIOS || Platform.isMacOS) {
|
||||||
@ -66,14 +64,14 @@ class NotificationProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
logging.info('[Push Notification] Device Push Token is $token');
|
logging.info('[Push Notification] Device Push Token is $token');
|
||||||
|
|
||||||
await _sn.client.post(
|
try {
|
||||||
'/cgi/id/notifications/subscription',
|
await _sn.client.post(
|
||||||
data: {
|
'/cgi/id/notifications/subscription',
|
||||||
'provider': provider,
|
data: {'provider': provider, 'device_token': token, 'device_id': deviceUuid},
|
||||||
'device_token': token,
|
);
|
||||||
'device_id': deviceUuid,
|
} catch (err) {
|
||||||
},
|
logging.error('[Push Notification] Unable to register push notifications: $err');
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int showingCount = 0;
|
int showingCount = 0;
|
||||||
@ -91,8 +89,7 @@ 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' &&
|
if (notification.topic == 'messaging.message' && skippableNotifyChannel != null) {
|
||||||
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;
|
||||||
|
@ -306,9 +306,7 @@ 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>();
|
||||||
context.showSnackbar('loginSuccess'.tr(args: [
|
ua.refreshUser();
|
||||||
'@${ua.user?.name} (${ua.user?.nick})',
|
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -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) => TurnstileScreen(),
|
builder: (context) => CaptchaScreen(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (captchaTk == null) return;
|
if (captchaTk == null) return;
|
||||||
|
@ -1,23 +1,67 @@
|
|||||||
|
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 TurnstileScreen extends StatefulWidget {
|
class CaptchaScreen extends StatefulWidget {
|
||||||
const TurnstileScreen({
|
const CaptchaScreen({
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TurnstileScreen> createState() => _TurnstileScreenState();
|
State<CaptchaScreen> createState() => _CaptchaScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TurnstileScreenState extends State<TurnstileScreen> {
|
class _CaptchaScreenState extends State<CaptchaScreen> {
|
||||||
|
@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(
|
||||||
|
@ -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) => TurnstileScreen(),
|
builder: (context) => CaptchaScreen(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (captchaTk == null) return;
|
if (captchaTk == null) return;
|
||||||
|
@ -16,7 +16,12 @@ 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 = cfg.drawerIsCollapsed ? 0.0 : cfg.drawerIsExpanded ? 304.0 : 80.0;
|
final marginLeft =
|
||||||
|
cfg.drawerIsCollapsed
|
||||||
|
? 0.0
|
||||||
|
: cfg.drawerIsExpanded
|
||||||
|
? 304.0
|
||||||
|
: 80.0;
|
||||||
|
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: ws,
|
listenable: ws,
|
||||||
@ -32,37 +37,39 @@ 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: ua.isAuthorized
|
child:
|
||||||
? Row(
|
ua.isAuthorized
|
||||||
mainAxisSize: MainAxisSize.min,
|
? Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
if (ws.isBusy)
|
children: [
|
||||||
Text('serverConnecting').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
if (ws.isBusy)
|
||||||
else if (!ws.isConnected)
|
Text(
|
||||||
Text('serverDisconnected')
|
'serverConnecting',
|
||||||
.tr()
|
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
||||||
.textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
else if (!ws.isConnected)
|
||||||
else
|
Text(
|
||||||
Text('serverConnected').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer),
|
'serverDisconnected',
|
||||||
const Gap(8),
|
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
||||||
if (ws.isBusy)
|
else
|
||||||
const CircularProgressIndicator(strokeWidth: 2.5)
|
Text(
|
||||||
.width(12)
|
'serverConnected',
|
||||||
.height(12)
|
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer),
|
||||||
.padding(horizontal: 4, right: 4)
|
const Gap(8),
|
||||||
else if (!ws.isConnected)
|
if (ws.isBusy)
|
||||||
const Icon(Symbols.power_off, size: 18)
|
const CircularProgressIndicator(
|
||||||
else
|
strokeWidth: 2.5,
|
||||||
const Icon(Symbols.power, size: 18),
|
padding: EdgeInsets.zero,
|
||||||
],
|
).width(12).height(12).padding(horizontal: 4, right: 4)
|
||||||
).padding(horizontal: 8, vertical: 4)
|
else if (!ws.isConnected)
|
||||||
: const SizedBox.shrink(),
|
const Icon(Symbols.power_off, size: 18)
|
||||||
).opacity(show ? 1 : 0, animate: true).animate(
|
else
|
||||||
const Duration(milliseconds: 300),
|
const Icon(Symbols.power, size: 18),
|
||||||
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();
|
||||||
|
@ -67,6 +67,7 @@ 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,
|
||||||
|
@ -179,6 +179,7 @@ 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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user