Add barebones session list viewer
This commit is contained in:
parent
a7f39c1d06
commit
e8ea2c9797
4 changed files with 330 additions and 159 deletions
|
@ -10,4 +10,11 @@ class SessionApi {
|
||||||
final body = jsonDecode(response.body);
|
final body = jsonDecode(response.body);
|
||||||
return Session.fromMap(body);
|
return Session.fromMap(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<List<Session>> getSessions(ApiClient client) async {
|
||||||
|
final response = await client.get("/sessions");
|
||||||
|
client.checkResponse(response);
|
||||||
|
final body = jsonDecode(response.body) as List;
|
||||||
|
return body.map((e) => Session.fromMap(e)).toList();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -9,13 +9,13 @@ import 'package:contacts_plus_plus/widgets/default_error_widget.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/friends/expanding_input_fab.dart';
|
import 'package:contacts_plus_plus/widgets/friends/expanding_input_fab.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/friends/friend_list_tile.dart';
|
import 'package:contacts_plus_plus/widgets/friends/friend_list_tile.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/my_profile_dialog.dart';
|
import 'package:contacts_plus_plus/widgets/my_profile_dialog.dart';
|
||||||
|
import 'package:contacts_plus_plus/widgets/session_list.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/settings_page.dart';
|
import 'package:contacts_plus_plus/widgets/settings_page.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/friends/user_search.dart';
|
import 'package:contacts_plus_plus/widgets/friends/user_search.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
|
||||||
class MenuItemDefinition {
|
class MenuItemDefinition {
|
||||||
final String name;
|
final String name;
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
|
@ -51,9 +51,8 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
_userStatusFuture = UserApi.getUserStatus(apiClient, userId: apiClient.userId).then((value) async {
|
_userStatusFuture = UserApi.getUserStatus(apiClient, userId: apiClient.userId).then((value) async {
|
||||||
if (value.onlineStatus == OnlineStatus.offline) {
|
if (value.onlineStatus == OnlineStatus.offline) {
|
||||||
final newStatus = value.copyWith(
|
final newStatus = value.copyWith(
|
||||||
onlineStatus: OnlineStatus.values[_clientHolder!.settingsClient.currentSettings.lastOnlineStatus
|
onlineStatus:
|
||||||
.valueOrDefault]
|
OnlineStatus.values[_clientHolder!.settingsClient.currentSettings.lastOnlineStatus.valueOrDefault]);
|
||||||
);
|
|
||||||
await UserApi.setStatus(apiClient, status: newStatus);
|
await UserApi.setStatus(apiClient, status: newStatus);
|
||||||
return newStatus;
|
return newStatus;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +77,11 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
child: Icon(Icons.circle, size: 16, color: userStatus.onlineStatus.color(context),),
|
child: Icon(
|
||||||
|
Icons.circle,
|
||||||
|
size: 16,
|
||||||
|
color: userStatus.onlineStatus.color(context),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Text(toBeginningOfSentenceCase(userStatus.onlineStatus.name) ?? "Unknown"),
|
Text(toBeginningOfSentenceCase(userStatus.onlineStatus.name) ?? "Unknown"),
|
||||||
],
|
],
|
||||||
|
@ -89,50 +92,50 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
setState(() {
|
setState(() {
|
||||||
_userStatusFuture = Future.value(newStatus.copyWith(lastStatusChange: DateTime.now()));
|
_userStatusFuture = Future.value(newStatus.copyWith(lastStatusChange: DateTime.now()));
|
||||||
});
|
});
|
||||||
final settingsClient = ClientHolder
|
final settingsClient = ClientHolder.of(context).settingsClient;
|
||||||
.of(context)
|
|
||||||
.settingsClient;
|
|
||||||
await UserApi.setStatus(clientHolder.apiClient, status: newStatus);
|
await UserApi.setStatus(clientHolder.apiClient, status: newStatus);
|
||||||
await settingsClient.changeSettings(
|
await settingsClient.changeSettings(
|
||||||
settingsClient.currentSettings.copyWith(lastOnlineStatus: onlineStatus.index));
|
settingsClient.currentSettings.copyWith(lastOnlineStatus: onlineStatus.index));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
FlutterError.reportError(FlutterErrorDetails(exception: e, stack: s));
|
FlutterError.reportError(FlutterErrorDetails(exception: e, stack: s));
|
||||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text(
|
ScaffoldMessenger.of(context)
|
||||||
"Failed to set online-status.")));
|
.showSnackBar(const SnackBar(content: Text("Failed to set online-status.")));
|
||||||
setState(() {
|
setState(() {
|
||||||
_userStatusFuture = Future.value(userStatus);
|
_userStatusFuture = Future.value(userStatus);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) =>
|
itemBuilder: (BuildContext context) => OnlineStatus.values
|
||||||
OnlineStatus.values.where((element) =>
|
.where((element) => element == OnlineStatus.online || element == OnlineStatus.invisible)
|
||||||
element == OnlineStatus.online
|
.map(
|
||||||
|| element == OnlineStatus.invisible).map((item) =>
|
(item) => PopupMenuItem<OnlineStatus>(
|
||||||
PopupMenuItem<OnlineStatus>(
|
value: item,
|
||||||
value: item,
|
child: Row(
|
||||||
child: Row(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
children: [
|
||||||
children: [
|
Icon(
|
||||||
Icon(Icons.circle, size: 16, color: item.color(context),),
|
Icons.circle,
|
||||||
const SizedBox(width: 8,),
|
size: 16,
|
||||||
Text(toBeginningOfSentenceCase(item.name)!),
|
color: item.color(context),
|
||||||
],
|
),
|
||||||
),
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text(toBeginningOfSentenceCase(item.name)!),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
).toList());
|
),
|
||||||
|
)
|
||||||
|
.toList());
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
return TextButton.icon(
|
return TextButton.icon(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: Theme
|
foregroundColor: Theme.of(context).colorScheme.onSurface,
|
||||||
.of(context)
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2)),
|
||||||
.colorScheme
|
|
||||||
.onSurface,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2)
|
|
||||||
),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_userStatusFuture = UserApi.getUserStatus(clientHolder.apiClient, userId: clientHolder.apiClient
|
_userStatusFuture =
|
||||||
.userId);
|
UserApi.getUserStatus(clientHolder.apiClient, userId: clientHolder.apiClient.userId);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.warning),
|
icon: const Icon(Icons.warning),
|
||||||
|
@ -141,10 +144,7 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
} else {
|
} else {
|
||||||
return TextButton.icon(
|
return TextButton.icon(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
disabledForegroundColor: Theme
|
disabledForegroundColor: Theme.of(context).colorScheme.onSurface,
|
||||||
.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onSurface,
|
|
||||||
),
|
),
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
icon: Container(
|
icon: Container(
|
||||||
|
@ -153,17 +153,13 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
margin: const EdgeInsets.only(right: 4),
|
margin: const EdgeInsets.only(right: 4),
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
color: Theme
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onSurface,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
label: const Text("Loading"),
|
label: const Text("Loading"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 4, right: 4),
|
padding: const EdgeInsets.only(left: 4, right: 4),
|
||||||
child: PopupMenuButton<MenuItemDefinition>(
|
child: PopupMenuButton<MenuItemDefinition>(
|
||||||
|
@ -171,55 +167,63 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
onSelected: (MenuItemDefinition itemDef) async {
|
onSelected: (MenuItemDefinition itemDef) async {
|
||||||
await itemDef.onTap();
|
await itemDef.onTap();
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) =>
|
itemBuilder: (BuildContext context) => [
|
||||||
[
|
MenuItemDefinition(
|
||||||
MenuItemDefinition(
|
name: "Settings",
|
||||||
name: "Settings",
|
icon: Icons.settings,
|
||||||
icon: Icons.settings,
|
onTap: () async {
|
||||||
onTap: () async {
|
await Navigator.of(context).push(MaterialPageRoute(builder: (context) => const SettingsPage()));
|
||||||
await Navigator.of(context).push(MaterialPageRoute(builder: (context) => const SettingsPage()));
|
},
|
||||||
},
|
),
|
||||||
),
|
MenuItemDefinition(
|
||||||
MenuItemDefinition(
|
name: "Find Users",
|
||||||
name: "Find Users",
|
icon: Icons.person_add,
|
||||||
icon: Icons.person_add,
|
onTap: () async {
|
||||||
onTap: () async {
|
final mClient = Provider.of<MessagingClient>(context, listen: false);
|
||||||
final mClient = Provider.of<MessagingClient>(context, listen: false);
|
await Navigator.of(context).push(
|
||||||
await Navigator.of(context).push(
|
MaterialPageRoute(
|
||||||
MaterialPageRoute(
|
builder: (context) => ChangeNotifierProvider<MessagingClient>.value(
|
||||||
builder: (context) =>
|
value: mClient,
|
||||||
ChangeNotifierProvider<MessagingClient>.value(
|
child: const UserSearch(),
|
||||||
value: mClient,
|
|
||||||
child: const UserSearch(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
MenuItemDefinition(
|
|
||||||
name: "My Profile",
|
|
||||||
icon: Icons.person,
|
|
||||||
onTap: () async {
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return const MyProfileDialog();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
].map((item) =>
|
|
||||||
PopupMenuItem<MenuItemDefinition>(
|
|
||||||
value: item,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(item.name),
|
|
||||||
Icon(item.icon),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).toList(),
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItemDefinition(
|
||||||
|
name: "My Profile",
|
||||||
|
icon: Icons.person,
|
||||||
|
onTap: () async {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return const MyProfileDialog();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItemDefinition(
|
||||||
|
name: "Sessions",
|
||||||
|
icon: Icons.location_city,
|
||||||
|
onTap: () async {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(builder: (context) => const SessionList()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
].map(
|
||||||
|
(item) => PopupMenuItem<MenuItemDefinition>(
|
||||||
|
value: item,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(item.name),
|
||||||
|
Icon(item.icon),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -227,46 +231,45 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
body: Stack(
|
body: Stack(
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
children: [
|
children: [
|
||||||
Consumer<MessagingClient>(
|
Consumer<MessagingClient>(builder: (context, mClient, _) {
|
||||||
builder: (context, mClient, _) {
|
if (mClient.initStatus == null) {
|
||||||
if (mClient.initStatus == null) {
|
return const LinearProgressIndicator();
|
||||||
return const LinearProgressIndicator();
|
} else if (mClient.initStatus!.isNotEmpty) {
|
||||||
} else if (mClient.initStatus!.isNotEmpty) {
|
return Column(
|
||||||
return Column(
|
children: [
|
||||||
children: [
|
Expanded(
|
||||||
Expanded(
|
child: DefaultErrorWidget(
|
||||||
child: DefaultErrorWidget(
|
message: mClient.initStatus,
|
||||||
message: mClient.initStatus,
|
onRetry: () async {
|
||||||
onRetry: () async {
|
mClient.resetInitStatus();
|
||||||
mClient.resetInitStatus();
|
mClient.refreshFriendsListWithErrorHandler();
|
||||||
mClient.refreshFriendsListWithErrorHandler();
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
);
|
||||||
);
|
} else {
|
||||||
} else {
|
var friends = List.from(mClient.cachedFriends); // Explicit copy.
|
||||||
var friends = List.from(mClient.cachedFriends); // Explicit copy.
|
if (_searchFilter.isNotEmpty) {
|
||||||
if (_searchFilter.isNotEmpty) {
|
friends = friends
|
||||||
friends = friends.where((element) =>
|
.where((element) => element.username.toLowerCase().contains(_searchFilter.toLowerCase()))
|
||||||
element.username.toLowerCase().contains(_searchFilter.toLowerCase())).toList();
|
.toList();
|
||||||
friends.sort((a, b) => a.username.length.compareTo(b.username.length));
|
friends.sort((a, b) => a.username.length.compareTo(b.username.length));
|
||||||
}
|
|
||||||
return ListView.builder(
|
|
||||||
physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast),
|
|
||||||
itemCount: friends.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final friend = friends[index];
|
|
||||||
final unreads = mClient.getUnreadsForFriend(friend);
|
|
||||||
return FriendListTile(
|
|
||||||
friend: friend,
|
|
||||||
unreads: unreads.length,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
),
|
return ListView.builder(
|
||||||
|
physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast),
|
||||||
|
itemCount: friends.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final friend = friends[index];
|
||||||
|
final unreads = mClient.getUnreadsForFriend(friend);
|
||||||
|
return FriendListTile(
|
||||||
|
friend: friend,
|
||||||
|
unreads: unreads.length,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: ExpandingInputFab(
|
child: ExpandingInputFab(
|
||||||
|
@ -288,4 +291,4 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
159
lib/widgets/session_list.dart
Normal file
159
lib/widgets/session_list.dart
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:contacts_plus_plus/apis/session_api.dart';
|
||||||
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
|
import 'package:contacts_plus_plus/client_holder.dart';
|
||||||
|
import 'package:contacts_plus_plus/models/session.dart';
|
||||||
|
import 'package:contacts_plus_plus/widgets/formatted_text.dart';
|
||||||
|
import 'package:contacts_plus_plus/widgets/session_view.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SessionList extends StatefulWidget {
|
||||||
|
const SessionList({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SessionList> createState() => _SessionListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SessionListState extends State<SessionList> {
|
||||||
|
Future<List<Session>>? _sessionsFuture;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
_sessionsFuture = SessionApi.getSessions(ClientHolder.of(context).apiClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
|
title: const Text("Sessions"),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: const Size.fromHeight(1),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 1,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: FutureBuilder<List<Session>>(
|
||||||
|
future: _sessionsFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final data = snapshot.data ?? [];
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
|
child: GridView.builder(
|
||||||
|
padding: const EdgeInsets.only(top: 10),
|
||||||
|
physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast),
|
||||||
|
itemCount: data.length,
|
||||||
|
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
maxCrossAxisExtent: 256,
|
||||||
|
crossAxisSpacing: 4,
|
||||||
|
mainAxisSpacing: 4,
|
||||||
|
childAspectRatio: .8,
|
||||||
|
),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final session = data[index];
|
||||||
|
return Card(
|
||||||
|
elevation: 0,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
side: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context)
|
||||||
|
.push(MaterialPageRoute(builder: (context) => SessionView(session: session)));
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 5,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
child: Hero(
|
||||||
|
tag: session.id,
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: Aux.neosDbToHttp(session.thumbnail),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorWidget: (context, url, error) => const Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.broken_image,
|
||||||
|
size: 64,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
placeholder: (context, uri) => const Center(child: CircularProgressIndicator()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: FormattedText(
|
||||||
|
session.formattedName,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"${session.sessionUsers.length.toString().padLeft(2, "0")}/${session.maxUsers.toString().padLeft(2, "0")} Online",
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) const LinearProgressIndicator()
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,36 +52,38 @@ class SessionView extends StatelessWidget {
|
||||||
),
|
),
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
collapseMode: CollapseMode.pin,
|
collapseMode: CollapseMode.pin,
|
||||||
background: CachedNetworkImage(
|
background: Hero(
|
||||||
imageUrl: Aux.neosDbToHttp(session.thumbnail),
|
tag: session.id,
|
||||||
imageBuilder: (context, image) {
|
child: CachedNetworkImage(
|
||||||
return InkWell(
|
imageUrl: Aux.neosDbToHttp(session.thumbnail),
|
||||||
onTap: () async {
|
imageBuilder: (context, image) {
|
||||||
await Navigator.push(
|
return Material(
|
||||||
context,
|
child: InkWell(
|
||||||
MaterialPageRoute(
|
onTap: () async {
|
||||||
builder: (context) => PhotoView(
|
await Navigator.push(
|
||||||
minScale: PhotoViewComputedScale.contained,
|
context,
|
||||||
imageProvider: image,
|
MaterialPageRoute(
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: session.id),
|
builder: (context) => PhotoView(
|
||||||
),
|
minScale: PhotoViewComputedScale.contained,
|
||||||
|
imageProvider: image,
|
||||||
|
heroAttributes: PhotoViewHeroAttributes(tag: session.id),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Image(
|
||||||
|
image: image,
|
||||||
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Hero(
|
|
||||||
tag: session.id,
|
|
||||||
child: Image(
|
|
||||||
image: image,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
errorWidget: (context, url, error) => const Icon(
|
||||||
errorWidget: (context, url, error) => const Icon(
|
Icons.broken_image,
|
||||||
Icons.broken_image,
|
size: 64,
|
||||||
size: 64,
|
),
|
||||||
|
placeholder: (context, uri) => const Center(child: CircularProgressIndicator()),
|
||||||
),
|
),
|
||||||
placeholder: (context, uri) => const Center(child: CircularProgressIndicator()),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -162,7 +164,7 @@ class SessionView extends StatelessWidget {
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
final user = session.sessionUsers[index % session.sessionUsers.length];
|
final user = session.sessionUsers[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(
|
title: Text(
|
||||||
|
@ -175,7 +177,7 @@ class SessionView extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: session.sessionUsers.length * 4,
|
childCount: session.sessionUsers.length,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in a new issue