2024-05-29 14:42:11 +00:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|
|
|
import 'package:solian/exts.dart';
|
2024-05-29 15:22:24 +00:00
|
|
|
import 'package:solian/models/channel.dart';
|
2024-05-29 14:42:11 +00:00
|
|
|
import 'package:solian/models/pagination.dart';
|
|
|
|
import 'package:solian/models/post.dart';
|
|
|
|
import 'package:solian/models/realm.dart';
|
2024-05-29 15:22:24 +00:00
|
|
|
import 'package:solian/providers/auth.dart';
|
|
|
|
import 'package:solian/providers/content/channel.dart';
|
2024-07-23 10:09:41 +00:00
|
|
|
import 'package:solian/providers/content/posts.dart';
|
2024-05-29 14:42:11 +00:00
|
|
|
import 'package:solian/providers/content/realm.dart';
|
|
|
|
import 'package:solian/router.dart';
|
2024-05-29 15:22:24 +00:00
|
|
|
import 'package:solian/screens/channel/channel_organize.dart';
|
2024-05-29 14:42:11 +00:00
|
|
|
import 'package:solian/theme.dart';
|
2024-07-12 14:31:45 +00:00
|
|
|
import 'package:solian/widgets/app_bar_leading.dart';
|
2024-05-29 15:22:24 +00:00
|
|
|
import 'package:solian/widgets/channel/channel_list.dart';
|
2024-05-29 14:42:11 +00:00
|
|
|
import 'package:solian/widgets/posts/post_list.dart';
|
|
|
|
|
|
|
|
class RealmViewScreen extends StatefulWidget {
|
|
|
|
final String alias;
|
|
|
|
|
|
|
|
const RealmViewScreen({super.key, required this.alias});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<RealmViewScreen> createState() => _RealmViewScreenState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _RealmViewScreenState extends State<RealmViewScreen> {
|
|
|
|
bool _isBusy = false;
|
|
|
|
String? _overrideAlias;
|
|
|
|
|
|
|
|
Realm? _realm;
|
2024-05-29 15:22:24 +00:00
|
|
|
final List<Channel> _channels = List.empty(growable: true);
|
2024-05-29 14:42:11 +00:00
|
|
|
|
|
|
|
getRealm({String? overrideAlias}) async {
|
2024-09-18 05:03:40 +00:00
|
|
|
final RealmProvider realm = Get.find();
|
2024-05-29 14:42:11 +00:00
|
|
|
|
|
|
|
setState(() => _isBusy = true);
|
|
|
|
|
|
|
|
if (overrideAlias != null) {
|
|
|
|
_overrideAlias = overrideAlias;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2024-09-18 05:03:40 +00:00
|
|
|
final resp = await realm.getRealm(_overrideAlias ?? widget.alias);
|
2024-05-29 14:42:11 +00:00
|
|
|
setState(() => _realm = Realm.fromJson(resp.body));
|
|
|
|
} catch (e) {
|
|
|
|
context.showErrorDialog(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
setState(() => _isBusy = false);
|
|
|
|
}
|
|
|
|
|
2024-05-29 15:22:24 +00:00
|
|
|
getChannels() async {
|
|
|
|
setState(() => _isBusy = true);
|
|
|
|
|
2024-09-18 05:03:40 +00:00
|
|
|
final ChannelProvider channel = Get.find();
|
|
|
|
final resp = await channel.listChannel(scope: _realm!.alias);
|
|
|
|
final availableResp = await channel.listAvailableChannel(
|
|
|
|
scope: _realm!.alias,
|
|
|
|
);
|
|
|
|
|
|
|
|
final Set<int> channelIdx = {};
|
2024-05-29 15:22:24 +00:00
|
|
|
|
|
|
|
setState(() {
|
|
|
|
_channels.clear();
|
|
|
|
_channels.addAll(
|
|
|
|
resp.body.map((e) => Channel.fromJson(e)).toList().cast<Channel>(),
|
|
|
|
);
|
2024-09-18 05:03:40 +00:00
|
|
|
_channels.addAll(
|
|
|
|
availableResp.body
|
|
|
|
.map((e) => Channel.fromJson(e))
|
|
|
|
.toList()
|
|
|
|
.cast<Channel>(),
|
|
|
|
);
|
|
|
|
_channels.retainWhere((x) => channelIdx.add(x.id));
|
2024-05-29 15:22:24 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
setState(() => _isBusy = false);
|
|
|
|
}
|
|
|
|
|
2024-05-29 14:42:11 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
|
2024-05-29 15:22:24 +00:00
|
|
|
getRealm().then((_) {
|
|
|
|
getChannels();
|
|
|
|
});
|
2024-05-29 14:42:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Material(
|
|
|
|
color: Theme.of(context).colorScheme.surface,
|
|
|
|
child: DefaultTabController(
|
|
|
|
length: 2,
|
2024-06-22 17:52:05 +00:00
|
|
|
child: NestedScrollView(
|
|
|
|
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
|
|
|
return [
|
|
|
|
SliverOverlapAbsorber(
|
|
|
|
handle:
|
|
|
|
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
|
|
|
sliver: SliverAppBar(
|
2024-07-12 14:37:58 +00:00
|
|
|
leading: AppBarLeadingButton.adaptive(context),
|
2024-06-22 17:52:05 +00:00
|
|
|
title: Text(_realm?.name ?? 'loading'.tr),
|
|
|
|
centerTitle: false,
|
|
|
|
actions: [
|
|
|
|
IconButton(
|
|
|
|
icon: const Icon(Icons.more_vert),
|
|
|
|
onPressed: () {
|
|
|
|
AppRouter.instance
|
|
|
|
.pushNamed(
|
|
|
|
'realmDetail',
|
|
|
|
pathParameters: {'alias': widget.alias},
|
|
|
|
extra: _realm,
|
|
|
|
)
|
|
|
|
.then((value) {
|
|
|
|
if (value == false) AppRouter.instance.pop();
|
|
|
|
if (value != null) {
|
|
|
|
final resp =
|
|
|
|
Realm.fromJson(value as Map<String, dynamic>);
|
|
|
|
getRealm(overrideAlias: resp.alias);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
2024-05-29 14:42:11 +00:00
|
|
|
),
|
2024-06-22 17:52:05 +00:00
|
|
|
SizedBox(
|
2024-09-13 12:22:10 +00:00
|
|
|
width: AppTheme.isLargeScreen(context) ? 8 : 16,
|
2024-05-29 15:22:24 +00:00
|
|
|
),
|
2024-05-29 14:42:11 +00:00
|
|
|
],
|
2024-06-22 17:52:05 +00:00
|
|
|
bottom: const TabBar(
|
|
|
|
isScrollable: true,
|
|
|
|
tabs: [
|
|
|
|
Tab(icon: Icon(Icons.feed)),
|
|
|
|
Tab(icon: Icon(Icons.chat)),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
];
|
|
|
|
},
|
|
|
|
body: Builder(
|
|
|
|
builder: (context) {
|
|
|
|
if (_isBusy) {
|
|
|
|
return const Center(child: CircularProgressIndicator());
|
|
|
|
}
|
|
|
|
|
|
|
|
return TabBarView(
|
2024-08-01 17:00:31 +00:00
|
|
|
physics: const NeverScrollableScrollPhysics(),
|
2024-06-22 17:52:05 +00:00
|
|
|
children: [
|
|
|
|
RealmPostListWidget(realm: _realm!),
|
|
|
|
RealmChannelListWidget(
|
|
|
|
realm: _realm!,
|
|
|
|
channels: _channels,
|
|
|
|
onRefresh: () => getChannels(),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
},
|
2024-05-29 14:42:11 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RealmPostListWidget extends StatefulWidget {
|
|
|
|
final Realm realm;
|
|
|
|
|
|
|
|
const RealmPostListWidget({super.key, required this.realm});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<RealmPostListWidget> createState() => _RealmPostListWidgetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _RealmPostListWidgetState extends State<RealmPostListWidget> {
|
|
|
|
final PagingController<int, Post> _pagingController =
|
|
|
|
PagingController(firstPageKey: 0);
|
|
|
|
|
|
|
|
getPosts(int pageKey) async {
|
2024-07-23 10:09:41 +00:00
|
|
|
final PostProvider provider = Get.find();
|
2024-05-29 14:42:11 +00:00
|
|
|
|
|
|
|
Response resp;
|
|
|
|
try {
|
2024-08-18 16:14:09 +00:00
|
|
|
resp = await provider.listPost(pageKey, realm: widget.realm.alias);
|
2024-05-29 14:42:11 +00:00
|
|
|
} catch (e) {
|
|
|
|
_pagingController.error = e;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
final PaginationResult result = PaginationResult.fromJson(resp.body);
|
|
|
|
final parsed = result.data?.map((e) => Post.fromJson(e)).toList();
|
|
|
|
if (parsed != null && parsed.length >= 10) {
|
|
|
|
_pagingController.appendPage(parsed, pageKey + parsed.length);
|
|
|
|
} else if (parsed != null) {
|
|
|
|
_pagingController.appendLastPage(parsed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
_pagingController.addPageRequestListener(getPosts);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return RefreshIndicator(
|
|
|
|
onRefresh: () => Future.sync(() => _pagingController.refresh()),
|
|
|
|
child: CustomScrollView(
|
|
|
|
slivers: [
|
|
|
|
PostListWidget(controller: _pagingController),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2024-05-29 15:22:24 +00:00
|
|
|
|
|
|
|
class RealmChannelListWidget extends StatelessWidget {
|
|
|
|
final Realm realm;
|
|
|
|
final List<Channel> channels;
|
|
|
|
final Future Function() onRefresh;
|
|
|
|
|
|
|
|
const RealmChannelListWidget({
|
|
|
|
super.key,
|
|
|
|
required this.realm,
|
|
|
|
required this.channels,
|
|
|
|
required this.onRefresh,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final AuthProvider auth = Get.find();
|
|
|
|
|
2024-07-24 17:18:47 +00:00
|
|
|
return RefreshIndicator(
|
|
|
|
onRefresh: onRefresh,
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
ListTile(
|
|
|
|
leading: const Icon(Icons.add_box),
|
|
|
|
contentPadding: const EdgeInsets.only(left: 32, right: 8),
|
|
|
|
tileColor: Theme.of(context).colorScheme.surfaceContainer,
|
|
|
|
title: Text('channelNew'.tr),
|
|
|
|
subtitle: Text(
|
|
|
|
'channelNewInRealmHint'.trParams({'realm': '#${realm.alias}'}),
|
|
|
|
),
|
|
|
|
onTap: () {
|
|
|
|
AppRouter.instance
|
|
|
|
.pushNamed(
|
|
|
|
'channelOrganizing',
|
|
|
|
extra: ChannelOrganizeArguments(realm: realm),
|
2024-05-29 15:22:24 +00:00
|
|
|
)
|
2024-07-24 17:18:47 +00:00
|
|
|
.then((value) {
|
|
|
|
if (value != null) onRefresh();
|
|
|
|
});
|
|
|
|
},
|
2024-05-29 15:22:24 +00:00
|
|
|
),
|
2024-07-24 17:18:47 +00:00
|
|
|
Expanded(
|
|
|
|
child: ChannelListWidget(
|
|
|
|
channels: channels,
|
|
|
|
selfId: auth.userProfile.value!['id'],
|
|
|
|
noCategory: true,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
2024-05-29 15:22:24 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|