Complete the realm system

This commit is contained in:
LittleSheep 2024-05-06 20:57:52 +08:00
parent 0b9439262c
commit 22c2a80650
7 changed files with 258 additions and 19 deletions

View File

@ -87,6 +87,8 @@
"realmDescriptionLabel": "Realm Description", "realmDescriptionLabel": "Realm Description",
"realmPublicLabel": "It's public", "realmPublicLabel": "It's public",
"realmCommunityLabel": "It's community realm", "realmCommunityLabel": "It's community realm",
"realmMember": "Member",
"realmManage": "Realm Manage",
"chatNew": "New Chat", "chatNew": "New Chat",
"chatNewCreate": "Create a channel", "chatNewCreate": "Create a channel",
"chatNewJoin": "Join a exists channel", "chatNewJoin": "Join a exists channel",

View File

@ -21,8 +21,10 @@ import 'package:solian/screens/posts/moment_editor.dart';
import 'package:solian/screens/posts/screen.dart'; import 'package:solian/screens/posts/screen.dart';
import 'package:solian/screens/auth/signin.dart'; import 'package:solian/screens/auth/signin.dart';
import 'package:solian/screens/realms/realm.dart'; import 'package:solian/screens/realms/realm.dart';
import 'package:solian/screens/realms/realm_manage.dart';
import 'package:solian/screens/realms/realm_editor.dart'; import 'package:solian/screens/realms/realm_editor.dart';
import 'package:solian/screens/realms/realm_list.dart'; import 'package:solian/screens/realms/realm_list.dart';
import 'package:solian/screens/realms/realm_member.dart';
import 'package:solian/screens/users/userinfo.dart'; import 'package:solian/screens/users/userinfo.dart';
import 'package:solian/utils/theme.dart'; import 'package:solian/utils/theme.dart';
import 'package:solian/widgets/empty.dart'; import 'package:solian/widgets/empty.dart';
@ -103,6 +105,16 @@ abstract class SolianRouter {
realm: state.uri.queryParameters['realm'], realm: state.uri.queryParameters['realm'],
), ),
), ),
GoRoute(
path: '/realms/:realm/manage',
name: 'realms.manage',
builder: (context, state) => RealmManageScreen(realm: state.extra as Realm),
),
GoRoute(
path: '/realms/:realm/member',
name: 'realms.member',
builder: (context, state) => RealmMemberScreen(realm: state.extra as Realm),
),
GoRoute( GoRoute(
path: '/realms/:realm/posts/:dataset/:alias', path: '/realms/:realm/posts/:dataset/:alias',
name: 'realms.posts.details', name: 'realms.posts.details',

View File

@ -6,7 +6,6 @@ import 'package:solian/providers/realm.dart';
import 'package:solian/router.dart'; import 'package:solian/router.dart';
import 'package:solian/screens/chat/chat_list.dart'; import 'package:solian/screens/chat/chat_list.dart';
import 'package:solian/screens/explore.dart'; import 'package:solian/screens/explore.dart';
import 'package:solian/screens/realms/realm_member.dart';
import 'package:solian/utils/theme.dart'; import 'package:solian/utils/theme.dart';
import 'package:solian/widgets/scaffold.dart'; import 'package:solian/widgets/scaffold.dart';
@ -82,7 +81,7 @@ class _RealmWidgetState extends State<RealmWidget> {
} }
return DefaultTabController( return DefaultTabController(
length: 3, length: 2,
child: Column( child: Column(
children: [ children: [
TabBar( TabBar(
@ -90,7 +89,6 @@ class _RealmWidgetState extends State<RealmWidget> {
tabs: const [ tabs: const [
Tab(icon: Icon(Icons.newspaper)), Tab(icon: Icon(Icons.newspaper)),
Tab(icon: Icon(Icons.message)), Tab(icon: Icon(Icons.message)),
Tab(icon: Icon(Icons.supervisor_account))
], ],
), ),
Expanded( Expanded(
@ -98,11 +96,6 @@ class _RealmWidgetState extends State<RealmWidget> {
children: [ children: [
ExplorePostWidget(realm: widget.alias), ExplorePostWidget(realm: widget.alias),
ChatListWidget(realm: widget.alias), ChatListWidget(realm: widget.alias),
_realm.focusRealm != null
? RealmMemberWidget(realm: _realm.focusRealm!)
: const Center(
child: CircularProgressIndicator(),
),
], ],
), ),
) )
@ -127,8 +120,9 @@ class RealmManageAction extends StatelessWidget {
return IconButton( return IconButton(
onPressed: () async { onPressed: () async {
final did = await SolianRouter.router.pushNamed( final did = await SolianRouter.router.pushNamed(
'realms.editor', 'realms.manage',
extra: realm, extra: realm,
pathParameters: {'realm': realm.alias},
); );
if (did == true) onUpdate(); if (did == true) onUpdate();
}, },

View File

@ -8,6 +8,7 @@ import 'package:solian/models/realm.dart';
import 'package:solian/providers/auth.dart'; import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart'; import 'package:solian/router.dart';
import 'package:solian/utils/service_url.dart'; import 'package:solian/utils/service_url.dart';
import 'package:solian/utils/theme.dart';
import 'package:solian/widgets/exts.dart'; import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/scaffold.dart'; import 'package:solian/widgets/scaffold.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@ -117,6 +118,7 @@ class _RealmEditorScreenState extends State<RealmEditorScreen> {
child: Text(AppLocalizations.of(context)!.apply.toUpperCase()), child: Text(AppLocalizations.of(context)!.apply.toUpperCase()),
), ),
], ],
fixedAppBarColor: SolianTheme.isLargeScreen(context),
child: Column( child: Column(
children: [ children: [
_isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(), _isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(),

View File

@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:solian/models/realm.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart';
import 'package:solian/widgets/realms/realm_deletion.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class RealmManageScreen extends StatefulWidget {
final Realm realm;
const RealmManageScreen({super.key, required this.realm});
@override
State<RealmManageScreen> createState() => _RealmManageScreenState();
}
class _RealmManageScreenState extends State<RealmManageScreen> {
bool _isOwned = false;
void promptLeaveChannel() async {
final did = await showDialog(
context: context,
builder: (context) => RealmDeletion(
realm: widget.realm,
isOwned: _isOwned,
),
);
if (did == true && SolianRouter.router.canPop()) {
SolianRouter.router.pop('disposed');
}
}
@override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
final auth = context.read<AuthProvider>();
final prof = await auth.getProfiles();
setState(() {
_isOwned = prof['id'] == widget.realm.accountId;
});
});
}
@override
Widget build(BuildContext context) {
final authorizedItems = [
ListTile(
leading: const Icon(Icons.settings),
title: Text(AppLocalizations.of(context)!.settings),
onTap: () async {
SolianRouter.router.pushNamed('realms.editor', extra: widget.realm).then((did) {
if (did == true) {
if (SolianRouter.router.canPop()) SolianRouter.router.pop('refresh');
}
});
},
),
];
return IndentScaffold(
title: AppLocalizations.of(context)!.realmManage,
hideDrawer: true,
noSafeArea: true,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
const CircleAvatar(
radius: 24,
backgroundColor: Colors.teal,
child: Icon(Icons.tag, color: Colors.white),
),
const SizedBox(width: 16),
Expanded(
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(widget.realm.name, style: Theme.of(context).textTheme.bodyLarge),
Text(widget.realm.description, style: Theme.of(context).textTheme.bodySmall),
]),
)
],
),
),
const Divider(thickness: 0.3),
Expanded(
child: ListView(
children: [
ListTile(
leading: const Icon(Icons.supervisor_account),
title: Text(AppLocalizations.of(context)!.chatMember),
onTap: () {
SolianRouter.router.pushNamed(
'realms.member',
extra: widget.realm,
pathParameters: {'realm': widget.realm.alias},
);
},
),
...(_isOwned ? authorizedItems : List.empty()),
const Divider(thickness: 0.3),
ListTile(
leading: _isOwned ? const Icon(Icons.delete) : const Icon(Icons.exit_to_app),
title: Text(_isOwned ? AppLocalizations.of(context)!.delete : AppLocalizations.of(context)!.exit),
onTap: () => promptLeaveChannel(),
),
],
),
),
],
),
);
}
}

View File

@ -7,20 +7,23 @@ import 'package:solian/models/account.dart';
import 'package:solian/models/realm.dart'; import 'package:solian/models/realm.dart';
import 'package:solian/providers/auth.dart'; import 'package:solian/providers/auth.dart';
import 'package:solian/utils/service_url.dart'; import 'package:solian/utils/service_url.dart';
import 'package:solian/utils/theme.dart';
import 'package:solian/widgets/account/account_avatar.dart'; import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/account/friend_picker.dart'; import 'package:solian/widgets/account/friend_picker.dart';
import 'package:solian/widgets/exts.dart'; import 'package:solian/widgets/exts.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:solian/widgets/scaffold.dart';
class RealmMemberWidget extends StatefulWidget { class RealmMemberScreen extends StatefulWidget {
final Realm realm; final Realm realm;
const RealmMemberWidget({super.key, required this.realm}); const RealmMemberScreen({super.key, required this.realm});
@override @override
State<RealmMemberWidget> createState() => _RealmMemberWidgetState(); State<RealmMemberScreen> createState() => _RealmMemberScreenState();
} }
class _RealmMemberWidgetState extends State<RealmMemberWidget> { class _RealmMemberScreenState extends State<RealmMemberScreen> {
bool _isSubmitting = false; bool _isSubmitting = false;
List<RealmMember> _members = List.empty(); List<RealmMember> _members = List.empty();
@ -136,12 +139,18 @@ class _RealmMemberWidgetState extends State<RealmMemberWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return IndentScaffold(
floatingActionButton: FloatingActionButton( title: AppLocalizations.of(context)!.realmMember,
child: const Icon(Icons.add), fixedAppBarColor: SolianTheme.isLargeScreen(context),
noSafeArea: true,
hideDrawer: true,
appBarActions: [
IconButton(
icon: const Icon(Icons.add),
onPressed: () => promptAddMember(), onPressed: () => promptAddMember(),
), ),
body: RefreshIndicator( ],
child: RefreshIndicator(
onRefresh: () => fetchMemberships(), onRefresh: () => fetchMemberships(),
child: CustomScrollView( child: CustomScrollView(
slivers: [ slivers: [

View File

@ -0,0 +1,101 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:solian/models/realm.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/exts.dart';
class RealmDeletion extends StatefulWidget {
final Realm realm;
final bool isOwned;
const RealmDeletion({
super.key,
required this.realm,
required this.isOwned,
});
@override
State<RealmDeletion> createState() => _RealmDeletionState();
}
class _RealmDeletionState extends State<RealmDeletion> {
bool _isSubmitting = false;
Future<void> deleteChannel() async {
setState(() => _isSubmitting = true);
final auth = context.read<AuthProvider>();
if (!await auth.isAuthorized()) {
setState(() => _isSubmitting = false);
return;
}
var res = await auth.client!.delete(
getRequestUri('passport', '/api/realms/${widget.realm.alias}'),
);
if (res.statusCode != 200) {
var message = utf8.decode(res.bodyBytes);
context.showErrorDialog(message);
} else if (Navigator.canPop(context)) {
Navigator.pop(context, true);
}
setState(() => _isSubmitting = false);
}
Future<void> leaveChannel() async {
setState(() => _isSubmitting = true);
final auth = context.read<AuthProvider>();
if (!await auth.isAuthorized()) {
setState(() => _isSubmitting = false);
return;
}
var res = await auth.client!.delete(
getRequestUri('passport', '/api/realms/${widget.realm.alias}/members/me'),
);
if (res.statusCode != 200) {
var message = utf8.decode(res.bodyBytes);
context.showErrorDialog(message);
} else if (Navigator.canPop(context)) {
Navigator.pop(context, true);
}
setState(() => _isSubmitting = false);
}
@override
Widget build(BuildContext context) {
final content = widget.isOwned
? AppLocalizations.of(context)!.chatChannelDeleteConfirm
: AppLocalizations.of(context)!.chatChannelLeaveConfirm;
return AlertDialog(
title: Text(AppLocalizations.of(context)!.confirmation),
content: Text(content),
actions: <Widget>[
TextButton(
onPressed: _isSubmitting ? null : () => Navigator.pop(context),
child: Text(AppLocalizations.of(context)!.confirmCancel),
),
TextButton(
onPressed: _isSubmitting
? null
: () {
if (widget.isOwned) {
deleteChannel();
} else {
leaveChannel();
}
},
child: Text(AppLocalizations.of(context)!.confirmOkay),
),
],
);
}
}