🐛 Fix several known bugs

This commit is contained in:
LittleSheep 2024-05-08 22:01:06 +08:00
parent a4f8c65aa5
commit c1d3bac0c8
27 changed files with 88 additions and 61 deletions

View File

@ -205,6 +205,7 @@ class ChatProvider extends ChangeNotifier {
void unFocus() { void unFocus() {
currentCall = null; currentCall = null;
focusChannel = null; focusChannel = null;
historyPagingController?.dispose();
historyPagingController = null; historyPagingController = null;
notifyListeners(); notifyListeners();
} }

View File

@ -14,9 +14,8 @@ class AccountScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.account, title: AppLocalizations.of(context)!.account,
noSafeArea: true,
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
child: AccountScreenWidget( body: AccountScreenWidget(
onSelect: (item) { onSelect: (item) {
SolianRouter.router.pushNamed(item); SolianRouter.router.pushNamed(item);
}, },

View File

@ -18,9 +18,8 @@ class FriendScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.friend, title: AppLocalizations.of(context)!.friend,
noSafeArea: true,
hideDrawer: true, hideDrawer: true,
child: const FriendScreenWidget(), body: const FriendScreenWidget(),
); );
} }
} }

View File

@ -22,7 +22,7 @@ class PersonalizeScreen extends StatelessWidget {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.personalize, title: AppLocalizations.of(context)!.personalize,
hideDrawer: true, hideDrawer: true,
child: const PersonalizeScreenWidget(), body: const PersonalizeScreenWidget(),
); );
} }
} }

View File

@ -64,7 +64,7 @@ class SignInScreen extends StatelessWidget {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.signIn, title: AppLocalizations.of(context)!.signIn,
hideDrawer: true, hideDrawer: true,
child: Center( body: Center(
child: Container( child: Container(
width: MediaQuery.of(context).size.width * 0.6, width: MediaQuery.of(context).size.width * 0.6,
constraints: const BoxConstraints(maxWidth: 360), constraints: const BoxConstraints(maxWidth: 360),

View File

@ -71,7 +71,7 @@ class SignUpScreen extends StatelessWidget {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.signUp, title: AppLocalizations.of(context)!.signUp,
hideDrawer: true, hideDrawer: true,
child: Center( body: Center(
child: Container( child: Container(
width: MediaQuery.of(context).size.width * 0.6, width: MediaQuery.of(context).size.width * 0.6,
constraints: const BoxConstraints(maxWidth: 360), constraints: const BoxConstraints(maxWidth: 360),

View File

@ -132,7 +132,7 @@ class _ChatCallState extends State<ChatCall> {
title: AppLocalizations.of(context)!.chatCall, title: AppLocalizations.of(context)!.chatCall,
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
hideDrawer: true, hideDrawer: true,
child: content, body: content,
); );
} }

View File

@ -102,6 +102,7 @@ class _ChannelEditorScreenState extends State<ChannelEditorScreen> {
return IndentScaffold( return IndentScaffold(
hideDrawer: true, hideDrawer: true,
showSafeArea: true,
title: AppLocalizations.of(context)!.chatChannelOrganize, title: AppLocalizations.of(context)!.chatChannelOrganize,
appBarActions: <Widget>[ appBarActions: <Widget>[
TextButton( TextButton(
@ -109,7 +110,7 @@ class _ChannelEditorScreenState extends State<ChannelEditorScreen> {
child: Text(AppLocalizations.of(context)!.apply.toUpperCase()), child: Text(AppLocalizations.of(context)!.apply.toUpperCase()),
), ),
], ],
child: Column( body: Column(
children: [ children: [
_isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(), _isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(),
widget.editing != null ? editingBanner : Container(), widget.editing != null ? editingBanner : Container(),

View File

@ -38,6 +38,7 @@ class _ChatMemberScreenState extends State<ChatMemberScreen> {
_selfId = prof['id']; _selfId = prof['id'];
var uri = getRequestUri('messaging', '/api/channels/${widget.realm}/${widget.channel.alias}/members'); var uri = getRequestUri('messaging', '/api/channels/${widget.realm}/${widget.channel.alias}/members');
print(uri);
var res = await auth.client!.get(uri); var res = await auth.client!.get(uri);
if (res.statusCode == 200) { if (res.statusCode == 200) {
@ -141,7 +142,6 @@ class _ChatMemberScreenState extends State<ChatMemberScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.chatMember, title: AppLocalizations.of(context)!.chatMember,
noSafeArea: true,
hideDrawer: true, hideDrawer: true,
appBarActions: [ appBarActions: [
IconButton( IconButton(
@ -149,7 +149,7 @@ class _ChatMemberScreenState extends State<ChatMemberScreen> {
onPressed: () => promptAddMember(), onPressed: () => promptAddMember(),
), ),
], ],
child: RefreshIndicator( body: RefreshIndicator(
onRefresh: () => fetchMemberships(), onRefresh: () => fetchMemberships(),
child: CustomScrollView( child: CustomScrollView(
slivers: [ slivers: [

View File

@ -32,6 +32,7 @@ class ChatScreen extends StatelessWidget {
return IndentScaffold( return IndentScaffold(
title: chat.focusChannel?.name ?? 'Loading...', title: chat.focusChannel?.name ?? 'Loading...',
hideDrawer: true, hideDrawer: true,
showSafeArea: true,
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
appBarActions: chat.focusChannel != null appBarActions: chat.focusChannel != null
? [ ? [
@ -48,7 +49,7 @@ class ChatScreen extends StatelessWidget {
), ),
] ]
: [], : [],
child: ChatWidget( body: ChatWidget(
alias: alias, alias: alias,
realm: realm, realm: realm,
), ),
@ -96,7 +97,7 @@ class _ChatWidgetState extends State<ChatWidget> {
if (a?.replyTo != null) return false; if (a?.replyTo != null) return false;
if (a == null || b == null) return false; if (a == null || b == null) return false;
if (a.senderId != b.senderId) return false; if (a.senderId != b.senderId) return false;
return a.createdAt.difference(b.createdAt).inMinutes <= 5; return a.createdAt.difference(b.createdAt).inMinutes <= 3;
} }
Message? _editingItem; Message? _editingItem;
@ -107,6 +108,7 @@ class _ChatWidgetState extends State<ChatWidget> {
context: context, context: context,
builder: (context) => ChatMessageAction( builder: (context) => ChatMessageAction(
channel: widget.alias, channel: widget.alias,
realm: widget.realm,
item: item, item: item,
onEdit: () => setState(() { onEdit: () => setState(() {
_editingItem = item; _editingItem = item;

View File

@ -67,8 +67,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.chatDetail, title: AppLocalizations.of(context)!.chatDetail,
hideDrawer: true, hideDrawer: true,
noSafeArea: true, body: Column(
child: Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
@ -103,9 +102,12 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
title: Text(AppLocalizations.of(context)!.chatMember), title: Text(AppLocalizations.of(context)!.chatMember),
onTap: () { onTap: () {
SolianRouter.router.pushNamed( SolianRouter.router.pushNamed(
'chat.channel.member', widget.realm == 'global' ? 'chat.channel.member' : 'realms.chat.channel.member',
extra: widget.channel, extra: widget.channel,
pathParameters: {'channel': widget.channel.alias}, pathParameters: {
'channel': widget.channel.alias,
...(widget.realm == 'global' ? {} : {'realm': widget.realm}),
},
); );
}, },
), ),

View File

@ -24,7 +24,7 @@ class ChatListScreen extends StatelessWidget {
title: AppLocalizations.of(context)!.chat, title: AppLocalizations.of(context)!.chat,
appBarActions: const [NotificationButton()], appBarActions: const [NotificationButton()],
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
child: const ChatListWidget(), body: const ChatListWidget(),
); );
} }
} }

View File

@ -23,11 +23,10 @@ class ExplorePostScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return IndentScaffold( return IndentScaffold(
noSafeArea: true,
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
appBarActions: const [NotificationButton()], appBarActions: const [NotificationButton()],
title: AppLocalizations.of(context)!.explore, title: AppLocalizations.of(context)!.explore,
child: const ExplorePostWidget(showRealmShortcuts: true), body: const ExplorePostWidget(showRealmShortcuts: true),
); );
} }
} }

View File

@ -73,10 +73,9 @@ class _NotificationScreenState extends State<NotificationScreen> {
final nty = context.watch<NotifyProvider>(); final nty = context.watch<NotifyProvider>();
return IndentScaffold( return IndentScaffold(
noSafeArea: true,
hideDrawer: true, hideDrawer: true,
title: AppLocalizations.of(context)!.notification, title: AppLocalizations.of(context)!.notification,
child: RefreshIndicator( body: RefreshIndicator(
onRefresh: () => nty.fetch(auth), onRefresh: () => nty.fetch(auth),
child: CustomScrollView( child: CustomScrollView(
slivers: [ slivers: [

View File

@ -122,6 +122,7 @@ class _CommentEditorScreenState extends State<CommentEditorScreen> {
return IndentScaffold( return IndentScaffold(
hideDrawer: true, hideDrawer: true,
showSafeArea: true,
title: AppLocalizations.of(context)!.newComment, title: AppLocalizations.of(context)!.newComment,
appBarActions: <Widget>[ appBarActions: <Widget>[
TextButton( TextButton(
@ -129,7 +130,7 @@ class _CommentEditorScreenState extends State<CommentEditorScreen> {
child: Text(AppLocalizations.of(context)!.postVerb.toUpperCase()), child: Text(AppLocalizations.of(context)!.postVerb.toUpperCase()),
), ),
], ],
child: Column( body: Column(
children: [ children: [
_isSubmitting _isSubmitting
? const LinearProgressIndicator().animate().scaleX() ? const LinearProgressIndicator().animate().scaleX()

View File

@ -55,6 +55,7 @@ class _MomentEditorScreenState extends State<MomentEditorScreen> {
final uri = widget.editing == null final uri = widget.editing == null
? getRequestUri('interactive', '/api/p/moments') ? getRequestUri('interactive', '/api/p/moments')
: getRequestUri('interactive', '/api/p/moments/${widget.editing!.id}'); : getRequestUri('interactive', '/api/p/moments/${widget.editing!.id}');
print(uri);
final req = Request(widget.editing == null ? 'POST' : 'PUT', uri); final req = Request(widget.editing == null ? 'POST' : 'PUT', uri);
req.headers['Content-Type'] = 'application/json'; req.headers['Content-Type'] = 'application/json';
@ -113,6 +114,7 @@ class _MomentEditorScreenState extends State<MomentEditorScreen> {
); );
return IndentScaffold( return IndentScaffold(
showSafeArea: true,
hideDrawer: true, hideDrawer: true,
title: AppLocalizations.of(context)!.newMoment, title: AppLocalizations.of(context)!.newMoment,
appBarActions: <Widget>[ appBarActions: <Widget>[
@ -121,7 +123,7 @@ class _MomentEditorScreenState extends State<MomentEditorScreen> {
child: Text(AppLocalizations.of(context)!.postVerb.toUpperCase()), child: Text(AppLocalizations.of(context)!.postVerb.toUpperCase()),
), ),
], ],
child: Column( body: Column(
children: [ children: [
_isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(), _isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(),
FutureBuilder( FutureBuilder(

View File

@ -21,9 +21,8 @@ class PostScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.post, title: AppLocalizations.of(context)!.post,
noSafeArea: true,
hideDrawer: true, hideDrawer: true,
child: PostScreenWidget( body: PostScreenWidget(
dataset: dataset, dataset: dataset,
alias: alias, alias: alias,
), ),

View File

@ -21,7 +21,6 @@ class RealmScreen extends StatelessWidget {
return IndentScaffold( return IndentScaffold(
title: realm.focusRealm?.name ?? 'Loading...', title: realm.focusRealm?.name ?? 'Loading...',
noSafeArea: true,
hideDrawer: true, hideDrawer: true,
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
appBarActions: realm.focusRealm != null appBarActions: realm.focusRealm != null
@ -32,17 +31,15 @@ class RealmScreen extends StatelessWidget {
), ),
] ]
: [], : [],
appBarLeading: IconButton( appBarLeading: SolianTheme.isLargeScreen(context)
? IconButton(
icon: const Icon(Icons.arrow_back), icon: const Icon(Icons.arrow_back),
onPressed: () { onPressed: () {
if (SolianTheme.isLargeScreen(context)) {
realm.clearFocus(); realm.clearFocus();
} else if (SolianRouter.router.canPop()) {
SolianRouter.router.pop();
}
}, },
), )
child: RealmWidget( : null,
body: RealmWidget(
alias: alias, alias: alias,
), ),
); );

View File

@ -111,6 +111,7 @@ class _RealmEditorScreenState extends State<RealmEditorScreen> {
return IndentScaffold( return IndentScaffold(
hideDrawer: true, hideDrawer: true,
showSafeArea: true,
title: AppLocalizations.of(context)!.realmEstablish, title: AppLocalizations.of(context)!.realmEstablish,
appBarActions: <Widget>[ appBarActions: <Widget>[
TextButton( TextButton(
@ -119,7 +120,7 @@ class _RealmEditorScreenState extends State<RealmEditorScreen> {
), ),
], ],
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
child: Column( body: Column(
children: [ children: [
_isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(), _isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(),
widget.editing != null ? editingBanner : Container(), widget.editing != null ? editingBanner : Container(),

View File

@ -23,7 +23,7 @@ class RealmListScreen extends StatelessWidget {
title: AppLocalizations.of(context)!.realm, title: AppLocalizations.of(context)!.realm,
appBarActions: const [NotificationButton()], appBarActions: const [NotificationButton()],
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
child: const RealmListWidget(), body: const RealmListWidget(),
) )
: RealmScreen(alias: realm.focusRealm!.alias); : RealmScreen(alias: realm.focusRealm!.alias);
} }

View File

@ -65,8 +65,7 @@ class _RealmManageScreenState extends State<RealmManageScreen> {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.realmManage, title: AppLocalizations.of(context)!.realmManage,
hideDrawer: true, hideDrawer: true,
noSafeArea: true, body: Column(
child: Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),

View File

@ -142,7 +142,6 @@ class _RealmMemberScreenState extends State<RealmMemberScreen> {
return IndentScaffold( return IndentScaffold(
title: AppLocalizations.of(context)!.realmMember, title: AppLocalizations.of(context)!.realmMember,
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
noSafeArea: true,
hideDrawer: true, hideDrawer: true,
appBarActions: [ appBarActions: [
IconButton( IconButton(
@ -150,7 +149,7 @@ class _RealmMemberScreenState extends State<RealmMemberScreen> {
onPressed: () => promptAddMember(), onPressed: () => promptAddMember(),
), ),
], ],
child: RefreshIndicator( body: RefreshIndicator(
onRefresh: () => fetchMemberships(), onRefresh: () => fetchMemberships(),
child: CustomScrollView( child: CustomScrollView(
slivers: [ slivers: [

View File

@ -65,8 +65,7 @@ class _UserInfoScreenState extends State<UserInfoScreen> {
title: _userinfo?.nick ?? 'Loading...', title: _userinfo?.nick ?? 'Loading...',
fixedAppBarColor: SolianTheme.isLargeScreen(context), fixedAppBarColor: SolianTheme.isLargeScreen(context),
hideDrawer: true, hideDrawer: true,
noSafeArea: true, body: FutureBuilder(
child: FutureBuilder(
future: fetchUserinfo(), future: fetchUserinfo(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == null) { if (!snapshot.hasData || snapshot.data == null) {

View File

@ -7,6 +7,7 @@ import 'package:solian/widgets/chat/message_deletion.dart';
class ChatMessageAction extends StatelessWidget { class ChatMessageAction extends StatelessWidget {
final String channel; final String channel;
final String realm;
final Message item; final Message item;
final Function? onEdit; final Function? onEdit;
final Function? onReply; final Function? onReply;
@ -15,6 +16,7 @@ class ChatMessageAction extends StatelessWidget {
super.key, super.key,
required this.channel, required this.channel,
required this.item, required this.item,
this.realm = 'global',
this.onEdit, this.onEdit,
this.onReply, this.onReply,
}); });
@ -67,6 +69,7 @@ class ChatMessageAction extends StatelessWidget {
builder: (context) => ChatMessageDeletionDialog( builder: (context) => ChatMessageDeletionDialog(
item: item, item: item,
channel: channel, channel: channel,
realm: realm,
), ),
).then((did) { ).then((did) {
if (did == true && Navigator.canPop(context)) { if (did == true && Navigator.canPop(context)) {

View File

@ -10,17 +10,18 @@ import 'package:solian/widgets/exts.dart';
class ChatMessageDeletionDialog extends StatefulWidget { class ChatMessageDeletionDialog extends StatefulWidget {
final String channel; final String channel;
final String realm;
final Message item; final Message item;
const ChatMessageDeletionDialog({ const ChatMessageDeletionDialog({
super.key, super.key,
required this.item, required this.item,
required this.channel, required this.channel,
this.realm = 'global'
}); });
@override @override
State<ChatMessageDeletionDialog> createState() => State<ChatMessageDeletionDialog> createState() => _ChatMessageDeletionDialogState();
_ChatMessageDeletionDialogState();
} }
class _ChatMessageDeletionDialogState extends State<ChatMessageDeletionDialog> { class _ChatMessageDeletionDialogState extends State<ChatMessageDeletionDialog> {
@ -30,8 +31,8 @@ class _ChatMessageDeletionDialogState extends State<ChatMessageDeletionDialog> {
final auth = context.read<AuthProvider>(); final auth = context.read<AuthProvider>();
if (!await auth.isAuthorized()) return; if (!await auth.isAuthorized()) return;
final uri = getRequestUri('messaging', final uri =
'/api/channels/global/${widget.channel}/messages/${widget.item.id}'); getRequestUri('messaging', '/api/channels/${widget.realm}/${widget.channel}/messages/${widget.item.id}');
setState(() => _isSubmitting = true); setState(() => _isSubmitting = true);
final res = await auth.client!.delete(uri); final res = await auth.client!.delete(uri);

View File

@ -57,11 +57,13 @@ class RealmShortcuts extends StatelessWidget {
onTap: () async { onTap: () async {
if (SolianTheme.isLargeScreen(context)) { if (SolianTheme.isLargeScreen(context)) {
await realm.fetchSingle(auth, element.alias); await realm.fetchSingle(auth, element.alias);
} SolianRouter.router.pushNamed('realms');
} else {
SolianRouter.router.pushNamed( SolianRouter.router.pushNamed(
'realms.details', 'realms.details',
pathParameters: {'realm': element.alias}, pathParameters: {'realm': element.alias},
); );
}
}, },
); );
}, },

View File

@ -1,45 +1,67 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:solian/router.dart';
import 'package:solian/utils/theme.dart'; import 'package:solian/utils/theme.dart';
import 'package:solian/widgets/navigation_drawer.dart'; import 'package:solian/widgets/navigation_drawer.dart';
class IndentScaffold extends StatelessWidget { class IndentScaffold extends StatelessWidget {
final Widget? child; final Widget? body;
final Widget? floatingActionButton; final Widget? floatingActionButton;
final Widget? appBarLeading; final Widget? appBarLeading;
final List<Widget>? appBarActions; final List<Widget>? appBarActions;
final bool noSafeArea;
final bool hideDrawer; final bool hideDrawer;
final bool showSafeArea;
final bool fixedAppBarColor; final bool fixedAppBarColor;
final String title; final String title;
const IndentScaffold({ const IndentScaffold({
super.key, super.key,
this.child, this.body,
required this.title, required this.title,
this.floatingActionButton, this.floatingActionButton,
this.appBarLeading, this.appBarLeading,
this.appBarActions, this.appBarActions,
this.hideDrawer = false, this.hideDrawer = false,
this.showSafeArea = false,
this.fixedAppBarColor = false, this.fixedAppBarColor = false,
this.noSafeArea = false,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final content = child ?? Container(); final backButton = IconButton(
icon: const Icon(Icons.arrow_back),
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
onPressed: () {
if (SolianRouter.router.canPop()) {
SolianRouter.router.pop();
}
},
);
final drawerButton = Builder(
builder: (context) {
return IconButton(
icon: const Icon(Icons.menu),
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
onPressed: () {
Scaffold.of(context).openDrawer();
},
);
}
);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(title), title: Text(title),
leading: appBarLeading, leading: appBarLeading ?? (hideDrawer ? backButton : drawerButton),
actions: appBarActions, actions: appBarActions,
centerTitle: false, centerTitle: false,
elevation: fixedAppBarColor ? 4 : null, elevation: fixedAppBarColor ? 4 : null,
automaticallyImplyLeading: false,
), ),
floatingActionButton: floatingActionButton, floatingActionButton: floatingActionButton,
drawer: !hideDrawer ? const SolianNavigationDrawer() : null, drawer: !hideDrawer ? const SolianNavigationDrawer() : null,
drawerScrimColor: SolianTheme.isLargeScreen(context) ? Colors.transparent : null, drawerScrimColor: SolianTheme.isLargeScreen(context) ? Colors.transparent : null,
body: noSafeArea ? content : SafeArea(child: content), body: showSafeArea ? SafeArea(child: body ?? Container()) : body,
); );
} }
} }