Compare commits
3 Commits
2.4.2+78
...
e88dea8858
Author | SHA1 | Date | |
---|---|---|---|
e88dea8858 | |||
813679b161 | |||
9d4ce6ca8c |
@ -15,12 +15,10 @@ body:json {
|
|||||||
"client_id": "{{third_client_id}}",
|
"client_id": "{{third_client_id}}",
|
||||||
"client_secret":"{{third_client_tk}}",
|
"client_secret":"{{third_client_tk}}",
|
||||||
"type": "general",
|
"type": "general",
|
||||||
"subject": "新年快乐!",
|
"subject": "关于迁移服务器完成的提示",
|
||||||
"subtitle": "一条来自 Solar Network 团队的信息",
|
"subtitle": "一条来自 Solar Network 团队的运营信息",
|
||||||
"content": "今天是农历正月初一,小羊祝您新年快乐 🎉",
|
"content": "我们已经将所有用户数据迁移到新版服务器,刚刚发布新的 DNS,因为部分 DNS 缓存的影响。可能更改不会生效,可以使用 nslookup / ping 检查解析地址是否未 8. 开头,您可以主动刷新 DNS。谢谢!",
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"image": "D2EDbcrsTugs3xk5"
|
|
||||||
},
|
|
||||||
"priority": 10
|
"priority": 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ import 'package:surface/providers/widget.dart';
|
|||||||
import 'package:surface/router.dart';
|
import 'package:surface/router.dart';
|
||||||
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
|
import 'package:surface/widgets/menu_bar.dart';
|
||||||
import 'package:tray_manager/tray_manager.dart';
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
import 'package:version/version.dart';
|
import 'package:version/version.dart';
|
||||||
import 'package:workmanager/workmanager.dart';
|
import 'package:workmanager/workmanager.dart';
|
||||||
@ -331,18 +332,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
|
|
||||||
Future<void> _hotkeyInitialization() async {
|
Future<void> _hotkeyInitialization() async {
|
||||||
if (kIsWeb) return;
|
if (kIsWeb) return;
|
||||||
|
// The quit key has been removed, and the logic of the quit key is moved to system menu bar activator.
|
||||||
if (Platform.isMacOS) {
|
|
||||||
HotKey quitHotKey = HotKey(
|
|
||||||
key: PhysicalKeyboardKey.keyQ,
|
|
||||||
modifiers: [HotKeyModifier.meta],
|
|
||||||
scope: HotKeyScope.inapp,
|
|
||||||
);
|
|
||||||
await hotKeyManager.register(quitHotKey, keyUpHandler: (_) {
|
|
||||||
_appLifecycleListener?.dispose();
|
|
||||||
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final Menu _appTrayMenu = Menu(
|
final Menu _appTrayMenu = Menu(
|
||||||
@ -426,6 +416,15 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
return AppExitResponse.cancel;
|
return AppExitResponse.cancel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _quitApp() {
|
||||||
|
_appLifecycleListener?.dispose();
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
appWindow.close();
|
||||||
|
} else {
|
||||||
|
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onTrayIconMouseDown() {
|
void onTrayIconMouseDown() {
|
||||||
if (Platform.isWindows) {
|
if (Platform.isWindows) {
|
||||||
@ -460,12 +459,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
Timer(const Duration(milliseconds: 100), () => appWindow.show());
|
Timer(const Duration(milliseconds: 100), () => appWindow.show());
|
||||||
break;
|
break;
|
||||||
case 'exit':
|
case 'exit':
|
||||||
_appLifecycleListener?.dispose();
|
_quitApp();
|
||||||
if (Platform.isWindows) {
|
|
||||||
appWindow.close();
|
|
||||||
} else {
|
|
||||||
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,28 +476,31 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cfg = context.read<ConfigProvider>();
|
final cfg = context.read<ConfigProvider>();
|
||||||
return NotificationListener<SizeChangedLayoutNotification>(
|
return AppSystemMenuBar(
|
||||||
onNotification: (notification) {
|
onQuit: _quitApp,
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
child: NotificationListener<SizeChangedLayoutNotification>(
|
||||||
cfg.calcDrawerSize(context);
|
onNotification: (notification) {
|
||||||
});
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
child: OrientationBuilder(
|
|
||||||
builder: (context, orientation) {
|
|
||||||
final cfg = context.read<ConfigProvider>();
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
cfg.calcDrawerSize(context);
|
cfg.calcDrawerSize(context);
|
||||||
});
|
});
|
||||||
Future.delayed(const Duration(milliseconds: 300), () {
|
return false;
|
||||||
if (context.mounted) {
|
|
||||||
cfg.calcDrawerSize(context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return SizeChangedLayoutNotifier(
|
|
||||||
child: widget.child,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
child: OrientationBuilder(
|
||||||
|
builder: (context, orientation) {
|
||||||
|
final cfg = context.read<ConfigProvider>();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
cfg.calcDrawerSize(context);
|
||||||
|
});
|
||||||
|
Future.delayed(const Duration(milliseconds: 300), () {
|
||||||
|
if (context.mounted) {
|
||||||
|
cfg.calcDrawerSize(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return SizeChangedLayoutNotifier(
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class UserDirectoryProvider {
|
|||||||
|
|
||||||
Future<List<SnAccount?>> listAccount(Iterable<dynamic> id) async {
|
Future<List<SnAccount?>> listAccount(Iterable<dynamic> id) async {
|
||||||
// In-memory cache
|
// In-memory cache
|
||||||
if (_cacheExpiredAt != null && _cacheExpiredAt!.isAfter(DateTime.now())) {
|
if (_cacheExpiredAt != null && _cacheExpiredAt!.isBefore(DateTime.now())) {
|
||||||
_cache.clear();
|
_cache.clear();
|
||||||
_cacheExpiredAt = DateTime.now().add(const Duration(hours: 1));
|
_cacheExpiredAt = DateTime.now().add(const Duration(hours: 1));
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,10 +5,12 @@ import 'package:gap/gap.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/post.dart';
|
import 'package:surface/providers/post.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/sn_realm.dart';
|
import 'package:surface/providers/sn_realm.dart';
|
||||||
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/types/realm.dart';
|
import 'package:surface/types/realm.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
@ -75,6 +77,8 @@ class _ExploreScreenState extends State<ExploreScreen>
|
|||||||
|
|
||||||
Future<void> _fetchRealms() async {
|
Future<void> _fetchRealms() async {
|
||||||
try {
|
try {
|
||||||
|
final ua = context.read<UserProvider>();
|
||||||
|
if (!ua.isAuthorized) return;
|
||||||
final rels = context.read<SnRealmProvider>();
|
final rels = context.read<SnRealmProvider>();
|
||||||
final out = await rels.listAvailableRealms();
|
final out = await rels.listAvailableRealms();
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -219,10 +223,15 @@ class _ExploreScreenState extends State<ExploreScreen>
|
|||||||
SliverOverlapAbsorber(
|
SliverOverlapAbsorber(
|
||||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||||
sliver: SliverAppBar(
|
sliver: SliverAppBar(
|
||||||
leading: AutoAppBarLeading(),
|
leading:
|
||||||
|
ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
|
||||||
|
? AutoAppBarLeading()
|
||||||
|
: null,
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
|
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE))
|
||||||
|
const Gap(8),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Symbols.shuffle),
|
icon: const Icon(Symbols.shuffle),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -164,7 +164,7 @@ class _PostEditorScreenState extends State<PostEditorScreen>
|
|||||||
});
|
});
|
||||||
hotKeyManager.register(_saveDraftHotKey, keyDownHandler: (_) async {
|
hotKeyManager.register(_saveDraftHotKey, keyDownHandler: (_) async {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_writeController.sendPost(context);
|
_writeController.sendPost(context, saveAsDraft: true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
103
lib/widgets/menu_bar.dart
Normal file
103
lib/widgets/menu_bar.dart
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:surface/providers/navigation.dart';
|
||||||
|
import 'package:surface/router.dart';
|
||||||
|
|
||||||
|
// https://api.flutter.dev/flutter/widgets/PlatformMenuBar-class.html
|
||||||
|
// All the code following is only works on macOS
|
||||||
|
class AppSystemMenuBar extends StatelessWidget {
|
||||||
|
final Function? onQuit;
|
||||||
|
final Widget child;
|
||||||
|
const AppSystemMenuBar({super.key, this.onQuit, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (kIsWeb || !Platform.isMacOS) return child;
|
||||||
|
|
||||||
|
final nav = context.watch<NavigationProvider>();
|
||||||
|
|
||||||
|
return PlatformMenuBar(
|
||||||
|
menus: <PlatformMenuItem>[
|
||||||
|
PlatformMenu(
|
||||||
|
label: 'Solian',
|
||||||
|
menus: <PlatformMenuItem>[
|
||||||
|
PlatformMenuItemGroup(
|
||||||
|
members: <PlatformMenuItem>[
|
||||||
|
PlatformMenuItem(
|
||||||
|
label: 'screenAbout'.tr(),
|
||||||
|
onSelected: () {
|
||||||
|
appRouter.goNamed('about');
|
||||||
|
nav.autoDetectIndex(appRouter);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
PlatformMenuItemGroup(
|
||||||
|
members: [
|
||||||
|
PlatformMenuItem(
|
||||||
|
label: 'screenHome'.tr(),
|
||||||
|
shortcut: const SingleActivator(
|
||||||
|
LogicalKeyboardKey.digit1,
|
||||||
|
meta: true,
|
||||||
|
),
|
||||||
|
onSelected: () {
|
||||||
|
appRouter.goNamed('home');
|
||||||
|
nav.autoDetectIndex(appRouter);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PlatformMenuItem(
|
||||||
|
label: 'screenExplore'.tr(),
|
||||||
|
shortcut: const SingleActivator(
|
||||||
|
LogicalKeyboardKey.digit2,
|
||||||
|
meta: true,
|
||||||
|
),
|
||||||
|
onSelected: () {
|
||||||
|
appRouter.goNamed('explore');
|
||||||
|
nav.autoDetectIndex(appRouter);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PlatformMenuItem(
|
||||||
|
label: 'screenChat'.tr(),
|
||||||
|
shortcut: const SingleActivator(
|
||||||
|
LogicalKeyboardKey.digit3,
|
||||||
|
meta: true,
|
||||||
|
),
|
||||||
|
onSelected: () {
|
||||||
|
appRouter.goNamed('chat');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PlatformMenuItem(
|
||||||
|
label: 'screenAccount'.tr(),
|
||||||
|
shortcut: const SingleActivator(
|
||||||
|
LogicalKeyboardKey.digit4,
|
||||||
|
meta: true,
|
||||||
|
),
|
||||||
|
onSelected: () {
|
||||||
|
appRouter.goNamed('account');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (onQuit != null)
|
||||||
|
PlatformMenuItem(
|
||||||
|
shortcut: const SingleActivator(
|
||||||
|
LogicalKeyboardKey.keyQ,
|
||||||
|
meta: true,
|
||||||
|
),
|
||||||
|
label: 'trayMenuExit'.tr(),
|
||||||
|
onSelected: () {
|
||||||
|
onQuit?.call();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 2.4.2+78
|
version: 2.4.2+79
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
|
Reference in New Issue
Block a user