Compare commits
	
		
			5 Commits
		
	
	
		
			2.2.2+55
			...
			cb4a2598c8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cb4a2598c8 | |||
| 950612dc07 | |||
| cbd1eaf1af | |||
| ac41cbd99f | |||
| 9f9c90abc4 | 
| @@ -196,6 +196,10 @@ | |||||||
|   "settingsFeatures": "Features", |   "settingsFeatures": "Features", | ||||||
|   "settingsNotifyWithHaptic": "Haptic when Notified", |   "settingsNotifyWithHaptic": "Haptic when Notified", | ||||||
|   "settingsNotifyWithHapticDescription": "Vibrate lightly when a new notification appears in the foreground.", |   "settingsNotifyWithHapticDescription": "Vibrate lightly when a new notification appears in the foreground.", | ||||||
|  |   "settingsExpandPostLink": "Expand Post Link", | ||||||
|  |   "settingsExpandPostLinkDescription": "Expand the post link in the post list.", | ||||||
|  |   "settingsExpandChatLink": "Expand Chat Link", | ||||||
|  |   "settingsExpandChatLinkDescription": "Expand the chat link in the chat list.", | ||||||
|   "settingsNetwork": "Network", |   "settingsNetwork": "Network", | ||||||
|   "settingsNetworkServer": "HyperNet Server", |   "settingsNetworkServer": "HyperNet Server", | ||||||
|   "settingsNetworkServerDescription": "Set the HyperNet server address, choose ours or build your own.", |   "settingsNetworkServerDescription": "Set the HyperNet server address, choose ours or build your own.", | ||||||
|   | |||||||
| @@ -194,6 +194,10 @@ | |||||||
|   "settingsFeatures": "功能", |   "settingsFeatures": "功能", | ||||||
|   "settingsNotifyWithHaptic": "新通知时振动", |   "settingsNotifyWithHaptic": "新通知时振动", | ||||||
|   "settingsNotifyWithHapticDescription": "在应用在前台时收到新通知出现时出发轻量的振动。", |   "settingsNotifyWithHapticDescription": "在应用在前台时收到新通知出现时出发轻量的振动。", | ||||||
|  |   "settingsExpandPostLink": "展开帖子链接", | ||||||
|  |   "settingsExpandPostLinkDescription": "在帖子列表中展开显示帖子中的链接。", | ||||||
|  |   "settingsExpandChatLink": "展开聊天链接", | ||||||
|  |   "settingsExpandChatLinkDescription": "在聊天信息中展开显示内容中的链接。", | ||||||
|   "settingsNetwork": "网络", |   "settingsNetwork": "网络", | ||||||
|   "settingsNetworkServer": "HyperNet 服务器", |   "settingsNetworkServer": "HyperNet 服务器", | ||||||
|   "settingsNetworkServerDescription": "设置 HyperNet 服务器地址,选择我们提供的,或者自己搭建。", |   "settingsNetworkServerDescription": "设置 HyperNet 服务器地址,选择我们提供的,或者自己搭建。", | ||||||
|   | |||||||
| @@ -260,7 +260,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> { | |||||||
|     try { |     try { | ||||||
|       final cfg = context.read<ConfigProvider>(); |       final cfg = context.read<ConfigProvider>(); | ||||||
|       WidgetsBinding.instance.addPostFrameCallback((_) { |       WidgetsBinding.instance.addPostFrameCallback((_) { | ||||||
|         cfg.calcDrawerSize(context); |         cfg.calcDrawerSize(context, withMediaQuery: true); | ||||||
|       }); |       }); | ||||||
|       final home = context.read<HomeWidgetProvider>(); |       final home = context.read<HomeWidgetProvider>(); | ||||||
|       await home.initialize(); |       await home.initialize(); | ||||||
|   | |||||||
| @@ -15,6 +15,8 @@ const kAppBackgroundStoreKey = 'app_has_background'; | |||||||
| const kAppColorSchemeStoreKey = 'app_color_scheme'; | const kAppColorSchemeStoreKey = 'app_color_scheme'; | ||||||
| const kAppDrawerPreferCollapse = 'app_drawer_prefer_collapse'; | const kAppDrawerPreferCollapse = 'app_drawer_prefer_collapse'; | ||||||
| const kAppNotifyWithHaptic = 'app_notify_with_haptic'; | const kAppNotifyWithHaptic = 'app_notify_with_haptic'; | ||||||
|  | const kAppExpandPostLink = 'app_expand_post_link'; | ||||||
|  | const kAppExpandChatLink = 'app_expand_chat_link'; | ||||||
|  |  | ||||||
| const Map<String, FilterQuality> kImageQualityLevel = { | const Map<String, FilterQuality> kImageQualityLevel = { | ||||||
|   'settingsImageQualityLowest': FilterQuality.none, |   'settingsImageQualityLowest': FilterQuality.none, | ||||||
| @@ -39,14 +41,22 @@ class ConfigProvider extends ChangeNotifier { | |||||||
|   bool drawerIsCollapsed = false; |   bool drawerIsCollapsed = false; | ||||||
|   bool drawerIsExpanded = false; |   bool drawerIsExpanded = false; | ||||||
|  |  | ||||||
|   void calcDrawerSize(BuildContext context) { |   void calcDrawerSize(BuildContext context, {bool withMediaQuery = false}) { | ||||||
|     final rpb = ResponsiveBreakpoints.of(context); |     bool newDrawerIsCollapsed = false; | ||||||
|     final newDrawerIsCollapsed = rpb.smallerOrEqualTo(MOBILE); |     bool newDrawerIsExpanded = false; | ||||||
|     final newDrawerIsExpanded = rpb.largerThan(TABLET) |     if (withMediaQuery) { | ||||||
|         ? (prefs.getBool(kAppDrawerPreferCollapse) ?? false) |       newDrawerIsCollapsed = MediaQuery.of(context).size.width < 450; | ||||||
|             ? false |       newDrawerIsExpanded = MediaQuery.of(context).size.width >= 451; | ||||||
|             : true |     } else { | ||||||
|         : false; |       final rpb = ResponsiveBreakpoints.of(context); | ||||||
|  |       newDrawerIsCollapsed = rpb.smallerOrEqualTo(MOBILE); | ||||||
|  |       newDrawerIsCollapsed = rpb.largerThan(TABLET) | ||||||
|  |           ? (prefs.getBool(kAppDrawerPreferCollapse) ?? false) | ||||||
|  |               ? false | ||||||
|  |               : true | ||||||
|  |           : false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (newDrawerIsExpanded != drawerIsExpanded || newDrawerIsCollapsed != drawerIsCollapsed) { |     if (newDrawerIsExpanded != drawerIsExpanded || newDrawerIsCollapsed != drawerIsCollapsed) { | ||||||
|       drawerIsExpanded = newDrawerIsExpanded; |       drawerIsExpanded = newDrawerIsExpanded; | ||||||
|       drawerIsCollapsed = newDrawerIsCollapsed; |       drawerIsCollapsed = newDrawerIsCollapsed; | ||||||
|   | |||||||
							
								
								
									
										428
									
								
								lib/router.dart
									
									
									
									
									
								
							
							
						
						
									
										428
									
								
								lib/router.dart
									
									
									
									
									
								
							| @@ -34,246 +34,222 @@ import 'package:surface/widgets/about.dart'; | |||||||
| import 'package:surface/widgets/navigation/app_background.dart'; | import 'package:surface/widgets/navigation/app_background.dart'; | ||||||
| import 'package:surface/widgets/navigation/app_scaffold.dart'; | import 'package:surface/widgets/navigation/app_scaffold.dart'; | ||||||
|  |  | ||||||
|  | Widget _fadeThroughTransition( | ||||||
|  |     BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { | ||||||
|  |   return FadeThroughTransition( | ||||||
|  |     animation: animation, | ||||||
|  |     secondaryAnimation: secondaryAnimation, | ||||||
|  |     fillColor: Colors.transparent, | ||||||
|  |     child: child, | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
| final _appRoutes = [ | final _appRoutes = [ | ||||||
|   ShellRoute( |   GoRoute( | ||||||
|     builder: (context, state, child) => child, |     path: '/', | ||||||
|  |     name: 'home', | ||||||
|  |     pageBuilder: (context, state) => CustomTransitionPage( | ||||||
|  |       transitionsBuilder: _fadeThroughTransition, | ||||||
|  |       child: const HomeScreen(), | ||||||
|  |     ), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/posts', | ||||||
|  |     name: 'explore', | ||||||
|  |     pageBuilder: (context, state) => CustomTransitionPage( | ||||||
|  |       transitionsBuilder: _fadeThroughTransition, | ||||||
|  |       child: const ExploreScreen(), | ||||||
|  |     ), | ||||||
|     routes: [ |     routes: [ | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/', |         path: '/write/:mode', | ||||||
|         name: 'home', |         name: 'postEditor', | ||||||
|         pageBuilder: (context, state) => NoTransitionPage( |         builder: (context, state) => PostEditorScreen( | ||||||
|           child: const HomeScreen(), |           mode: state.pathParameters['mode']!, | ||||||
|  |           postEditId: int.tryParse( | ||||||
|  |             state.uri.queryParameters['editing'] ?? '', | ||||||
|  |           ), | ||||||
|  |           postReplyId: int.tryParse( | ||||||
|  |             state.uri.queryParameters['replying'] ?? '', | ||||||
|  |           ), | ||||||
|  |           postRepostId: int.tryParse( | ||||||
|  |             state.uri.queryParameters['reposting'] ?? '', | ||||||
|  |           ), | ||||||
|  |           extraProps: state.extra as PostEditorExtraProps?, | ||||||
|         ), |         ), | ||||||
|       ), |       ), | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/posts', |         path: '/search', | ||||||
|         name: 'explore', |         name: 'postSearch', | ||||||
|         pageBuilder: (context, state) => NoTransitionPage( |         builder: (context, state) => PostSearchScreen( | ||||||
|           child: const ExploreScreen(), |           initialTags: state.uri.queryParameters['tags']?.split(','), | ||||||
|         ), |           initialCategories: state.uri.queryParameters['categories']?.split(','), | ||||||
|         routes: [ |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/write/:mode', |  | ||||||
|             name: 'postEditor', |  | ||||||
|             builder: (context, state) => PostEditorScreen( |  | ||||||
|               mode: state.pathParameters['mode']!, |  | ||||||
|               postEditId: int.tryParse( |  | ||||||
|                 state.uri.queryParameters['editing'] ?? '', |  | ||||||
|               ), |  | ||||||
|               postReplyId: int.tryParse( |  | ||||||
|                 state.uri.queryParameters['replying'] ?? '', |  | ||||||
|               ), |  | ||||||
|               postRepostId: int.tryParse( |  | ||||||
|                 state.uri.queryParameters['reposting'] ?? '', |  | ||||||
|               ), |  | ||||||
|               extraProps: state.extra as PostEditorExtraProps?, |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/search', |  | ||||||
|             name: 'postSearch', |  | ||||||
|             builder: (context, state) => PostSearchScreen( |  | ||||||
|               initialTags: state.uri.queryParameters['tags']?.split(','), |  | ||||||
|               initialCategories: state.uri.queryParameters['categories']?.split(','), |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/publishers/:name', |  | ||||||
|             name: 'postPublisher', |  | ||||||
|             builder: (context, state) => PostPublisherScreen(name: state.pathParameters['name']!), |  | ||||||
|           ), |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/:slug', |  | ||||||
|             name: 'postDetail', |  | ||||||
|             builder: (context, state) => PostDetailScreen( |  | ||||||
|               slug: state.pathParameters['slug']!, |  | ||||||
|               preload: state.extra as SnPost?, |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|         ], |  | ||||||
|       ), |  | ||||||
|       GoRoute( |  | ||||||
|         path: '/account', |  | ||||||
|         name: 'account', |  | ||||||
|         pageBuilder: (context, state) => CustomTransitionPage( |  | ||||||
|           transitionsBuilder: (context, animation, secondaryAnimation, child) { |  | ||||||
|             return FadeThroughTransition( |  | ||||||
|               animation: animation, |  | ||||||
|               secondaryAnimation: secondaryAnimation, |  | ||||||
|               fillColor: Colors.transparent, |  | ||||||
|               child: child, |  | ||||||
|             ); |  | ||||||
|           }, |  | ||||||
|           child: const AccountScreen(), |  | ||||||
|         ), |  | ||||||
|         routes: [], |  | ||||||
|       ), |  | ||||||
|       GoRoute( |  | ||||||
|         path: '/chat', |  | ||||||
|         name: 'chat', |  | ||||||
|         pageBuilder: (context, state) => CustomTransitionPage( |  | ||||||
|           transitionsBuilder: (context, animation, secondaryAnimation, child) { |  | ||||||
|             return FadeThroughTransition( |  | ||||||
|               animation: animation, |  | ||||||
|               secondaryAnimation: secondaryAnimation, |  | ||||||
|               fillColor: Colors.transparent, |  | ||||||
|               child: child, |  | ||||||
|             ); |  | ||||||
|           }, |  | ||||||
|           child: const ChatScreen(), |  | ||||||
|         ), |  | ||||||
|         routes: [ |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/:scope/:alias', |  | ||||||
|             name: 'chatRoom', |  | ||||||
|             builder: (context, state) => AppBackground( |  | ||||||
|               child: ChatRoomScreen( |  | ||||||
|                 scope: state.pathParameters['scope']!, |  | ||||||
|                 alias: state.pathParameters['alias']!, |  | ||||||
|               ), |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/:scope/:alias/call', |  | ||||||
|             name: 'chatCallRoom', |  | ||||||
|             builder: (context, state) => AppBackground( |  | ||||||
|               child: CallRoomScreen( |  | ||||||
|                 scope: state.pathParameters['scope']!, |  | ||||||
|                 alias: state.pathParameters['alias']!, |  | ||||||
|               ), |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/:scope/:alias/detail', |  | ||||||
|             name: 'channelDetail', |  | ||||||
|             builder: (context, state) => AppBackground( |  | ||||||
|               child: ChannelDetailScreen( |  | ||||||
|                 scope: state.pathParameters['scope']!, |  | ||||||
|                 alias: state.pathParameters['alias']!, |  | ||||||
|               ), |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/manage', |  | ||||||
|             name: 'chatManage', |  | ||||||
|             pageBuilder: (context, state) => CustomTransitionPage( |  | ||||||
|               child: ChatManageScreen( |  | ||||||
|                 editingChannelAlias: state.uri.queryParameters['editing'], |  | ||||||
|               ), |  | ||||||
|               transitionsBuilder: (context, animation, secondaryAnimation, child) { |  | ||||||
|                 return FadeThroughTransition( |  | ||||||
|                   animation: animation, |  | ||||||
|                   secondaryAnimation: secondaryAnimation, |  | ||||||
|                   fillColor: Colors.transparent, |  | ||||||
|                   child: AppBackground( |  | ||||||
|                     child: child, |  | ||||||
|                   ), |  | ||||||
|                 ); |  | ||||||
|               }, |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/:alias', |  | ||||||
|             name: 'realmDetail', |  | ||||||
|             builder: (context, state) => AppBackground( |  | ||||||
|               child: RealmDetailScreen(alias: state.pathParameters['alias']!), |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|         ], |  | ||||||
|       ), |  | ||||||
|       GoRoute( |  | ||||||
|         path: '/realm', |  | ||||||
|         name: 'realm', |  | ||||||
|         pageBuilder: (context, state) => NoTransitionPage( |  | ||||||
|           child: const RealmScreen(), |  | ||||||
|         ), |  | ||||||
|         routes: [ |  | ||||||
|           GoRoute( |  | ||||||
|             path: '/manage', |  | ||||||
|             name: 'realmManage', |  | ||||||
|             pageBuilder: (context, state) => CustomTransitionPage( |  | ||||||
|               child: RealmManageScreen( |  | ||||||
|                 editingRealmAlias: state.uri.queryParameters['editing'], |  | ||||||
|               ), |  | ||||||
|               transitionsBuilder: (context, animation, secondaryAnimation, child) { |  | ||||||
|                 return FadeThroughTransition( |  | ||||||
|                   animation: animation, |  | ||||||
|                   secondaryAnimation: secondaryAnimation, |  | ||||||
|                   fillColor: Colors.transparent, |  | ||||||
|                   child: AppBackground( |  | ||||||
|                     child: child, |  | ||||||
|                   ), |  | ||||||
|                 ); |  | ||||||
|               }, |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|         ], |  | ||||||
|       ), |  | ||||||
|       GoRoute( |  | ||||||
|         path: '/album', |  | ||||||
|         name: 'album', |  | ||||||
|         pageBuilder: (context, state) => NoTransitionPage( |  | ||||||
|           child: const AlbumScreen(), |  | ||||||
|         ), |         ), | ||||||
|       ), |       ), | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/friend', |         path: '/publishers/:name', | ||||||
|         name: 'friend', |         name: 'postPublisher', | ||||||
|         pageBuilder: (context, state) => NoTransitionPage( |         builder: (context, state) => PostPublisherScreen(name: state.pathParameters['name']!), | ||||||
|           child: const FriendScreen(), |  | ||||||
|         ), |  | ||||||
|       ), |       ), | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/notification', |         path: '/:slug', | ||||||
|         name: 'notification', |         name: 'postDetail', | ||||||
|         pageBuilder: (context, state) => NoTransitionPage( |         builder: (context, state) => PostDetailScreen( | ||||||
|           child: const NotificationScreen(), |           slug: state.pathParameters['slug']!, | ||||||
|  |           preload: state.extra as SnPost?, | ||||||
|         ), |         ), | ||||||
|       ), |       ), | ||||||
|     ], |     ], | ||||||
|   ), |   ), | ||||||
|   ShellRoute( |   GoRoute( | ||||||
|     builder: (context, state, child) => child, |     path: '/account', | ||||||
|  |     name: 'account', | ||||||
|  |     pageBuilder: (context, state) => CustomTransitionPage( | ||||||
|  |       transitionsBuilder: _fadeThroughTransition, | ||||||
|  |       child: const AccountScreen(), | ||||||
|  |     ), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/chat', | ||||||
|  |     name: 'chat', | ||||||
|  |     pageBuilder: (context, state) => CustomTransitionPage( | ||||||
|  |       transitionsBuilder: _fadeThroughTransition, | ||||||
|  |       child: const ChatScreen(), | ||||||
|  |     ), | ||||||
|     routes: [ |     routes: [ | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/auth/login', |         path: '/:scope/:alias', | ||||||
|         name: 'authLogin', |         name: 'chatRoom', | ||||||
|         builder: (context, state) => LoginScreen(), |         builder: (context, state) => AppBackground( | ||||||
|  |           child: ChatRoomScreen( | ||||||
|  |             scope: state.pathParameters['scope']!, | ||||||
|  |             alias: state.pathParameters['alias']!, | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|       ), |       ), | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/auth/register', |         path: '/:scope/:alias/call', | ||||||
|         name: 'authRegister', |         name: 'chatCallRoom', | ||||||
|         builder: (context, state) => RegisterScreen(), |         builder: (context, state) => AppBackground( | ||||||
|  |           child: CallRoomScreen( | ||||||
|  |             scope: state.pathParameters['scope']!, | ||||||
|  |             alias: state.pathParameters['alias']!, | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|       ), |       ), | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/reports', |         path: '/:scope/:alias/detail', | ||||||
|         name: 'abuseReport', |         name: 'channelDetail', | ||||||
|         builder: (context, state) => AbuseReportScreen(), |         builder: (context, state) => AppBackground( | ||||||
|  |           child: ChannelDetailScreen( | ||||||
|  |             scope: state.pathParameters['scope']!, | ||||||
|  |             alias: state.pathParameters['alias']!, | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|       ), |       ), | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/account/profile/edit', |         path: '/manage', | ||||||
|         name: 'accountProfileEdit', |         name: 'chatManage', | ||||||
|         builder: (context, state) => ProfileEditScreen(), |         pageBuilder: (context, state) => CustomTransitionPage( | ||||||
|       ), |           child: ChatManageScreen( | ||||||
|       GoRoute( |             editingChannelAlias: state.uri.queryParameters['editing'], | ||||||
|         path: '/account/publishers', |           ), | ||||||
|         name: 'accountPublishers', |           transitionsBuilder: (context, animation, secondaryAnimation, child) { | ||||||
|         builder: (context, state) => PublisherScreen(), |             return FadeThroughTransition( | ||||||
|       ), |               animation: animation, | ||||||
|       GoRoute( |               secondaryAnimation: secondaryAnimation, | ||||||
|         path: '/account/publishers/new', |               fillColor: Colors.transparent, | ||||||
|         name: 'accountPublisherNew', |               child: child, | ||||||
|         builder: (context, state) => AccountPublisherNewScreen(), |             ); | ||||||
|       ), |           }, | ||||||
|       GoRoute( |  | ||||||
|         path: '/account/publishers/edit/:name', |  | ||||||
|         name: 'accountPublisherEdit', |  | ||||||
|         builder: (context, state) => AccountPublisherEditScreen( |  | ||||||
|           name: state.pathParameters['name']!, |  | ||||||
|         ), |         ), | ||||||
|       ), |       ), | ||||||
|     ], |     ], | ||||||
|   ), |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/realm', | ||||||
|  |     name: 'realm', | ||||||
|  |     pageBuilder: (context, state) => CustomTransitionPage( | ||||||
|  |       transitionsBuilder: _fadeThroughTransition, | ||||||
|  |       child: const RealmScreen(), | ||||||
|  |     ), | ||||||
|  |     routes: [ | ||||||
|  |       GoRoute( | ||||||
|  |         path: '/:alias', | ||||||
|  |         name: 'realmDetail', | ||||||
|  |         builder: (context, state) => RealmDetailScreen(alias: state.pathParameters['alias']!), | ||||||
|  |       ), | ||||||
|  |       GoRoute( | ||||||
|  |         path: '/manage', | ||||||
|  |         name: 'realmManage', | ||||||
|  |         pageBuilder: (context, state) => CustomTransitionPage( | ||||||
|  |           transitionsBuilder: _fadeThroughTransition, | ||||||
|  |           child: RealmManageScreen( | ||||||
|  |             editingRealmAlias: state.uri.queryParameters['editing'], | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ], | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/album', | ||||||
|  |     name: 'album', | ||||||
|  |     pageBuilder: (context, state) => CustomTransitionPage( | ||||||
|  |       transitionsBuilder: _fadeThroughTransition, | ||||||
|  |       child: const AlbumScreen(), | ||||||
|  |     ), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/friend', | ||||||
|  |     name: 'friend', | ||||||
|  |     pageBuilder: (context, state) => NoTransitionPage( | ||||||
|  |       child: const FriendScreen(), | ||||||
|  |     ), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/notification', | ||||||
|  |     name: 'notification', | ||||||
|  |     pageBuilder: (context, state) => NoTransitionPage( | ||||||
|  |       child: const NotificationScreen(), | ||||||
|  |     ), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/auth/login', | ||||||
|  |     name: 'authLogin', | ||||||
|  |     builder: (context, state) => LoginScreen(), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/auth/register', | ||||||
|  |     name: 'authRegister', | ||||||
|  |     builder: (context, state) => RegisterScreen(), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/reports', | ||||||
|  |     name: 'abuseReport', | ||||||
|  |     builder: (context, state) => AbuseReportScreen(), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/account/profile/edit', | ||||||
|  |     name: 'accountProfileEdit', | ||||||
|  |     builder: (context, state) => ProfileEditScreen(), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/account/publishers', | ||||||
|  |     name: 'accountPublishers', | ||||||
|  |     builder: (context, state) => PublisherScreen(), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/account/publishers/new', | ||||||
|  |     name: 'accountPublisherNew', | ||||||
|  |     builder: (context, state) => AccountPublisherNewScreen(), | ||||||
|  |   ), | ||||||
|  |   GoRoute( | ||||||
|  |     path: '/account/publishers/edit/:name', | ||||||
|  |     name: 'accountPublisherEdit', | ||||||
|  |     builder: (context, state) => AccountPublisherEditScreen( | ||||||
|  |       name: state.pathParameters['name']!, | ||||||
|  |     ), | ||||||
|  |   ), | ||||||
|   GoRoute( |   GoRoute( | ||||||
|     path: '/account/:name', |     path: '/account/:name', | ||||||
|     name: 'accountProfilePage', |     name: 'accountProfilePage', | ||||||
| @@ -281,25 +257,15 @@ final _appRoutes = [ | |||||||
|       child: UserScreen(name: state.pathParameters['name']!), |       child: UserScreen(name: state.pathParameters['name']!), | ||||||
|     ), |     ), | ||||||
|   ), |   ), | ||||||
|   ShellRoute( |   GoRoute( | ||||||
|     builder: (context, state, child) => child, |     path: '/settings', | ||||||
|     routes: [ |     name: 'settings', | ||||||
|       GoRoute( |     builder: (context, state) => SettingsScreen(), | ||||||
|         path: '/settings', |  | ||||||
|         name: 'settings', |  | ||||||
|         builder: (context, state) => SettingsScreen(), |  | ||||||
|       ), |  | ||||||
|     ], |  | ||||||
|   ), |   ), | ||||||
|   ShellRoute( |   GoRoute( | ||||||
|     builder: (context, state, child) => child, |     path: '/about', | ||||||
|     routes: [ |     name: 'about', | ||||||
|       GoRoute( |     builder: (context, state) => AboutScreen(), | ||||||
|         path: '/about', |  | ||||||
|         name: 'about', |  | ||||||
|         builder: (context, state) => AboutScreen(), |  | ||||||
|       ), |  | ||||||
|     ], |  | ||||||
|   ), |   ), | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ import 'package:surface/types/check_in.dart'; | |||||||
| import 'package:surface/types/post.dart'; | import 'package:surface/types/post.dart'; | ||||||
| import 'package:surface/widgets/account/account_image.dart'; | import 'package:surface/widgets/account/account_image.dart'; | ||||||
| import 'package:surface/widgets/dialog.dart'; | import 'package:surface/widgets/dialog.dart'; | ||||||
| import 'package:surface/widgets/navigation/app_scaffold.dart'; |  | ||||||
| import 'package:surface/widgets/universal_image.dart'; | import 'package:surface/widgets/universal_image.dart'; | ||||||
|  |  | ||||||
| const Map<String, (String, IconData, Color)> kBadgesMeta = { | const Map<String, (String, IconData, Color)> kBadgesMeta = { | ||||||
| @@ -596,7 +595,7 @@ class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateM | |||||||
|                 subtitle: Text('@${ele.name}'), |                 subtitle: Text('@${ele.name}'), | ||||||
|                 trailing: const Icon(Symbols.chevron_right), |                 trailing: const Icon(Symbols.chevron_right), | ||||||
|                 onTap: () { |                 onTap: () { | ||||||
|                   GoRouter.of(context).pushNamed( |                   GoRouter.of(context).goNamed( | ||||||
|                     'postPublisher', |                     'postPublisher', | ||||||
|                     pathParameters: {'name': ele.name}, |                     pathParameters: {'name': ele.name}, | ||||||
|                   ); |                   ); | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ import 'package:surface/widgets/navigation/app_scaffold.dart'; | |||||||
| class CallRoomScreen extends StatefulWidget { | class CallRoomScreen extends StatefulWidget { | ||||||
|   final String scope; |   final String scope; | ||||||
|   final String alias; |   final String alias; | ||||||
|  |  | ||||||
|   const CallRoomScreen({super.key, required this.scope, required this.alias}); |   const CallRoomScreen({super.key, required this.scope, required this.alias}); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
| @@ -36,8 +37,7 @@ class _CallRoomScreenState extends State<CallRoomScreen> { | |||||||
|     return Stack( |     return Stack( | ||||||
|       children: [ |       children: [ | ||||||
|         Container( |         Container( | ||||||
|           color: |           color: Theme.of(context).colorScheme.surfaceContainer.withOpacity(0.75), | ||||||
|               Theme.of(context).colorScheme.surfaceContainer.withOpacity(0.75), |  | ||||||
|           child: call.focusTrack != null |           child: call.focusTrack != null | ||||||
|               ? InteractiveParticipantWidget( |               ? InteractiveParticipantWidget( | ||||||
|                   isFixedAvatar: false, |                   isFixedAvatar: false, | ||||||
| @@ -72,8 +72,7 @@ class _CallRoomScreenState extends State<CallRoomScreen> { | |||||||
|                       color: Theme.of(context).cardColor, |                       color: Theme.of(context).cardColor, | ||||||
|                       participant: track, |                       participant: track, | ||||||
|                       onTap: () { |                       onTap: () { | ||||||
|                         if (track.participant.sid != |                         if (track.participant.sid != call.focusTrack?.participant.sid) { | ||||||
|                             call.focusTrack?.participant.sid) { |  | ||||||
|                           call.setFocusTrack(track); |                           call.setFocusTrack(track); | ||||||
|                         } |                         } | ||||||
|                       }, |                       }, | ||||||
| @@ -115,14 +114,10 @@ class _CallRoomScreenState extends State<CallRoomScreen> { | |||||||
|             child: ClipRRect( |             child: ClipRRect( | ||||||
|               borderRadius: const BorderRadius.all(Radius.circular(8)), |               borderRadius: const BorderRadius.all(Radius.circular(8)), | ||||||
|               child: InteractiveParticipantWidget( |               child: InteractiveParticipantWidget( | ||||||
|                 color: Theme.of(context) |                 color: Theme.of(context).colorScheme.surfaceContainerHigh.withOpacity(0.75), | ||||||
|                     .colorScheme |  | ||||||
|                     .surfaceContainerHigh |  | ||||||
|                     .withOpacity(0.75), |  | ||||||
|                 participant: track, |                 participant: track, | ||||||
|                 onTap: () { |                 onTap: () { | ||||||
|                   if (track.participant.sid != |                   if (track.participant.sid != call.focusTrack?.participant.sid) { | ||||||
|                       call.focusTrack?.participant.sid) { |  | ||||||
|                     call.setFocusTrack(track); |                     call.setFocusTrack(track); | ||||||
|                   } |                   } | ||||||
|                 }, |                 }, | ||||||
| @@ -160,150 +155,127 @@ class _CallRoomScreenState extends State<CallRoomScreen> { | |||||||
|                 text: TextSpan(children: [ |                 text: TextSpan(children: [ | ||||||
|                   TextSpan( |                   TextSpan( | ||||||
|                     text: 'call'.tr(), |                     text: 'call'.tr(), | ||||||
|                     style: Theme.of(context) |                     style: Theme.of(context).textTheme.titleLarge!.copyWith(color: Colors.white), | ||||||
|                         .textTheme |  | ||||||
|                         .titleLarge! |  | ||||||
|                         .copyWith(color: Colors.white), |  | ||||||
|                   ), |                   ), | ||||||
|                   const TextSpan(text: '\n'), |                   const TextSpan(text: '\n'), | ||||||
|                   TextSpan( |                   TextSpan( | ||||||
|                     text: call.lastDuration.toString(), |                     text: call.lastDuration.toString(), | ||||||
|                     style: Theme.of(context) |                     style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.white), | ||||||
|                         .textTheme |  | ||||||
|                         .bodySmall! |  | ||||||
|                         .copyWith(color: Colors.white), |  | ||||||
|                   ), |                   ), | ||||||
|                 ]), |                 ]), | ||||||
|               ), |               ), | ||||||
|             ), |             ), | ||||||
|             body: SafeArea( |             body: GestureDetector( | ||||||
|               child: GestureDetector( |               behavior: HitTestBehavior.translucent, | ||||||
|                 behavior: HitTestBehavior.translucent, |               child: Column( | ||||||
|                 child: Column( |                 children: [ | ||||||
|                   children: [ |                   SizedBox( | ||||||
|  |                     width: MediaQuery.of(context).size.width, | ||||||
|  |                     height: 64, | ||||||
|  |                     child: Row( | ||||||
|  |                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                       crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |                       children: [ | ||||||
|  |                         Builder(builder: (context) { | ||||||
|  |                           final call = context.read<ChatCallProvider>(); | ||||||
|  |                           final connectionQuality = | ||||||
|  |                               call.room.localParticipant?.connectionQuality ?? livekit.ConnectionQuality.unknown; | ||||||
|  |                           return Expanded( | ||||||
|  |                             child: Column( | ||||||
|  |                               mainAxisSize: MainAxisSize.min, | ||||||
|  |                               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                               children: [ | ||||||
|  |                                 Row( | ||||||
|  |                                   children: [ | ||||||
|  |                                     Text( | ||||||
|  |                                       call.channel?.name ?? 'unknown'.tr(), | ||||||
|  |                                       style: const TextStyle( | ||||||
|  |                                         fontWeight: FontWeight.bold, | ||||||
|  |                                       ), | ||||||
|  |                                     ), | ||||||
|  |                                     const Gap(6), | ||||||
|  |                                     Text(call.lastDuration.toString()) | ||||||
|  |                                   ], | ||||||
|  |                                 ), | ||||||
|  |                                 Row( | ||||||
|  |                                   children: [ | ||||||
|  |                                     Text( | ||||||
|  |                                       { | ||||||
|  |                                         livekit.ConnectionState.disconnected: 'callStatusDisconnected'.tr(), | ||||||
|  |                                         livekit.ConnectionState.connected: 'callStatusConnected'.tr(), | ||||||
|  |                                         livekit.ConnectionState.connecting: 'callStatusConnecting'.tr(), | ||||||
|  |                                         livekit.ConnectionState.reconnecting: 'callStatusReconnecting'.tr(), | ||||||
|  |                                       }[call.room.connectionState]!, | ||||||
|  |                                     ), | ||||||
|  |                                     const Gap(6), | ||||||
|  |                                     if (connectionQuality != livekit.ConnectionQuality.unknown) | ||||||
|  |                                       Icon( | ||||||
|  |                                         { | ||||||
|  |                                           livekit.ConnectionQuality.excellent: Icons.signal_cellular_alt, | ||||||
|  |                                           livekit.ConnectionQuality.good: Icons.signal_cellular_alt_2_bar, | ||||||
|  |                                           livekit.ConnectionQuality.poor: Icons.signal_cellular_alt_1_bar, | ||||||
|  |                                         }[connectionQuality], | ||||||
|  |                                         color: { | ||||||
|  |                                           livekit.ConnectionQuality.excellent: Colors.green, | ||||||
|  |                                           livekit.ConnectionQuality.good: Colors.orange, | ||||||
|  |                                           livekit.ConnectionQuality.poor: Colors.red, | ||||||
|  |                                         }[connectionQuality], | ||||||
|  |                                         size: 16, | ||||||
|  |                                       ) | ||||||
|  |                                     else | ||||||
|  |                                       const SizedBox( | ||||||
|  |                                         width: 12, | ||||||
|  |                                         height: 12, | ||||||
|  |                                         child: CircularProgressIndicator( | ||||||
|  |                                           color: Colors.white, | ||||||
|  |                                           strokeWidth: 2, | ||||||
|  |                                         ), | ||||||
|  |                                       ).padding(all: 3), | ||||||
|  |                                   ], | ||||||
|  |                                 ), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                           ); | ||||||
|  |                         }), | ||||||
|  |                         Row( | ||||||
|  |                           children: [ | ||||||
|  |                             IconButton( | ||||||
|  |                               icon: _layoutMode == 0 ? const Icon(Icons.view_list) : const Icon(Icons.grid_view), | ||||||
|  |                               onPressed: () { | ||||||
|  |                                 _switchLayout(); | ||||||
|  |                               }, | ||||||
|  |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ), | ||||||
|  |                       ], | ||||||
|  |                     ).padding(left: 20, right: 16), | ||||||
|  |                   ), | ||||||
|  |                   Expanded( | ||||||
|  |                     child: Material( | ||||||
|  |                       color: Theme.of(context).colorScheme.surfaceContainerLow, | ||||||
|  |                       child: Builder( | ||||||
|  |                         builder: (context) { | ||||||
|  |                           switch (_layoutMode) { | ||||||
|  |                             case 1: | ||||||
|  |                               return _buildGridLayout(); | ||||||
|  |                             default: | ||||||
|  |                               return _buildListLayout(); | ||||||
|  |                           } | ||||||
|  |                         }, | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                   if (call.room.localParticipant != null) | ||||||
|                     SizedBox( |                     SizedBox( | ||||||
|                       width: MediaQuery.of(context).size.width, |                       width: MediaQuery.of(context).size.width, | ||||||
|                       height: 64, |                       child: ControlsWidget( | ||||||
|                       child: Row( |                         call.room, | ||||||
|                         mainAxisAlignment: MainAxisAlignment.spaceBetween, |                         call.room.localParticipant!, | ||||||
|                         crossAxisAlignment: CrossAxisAlignment.center, |  | ||||||
|                         children: [ |  | ||||||
|                           Builder(builder: (context) { |  | ||||||
|                             final call = context.read<ChatCallProvider>(); |  | ||||||
|                             final connectionQuality = |  | ||||||
|                                 call.room.localParticipant?.connectionQuality ?? |  | ||||||
|                                     livekit.ConnectionQuality.unknown; |  | ||||||
|                             return Expanded( |  | ||||||
|                               child: Column( |  | ||||||
|                                 mainAxisSize: MainAxisSize.min, |  | ||||||
|                                 crossAxisAlignment: CrossAxisAlignment.start, |  | ||||||
|                                 children: [ |  | ||||||
|                                   Row( |  | ||||||
|                                     children: [ |  | ||||||
|                                       Text( |  | ||||||
|                                         call.channel?.name ?? 'unknown'.tr(), |  | ||||||
|                                         style: const TextStyle( |  | ||||||
|                                           fontWeight: FontWeight.bold, |  | ||||||
|                                         ), |  | ||||||
|                                       ), |  | ||||||
|                                       const Gap(6), |  | ||||||
|                                       Text(call.lastDuration.toString()) |  | ||||||
|                                     ], |  | ||||||
|                                   ), |  | ||||||
|                                   Row( |  | ||||||
|                                     children: [ |  | ||||||
|                                       Text( |  | ||||||
|                                         { |  | ||||||
|                                           livekit.ConnectionState.disconnected: |  | ||||||
|                                               'callStatusDisconnected'.tr(), |  | ||||||
|                                           livekit.ConnectionState.connected: |  | ||||||
|                                               'callStatusConnected'.tr(), |  | ||||||
|                                           livekit.ConnectionState.connecting: |  | ||||||
|                                               'callStatusConnecting'.tr(), |  | ||||||
|                                           livekit.ConnectionState.reconnecting: |  | ||||||
|                                               'callStatusReconnecting'.tr(), |  | ||||||
|                                         }[call.room.connectionState]!, |  | ||||||
|                                       ), |  | ||||||
|                                       const Gap(6), |  | ||||||
|                                       if (connectionQuality != |  | ||||||
|                                           livekit.ConnectionQuality.unknown) |  | ||||||
|                                         Icon( |  | ||||||
|                                           { |  | ||||||
|                                             livekit.ConnectionQuality.excellent: |  | ||||||
|                                                 Icons.signal_cellular_alt, |  | ||||||
|                                             livekit.ConnectionQuality.good: |  | ||||||
|                                                 Icons.signal_cellular_alt_2_bar, |  | ||||||
|                                             livekit.ConnectionQuality.poor: |  | ||||||
|                                                 Icons.signal_cellular_alt_1_bar, |  | ||||||
|                                           }[connectionQuality], |  | ||||||
|                                           color: { |  | ||||||
|                                             livekit.ConnectionQuality.excellent: |  | ||||||
|                                                 Colors.green, |  | ||||||
|                                             livekit.ConnectionQuality.good: |  | ||||||
|                                                 Colors.orange, |  | ||||||
|                                             livekit.ConnectionQuality.poor: |  | ||||||
|                                                 Colors.red, |  | ||||||
|                                           }[connectionQuality], |  | ||||||
|                                           size: 16, |  | ||||||
|                                         ) |  | ||||||
|                                       else |  | ||||||
|                                         const SizedBox( |  | ||||||
|                                           width: 12, |  | ||||||
|                                           height: 12, |  | ||||||
|                                           child: CircularProgressIndicator( |  | ||||||
|                                             color: Colors.white, |  | ||||||
|                                             strokeWidth: 2, |  | ||||||
|                                           ), |  | ||||||
|                                         ).padding(all: 3), |  | ||||||
|                                     ], |  | ||||||
|                                   ), |  | ||||||
|                                 ], |  | ||||||
|                               ), |  | ||||||
|                             ); |  | ||||||
|                           }), |  | ||||||
|                           Row( |  | ||||||
|                             children: [ |  | ||||||
|                               IconButton( |  | ||||||
|                                 icon: _layoutMode == 0 |  | ||||||
|                                     ? const Icon(Icons.view_list) |  | ||||||
|                                     : const Icon(Icons.grid_view), |  | ||||||
|                                 onPressed: () { |  | ||||||
|                                   _switchLayout(); |  | ||||||
|                                 }, |  | ||||||
|                               ), |  | ||||||
|                             ], |  | ||||||
|                           ), |  | ||||||
|                         ], |  | ||||||
|                       ).padding(left: 20, right: 16), |  | ||||||
|                     ), |  | ||||||
|                     Expanded( |  | ||||||
|                       child: Material( |  | ||||||
|                         color: |  | ||||||
|                             Theme.of(context).colorScheme.surfaceContainerLow, |  | ||||||
|                         child: Builder( |  | ||||||
|                           builder: (context) { |  | ||||||
|                             switch (_layoutMode) { |  | ||||||
|                               case 1: |  | ||||||
|                                 return _buildGridLayout(); |  | ||||||
|                               default: |  | ||||||
|                                 return _buildListLayout(); |  | ||||||
|                             } |  | ||||||
|                           }, |  | ||||||
|                         ), |  | ||||||
|                       ), |                       ), | ||||||
|                     ), |                     ), | ||||||
|                     if (call.room.localParticipant != null) |                 ], | ||||||
|                       SizedBox( |  | ||||||
|                         width: MediaQuery.of(context).size.width, |  | ||||||
|                         child: ControlsWidget( |  | ||||||
|                           call.room, |  | ||||||
|                           call.room.localParticipant!, |  | ||||||
|                         ), |  | ||||||
|                       ), |  | ||||||
|                   ], |  | ||||||
|                 ), |  | ||||||
|                 onTap: () {}, |  | ||||||
|               ), |               ), | ||||||
|  |               onTap: () {}, | ||||||
|             ), |             ), | ||||||
|           ); |           ); | ||||||
|         }); |         }); | ||||||
|   | |||||||
| @@ -276,6 +276,30 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                     }); |                     }); | ||||||
|                   }, |                   }, | ||||||
|                 ), |                 ), | ||||||
|  |                 CheckboxListTile( | ||||||
|  |                   secondary: const Icon(Symbols.link), | ||||||
|  |                   title: Text('settingsExpandPostLink').tr(), | ||||||
|  |                   subtitle: Text('settingsExpandPostLinkDescription').tr(), | ||||||
|  |                   contentPadding: const EdgeInsets.only(left: 24, right: 17), | ||||||
|  |                   value: _prefs.getBool(kAppExpandPostLink) ?? true, | ||||||
|  |                   onChanged: (value) { | ||||||
|  |                     setState(() { | ||||||
|  |                       _prefs.setBool(kAppExpandPostLink, value ?? false); | ||||||
|  |                     }); | ||||||
|  |                   }, | ||||||
|  |                 ), | ||||||
|  |                 CheckboxListTile( | ||||||
|  |                   secondary: const Icon(Symbols.chat), | ||||||
|  |                   title: Text('settingsExpandChatLink').tr(), | ||||||
|  |                   subtitle: Text('settingsExpandChatLinkDescription').tr(), | ||||||
|  |                   contentPadding: const EdgeInsets.only(left: 24, right: 17), | ||||||
|  |                   value: _prefs.getBool(kAppExpandChatLink) ?? true, | ||||||
|  |                   onChanged: (value) { | ||||||
|  |                     setState(() { | ||||||
|  |                       _prefs.setBool(kAppExpandChatLink, value ?? false); | ||||||
|  |                     }); | ||||||
|  |                   }, | ||||||
|  |                 ), | ||||||
|               ], |               ], | ||||||
|             ), |             ), | ||||||
|             Column( |             Column( | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import 'package:material_symbols_icons/symbols.dart'; | |||||||
| import 'package:popover/popover.dart'; | import 'package:popover/popover.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| import 'package:styled_widget/styled_widget.dart'; | import 'package:styled_widget/styled_widget.dart'; | ||||||
|  | import 'package:surface/providers/config.dart'; | ||||||
| import 'package:surface/providers/user_directory.dart'; | import 'package:surface/providers/user_directory.dart'; | ||||||
| import 'package:surface/providers/userinfo.dart'; | import 'package:surface/providers/userinfo.dart'; | ||||||
| import 'package:surface/types/chat.dart'; | import 'package:surface/types/chat.dart'; | ||||||
| @@ -53,6 +54,8 @@ class ChatMessage extends StatelessWidget { | |||||||
|  |  | ||||||
|     final dateFormatter = DateFormat('MM/dd HH:mm'); |     final dateFormatter = DateFormat('MM/dd HH:mm'); | ||||||
|  |  | ||||||
|  |     final cfg = context.read<ConfigProvider>(); | ||||||
|  |  | ||||||
|     return SwipeTo( |     return SwipeTo( | ||||||
|       key: Key('chat-message-${data.id}'), |       key: Key('chat-message-${data.id}'), | ||||||
|       iconOnLeftSwipe: Symbols.reply, |       iconOnLeftSwipe: Symbols.reply, | ||||||
| @@ -192,7 +195,10 @@ class ChatMessage extends StatelessWidget { | |||||||
|                 ], |                 ], | ||||||
|               ).opacity(isPending ? 0.5 : 1), |               ).opacity(isPending ? 0.5 : 1), | ||||||
|             ), |             ), | ||||||
|             if (data.body['text'] != null && data.type == 'messages.new' && (data.body['text']?.isNotEmpty ?? false)) |             if (data.body['text'] != null && | ||||||
|  |                 data.type == 'messages.new' && | ||||||
|  |                 (data.body['text']?.isNotEmpty ?? false) && | ||||||
|  |                 (cfg.prefs.getBool(kAppExpandChatLink) ?? true)) | ||||||
|               LinkPreviewWidget(text: data.body['text']!), |               LinkPreviewWidget(text: data.body['text']!), | ||||||
|             if (data.preload?.attachments?.isNotEmpty ?? false) |             if (data.preload?.attachments?.isNotEmpty ?? false) | ||||||
|               AttachmentList( |               AttachmentList( | ||||||
|   | |||||||
| @@ -7,12 +7,11 @@ import 'package:marquee/marquee.dart'; | |||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| import 'package:responsive_framework/responsive_framework.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/link_preview.dart'; | ||||||
| import 'package:surface/types/link.dart'; | import 'package:surface/types/link.dart'; | ||||||
| import 'package:surface/widgets/universal_image.dart'; | import 'package:surface/widgets/universal_image.dart'; | ||||||
| import 'package:url_launcher/url_launcher_string.dart'; | import 'package:url_launcher/url_launcher_string.dart'; | ||||||
|  |  | ||||||
| import '../providers/link_preview.dart'; |  | ||||||
|  |  | ||||||
| class LinkPreviewWidget extends StatefulWidget { | class LinkPreviewWidget extends StatefulWidget { | ||||||
|   final String text; |   final String text; | ||||||
|  |  | ||||||
| @@ -81,8 +80,9 @@ class _LinkPreviewEntry extends StatelessWidget { | |||||||
|                   child: AspectRatio( |                   child: AspectRatio( | ||||||
|                     aspectRatio: 16 / 9, |                     aspectRatio: 16 / 9, | ||||||
|                     child: ClipRRect( |                     child: ClipRRect( | ||||||
|  |                       borderRadius: const BorderRadius.all(Radius.circular(8)), | ||||||
|                       child: AutoResizeUniversalImage( |                       child: AutoResizeUniversalImage( | ||||||
|                         meta.image!, |                         meta.image!.startsWith('//') ? 'https:${meta.image}' : meta.image!, | ||||||
|                         fit: BoxFit.contain, |                         fit: BoxFit.contain, | ||||||
|                       ), |                       ), | ||||||
|                     ), |                     ), | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| import 'dart:io'; | import 'dart:io'; | ||||||
|  |  | ||||||
| import 'package:bitsdojo_window/bitsdojo_window.dart'; | import 'package:bitsdojo_window/bitsdojo_window.dart'; | ||||||
| import 'package:easy_localization/easy_localization.dart'; |  | ||||||
| import 'package:flutter/foundation.dart'; | import 'package:flutter/foundation.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:go_router/go_router.dart'; | import 'package:go_router/go_router.dart'; | ||||||
| @@ -12,7 +11,6 @@ import 'package:styled_widget/styled_widget.dart'; | |||||||
| import 'package:surface/providers/config.dart'; | import 'package:surface/providers/config.dart'; | ||||||
| import 'package:surface/providers/navigation.dart'; | import 'package:surface/providers/navigation.dart'; | ||||||
| import 'package:surface/widgets/connection_indicator.dart'; | import 'package:surface/widgets/connection_indicator.dart'; | ||||||
| import 'package:surface/widgets/dialog.dart'; |  | ||||||
| import 'package:surface/widgets/navigation/app_background.dart'; | import 'package:surface/widgets/navigation/app_background.dart'; | ||||||
| import 'package:surface/widgets/navigation/app_bottom_navigation.dart'; | import 'package:surface/widgets/navigation/app_bottom_navigation.dart'; | ||||||
| import 'package:surface/widgets/navigation/app_drawer_navigation.dart'; | import 'package:surface/widgets/navigation/app_drawer_navigation.dart'; | ||||||
|   | |||||||
| @@ -203,6 +203,8 @@ class PostItem extends StatelessWidget { | |||||||
|         ?.where((ele) => ele?.mediaType != SnMediaType.image || data.type != 'article') |         ?.where((ele) => ele?.mediaType != SnMediaType.image || data.type != 'article') | ||||||
|         .toList(); |         .toList(); | ||||||
|  |  | ||||||
|  |     final cfg = context.read<ConfigProvider>(); | ||||||
|  |  | ||||||
|     return Column( |     return Column( | ||||||
|       crossAxisAlignment: CrossAxisAlignment.center, |       crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|       children: [ |       children: [ | ||||||
| @@ -261,7 +263,7 @@ class PostItem extends StatelessWidget { | |||||||
|             fit: showFullPost ? BoxFit.cover : BoxFit.contain, |             fit: showFullPost ? BoxFit.cover : BoxFit.contain, | ||||||
|             padding: const EdgeInsets.symmetric(horizontal: 12), |             padding: const EdgeInsets.symmetric(horizontal: 12), | ||||||
|           ), |           ), | ||||||
|         if (data.body['content'] != null) |         if (data.body['content'] != null && (cfg.prefs.getBool(kAppExpandPostLink) ?? true)) | ||||||
|           LinkPreviewWidget( |           LinkPreviewWidget( | ||||||
|             text: data.body['content'], |             text: data.body['content'], | ||||||
|           ).padding(horizontal: 4), |           ).padding(horizontal: 4), | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user