✨ Existing friends management
This commit is contained in:
parent
0230ea5c79
commit
5346224f1e
@ -10,6 +10,7 @@
|
|||||||
"signInRiskDetected": "Risk detected, click Next to open a webpage and signin through it to pass security check.",
|
"signInRiskDetected": "Risk detected, click Next to open a webpage and signin through it to pass security check.",
|
||||||
"signUp": "Sign Up",
|
"signUp": "Sign Up",
|
||||||
"signUpCaption": "Create an account on Solarpass and then get the access of entire Solar Networks!",
|
"signUpCaption": "Create an account on Solarpass and then get the access of entire Solar Networks!",
|
||||||
|
"signOut": "Sign Out",
|
||||||
"poweredBy": "Powered by Project Hydrogen",
|
"poweredBy": "Powered by Project Hydrogen",
|
||||||
"copyright": "Copyright © 2024 Solsynth LLC",
|
"copyright": "Copyright © 2024 Solsynth LLC",
|
||||||
"confirmation": "Confirmation",
|
"confirmation": "Confirmation",
|
||||||
@ -30,6 +31,11 @@
|
|||||||
"notifyDone": "You're done!",
|
"notifyDone": "You're done!",
|
||||||
"notifyDoneCaption": "There are no notifications unread for you.",
|
"notifyDoneCaption": "There are no notifications unread for you.",
|
||||||
"notifyListHint": "Pull to refresh, swipe to dismiss",
|
"notifyListHint": "Pull to refresh, swipe to dismiss",
|
||||||
|
"friend": "Friend",
|
||||||
|
"friendPending": "Pending",
|
||||||
|
"friendActive": "Active",
|
||||||
|
"friendBlocked": "Blocked",
|
||||||
|
"friendListHint": "Swipe left to decline, swipe right to approve",
|
||||||
"reaction": "Reaction",
|
"reaction": "Reaction",
|
||||||
"reactVerb": "React",
|
"reactVerb": "React",
|
||||||
"post": "Post",
|
"post": "Post",
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
"chat": "聊天",
|
"chat": "聊天",
|
||||||
"account": "账号",
|
"account": "账号",
|
||||||
"riskDetection": "风险监测",
|
"riskDetection": "风险监测",
|
||||||
"signIn": "登陆",
|
"signIn": "登录",
|
||||||
"signInCaption": "登陆以发表帖子、文章、创建领域、和你的朋友聊天,以及获取更多功能!",
|
"signInCaption": "登录以发表帖子、文章、创建领域、和你的朋友聊天,以及获取更多功能!",
|
||||||
"signInRequired": "请先登陆",
|
"signInRequired": "请先登录",
|
||||||
"signInRiskDetected": "检测到风险,点击下一步按钮来打开一个网页,并通过在其上面登陆来通过安全检查。",
|
"signInRiskDetected": "检测到风险,点击下一步按钮来打开一个网页,并通过在其上面登录来通过安全检查。",
|
||||||
"signUp": "注册",
|
"signUp": "注册",
|
||||||
"signUpCaption": "在 Solarpass 注册一个账号以获得整个 Solar Networks 的存取权!",
|
"signUpCaption": "在 Solarpass 注册一个账号以获得整个 Solar Networks 的存取权!",
|
||||||
|
"signOut": "登出",
|
||||||
"poweredBy": "由 Project Hydrogen 强力驱动",
|
"poweredBy": "由 Project Hydrogen 强力驱动",
|
||||||
"copyright": "2024 Solsynth LLC © 版权所有",
|
"copyright": "2024 Solsynth LLC © 版权所有",
|
||||||
"confirmation": "确认",
|
"confirmation": "确认",
|
||||||
@ -30,6 +31,11 @@
|
|||||||
"notifyDone": "所有通知已读!",
|
"notifyDone": "所有通知已读!",
|
||||||
"notifyDoneCaption": "这里没有什么东西可以给你看的了~",
|
"notifyDoneCaption": "这里没有什么东西可以给你看的了~",
|
||||||
"notifyListHint": "下拉以刷新,左滑来已读",
|
"notifyListHint": "下拉以刷新,左滑来已读",
|
||||||
|
"friend": "好友",
|
||||||
|
"friendPending": "请求中",
|
||||||
|
"friendActive": "活跃的好友",
|
||||||
|
"friendBlocked": "封锁中",
|
||||||
|
"friendListHint": "左滑来拒绝,右滑来接受",
|
||||||
"reaction": "反应",
|
"reaction": "反应",
|
||||||
"reactVerb": "作出反应",
|
"reactVerb": "作出反应",
|
||||||
"post": "帖子",
|
"post": "帖子",
|
||||||
|
@ -8,9 +8,9 @@ class Account {
|
|||||||
String avatar;
|
String avatar;
|
||||||
String banner;
|
String banner;
|
||||||
String description;
|
String description;
|
||||||
String emailAddress;
|
String? emailAddress;
|
||||||
int powerLevel;
|
int powerLevel;
|
||||||
int externalId;
|
int? externalId;
|
||||||
|
|
||||||
Account({
|
Account({
|
||||||
required this.id,
|
required this.id,
|
||||||
@ -22,9 +22,9 @@ class Account {
|
|||||||
required this.avatar,
|
required this.avatar,
|
||||||
required this.banner,
|
required this.banner,
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.emailAddress,
|
this.emailAddress,
|
||||||
required this.powerLevel,
|
required this.powerLevel,
|
||||||
required this.externalId,
|
this.externalId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Account.fromJson(Map<String, dynamic> json) => Account(
|
factory Account.fromJson(Map<String, dynamic> json) => Account(
|
||||||
|
53
lib/models/friendship.dart
Normal file
53
lib/models/friendship.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:solian/models/account.dart';
|
||||||
|
|
||||||
|
class Friendship {
|
||||||
|
int id;
|
||||||
|
DateTime createdAt;
|
||||||
|
DateTime updatedAt;
|
||||||
|
DateTime? deletedAt;
|
||||||
|
int accountId;
|
||||||
|
int relatedId;
|
||||||
|
int? blockedBy;
|
||||||
|
Account account;
|
||||||
|
Account related;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
Friendship({
|
||||||
|
required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
this.deletedAt,
|
||||||
|
required this.accountId,
|
||||||
|
required this.relatedId,
|
||||||
|
this.blockedBy,
|
||||||
|
required this.account,
|
||||||
|
required this.related,
|
||||||
|
required this.status,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Friendship.fromJson(Map<String, dynamic> json) => Friendship(
|
||||||
|
id: json["id"],
|
||||||
|
createdAt: DateTime.parse(json["created_at"]),
|
||||||
|
updatedAt: DateTime.parse(json["updated_at"]),
|
||||||
|
deletedAt: json["deleted_at"],
|
||||||
|
accountId: json["account_id"],
|
||||||
|
relatedId: json["related_id"],
|
||||||
|
blockedBy: json["blocked_by"],
|
||||||
|
account: Account.fromJson(json["account"]),
|
||||||
|
related: Account.fromJson(json["related"]),
|
||||||
|
status: json["status"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"id": id,
|
||||||
|
"created_at": createdAt.toIso8601String(),
|
||||||
|
"updated_at": updatedAt.toIso8601String(),
|
||||||
|
"deleted_at": deletedAt,
|
||||||
|
"account_id": accountId,
|
||||||
|
"related_id": relatedId,
|
||||||
|
"blocked_by": blockedBy,
|
||||||
|
"account": account.toJson(),
|
||||||
|
"related": related.toJson(),
|
||||||
|
"status": status,
|
||||||
|
};
|
||||||
|
}
|
@ -50,6 +50,11 @@ class NotifyProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clearAt(int index) {
|
||||||
|
notifications.removeAt(index);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
void clearNonRealtime() {
|
void clearNonRealtime() {
|
||||||
notifications = notifications.where((x) => !x.isRealtime).toList();
|
notifications = notifications.where((x) => !x.isRealtime).toList();
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:solian/models/channel.dart';
|
import 'package:solian/models/channel.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
import 'package:solian/screens/account.dart';
|
import 'package:solian/screens/account.dart';
|
||||||
|
import 'package:solian/screens/account/friend.dart';
|
||||||
import 'package:solian/screens/chat/chat.dart';
|
import 'package:solian/screens/chat/chat.dart';
|
||||||
import 'package:solian/screens/chat/index.dart';
|
import 'package:solian/screens/chat/index.dart';
|
||||||
import 'package:solian/screens/chat/manage.dart';
|
import 'package:solian/screens/chat/manage.dart';
|
||||||
@ -20,6 +21,11 @@ final router = GoRouter(
|
|||||||
name: 'explore',
|
name: 'explore',
|
||||||
builder: (context, state) => const ExploreScreen(),
|
builder: (context, state) => const ExploreScreen(),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/notification',
|
||||||
|
name: 'notification',
|
||||||
|
builder: (context, state) => const NotificationScreen(),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/chat',
|
path: '/chat',
|
||||||
name: 'chat',
|
name: 'chat',
|
||||||
@ -66,15 +72,15 @@ final router = GoRouter(
|
|||||||
dataset: state.pathParameters['dataset'] as String,
|
dataset: state.pathParameters['dataset'] as String,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
|
||||||
path: '/notification',
|
|
||||||
name: 'notification',
|
|
||||||
builder: (context, state) => const NotificationScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/auth/sign-in',
|
path: '/auth/sign-in',
|
||||||
name: 'auth.sign-in',
|
name: 'auth.sign-in',
|
||||||
builder: (context, state) => SignInScreen(),
|
builder: (context, state) => SignInScreen(),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/account/friend',
|
||||||
|
name: 'account.friend',
|
||||||
|
builder: (context, state) => const FriendScreen(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.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/widgets/common_wrapper.dart';
|
import 'package:solian/widgets/common_wrapper.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class AccountScreen extends StatefulWidget {
|
class AccountScreen extends StatefulWidget {
|
||||||
const AccountScreen({super.key});
|
const AccountScreen({super.key});
|
||||||
@ -40,21 +40,25 @@ class _AccountScreenState extends State<AccountScreen> {
|
|||||||
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 24),
|
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 24),
|
||||||
child: NameCard(),
|
child: NameCard(),
|
||||||
),
|
),
|
||||||
InkWell(
|
ListTile(
|
||||||
child: const Padding(
|
contentPadding: const EdgeInsets.symmetric(horizontal: 34),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 18),
|
leading: const Icon(Icons.diversity_1),
|
||||||
child: ListTile(
|
title: Text(AppLocalizations.of(context)!.friend),
|
||||||
leading: Icon(Icons.logout),
|
onTap: () {
|
||||||
title: Text("Sign out"),
|
router.goNamed('account.friend');
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 34),
|
||||||
|
leading: const Icon(Icons.logout),
|
||||||
|
title: Text(AppLocalizations.of(context)!.signOut),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
auth.signoff();
|
auth.signoff();
|
||||||
setState(() {
|
setState(() {
|
||||||
isAuthorized = false;
|
isAuthorized = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: Center(
|
: Center(
|
||||||
@ -126,8 +130,7 @@ class NameCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: renderAvatar(context),
|
future: renderAvatar(context),
|
||||||
builder:
|
builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
|
||||||
(BuildContext context, AsyncSnapshot<Widget> snapshot) {
|
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return snapshot.data!;
|
return snapshot.data!;
|
||||||
} else {
|
} else {
|
||||||
@ -138,8 +141,7 @@ class NameCard extends StatelessWidget {
|
|||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: renderLabel(context),
|
future: renderLabel(context),
|
||||||
builder:
|
builder: (BuildContext context, AsyncSnapshot<Column> snapshot) {
|
||||||
(BuildContext context, AsyncSnapshot<Column> snapshot) {
|
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return snapshot.data!;
|
return snapshot.data!;
|
||||||
} else {
|
} else {
|
||||||
@ -161,12 +163,7 @@ class ActionCard extends StatelessWidget {
|
|||||||
final String caption;
|
final String caption;
|
||||||
final Function onTap;
|
final Function onTap;
|
||||||
|
|
||||||
const ActionCard(
|
const ActionCard({super.key, required this.onTap, required this.title, required this.caption, required this.icon});
|
||||||
{super.key,
|
|
||||||
required this.onTap,
|
|
||||||
required this.title,
|
|
||||||
required this.caption,
|
|
||||||
required this.icon});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
218
lib/screens/account/friend.dart
Normal file
218
lib/screens/account/friend.dart
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:solian/models/account.dart';
|
||||||
|
import 'package:solian/models/friendship.dart';
|
||||||
|
import 'package:solian/providers/auth.dart';
|
||||||
|
import 'package:solian/utils/service_url.dart';
|
||||||
|
import 'package:solian/widgets/indent_wrapper.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class FriendScreen extends StatefulWidget {
|
||||||
|
const FriendScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FriendScreen> createState() => _FriendScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FriendScreenState extends State<FriendScreen> {
|
||||||
|
bool _isSubmitting = false;
|
||||||
|
|
||||||
|
int _currentSideId = 0;
|
||||||
|
List<Friendship> _friendships = List.empty();
|
||||||
|
|
||||||
|
Future<void> fetchFriendships() async {
|
||||||
|
final auth = context.read<AuthProvider>();
|
||||||
|
final prof = await auth.getProfiles();
|
||||||
|
if (!await auth.isAuthorized()) return;
|
||||||
|
|
||||||
|
_currentSideId = prof['id'];
|
||||||
|
|
||||||
|
var uri = getRequestUri('passport', '/api/users/me/friends');
|
||||||
|
|
||||||
|
var res = await auth.client!.get(uri);
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
final result = jsonDecode(utf8.decode(res.bodyBytes)) as List<dynamic>;
|
||||||
|
setState(() {
|
||||||
|
_friendships = result.map((x) => Friendship.fromJson(x)).toList();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var message = utf8.decode(res.bodyBytes);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Something went wrong... $message")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateFriendship(Friendship relation, int status) async {
|
||||||
|
setState(() => _isSubmitting = true);
|
||||||
|
|
||||||
|
final otherside = getOtherside(relation);
|
||||||
|
|
||||||
|
final auth = context.read<AuthProvider>();
|
||||||
|
if (!await auth.isAuthorized()) {
|
||||||
|
setState(() => _isSubmitting = false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = await auth.client!.put(
|
||||||
|
getRequestUri('passport', '/api/users/me/friends/${otherside.id}'),
|
||||||
|
headers: <String, String>{
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: jsonEncode(<String, dynamic>{
|
||||||
|
'status': status,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
await fetchFriendships();
|
||||||
|
} else {
|
||||||
|
var message = utf8.decode(res.bodyBytes);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Something went wrong... $message")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() => _isSubmitting = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Friendship> filterWithStatus(int status) {
|
||||||
|
return _friendships.where((x) => x.status == status).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
DismissDirection getDismissDirection(Friendship relation) {
|
||||||
|
if (relation.status == 2) return DismissDirection.endToStart;
|
||||||
|
if (relation.status == 1) return DismissDirection.startToEnd;
|
||||||
|
if (relation.status == 0 && relation.relatedId != _currentSideId) return DismissDirection.startToEnd;
|
||||||
|
return DismissDirection.horizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
Account getOtherside(Friendship relation) {
|
||||||
|
if (relation.accountId != _currentSideId) {
|
||||||
|
return relation.account;
|
||||||
|
} else {
|
||||||
|
return relation.related;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAvatarUrl(String uuid) {
|
||||||
|
return getRequestUri('passport', '/api/avatar/$uuid').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
Future.delayed(Duration.zero, () {
|
||||||
|
fetchFriendships();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget friendshipTileBuilder(context, index, status) {
|
||||||
|
final element = filterWithStatus(status)[index];
|
||||||
|
final otherside = getOtherside(element);
|
||||||
|
|
||||||
|
final randomId = DateTime.now().microsecondsSinceEpoch >> 10;
|
||||||
|
|
||||||
|
return Dismissible(
|
||||||
|
key: Key(randomId.toString()),
|
||||||
|
background: Container(
|
||||||
|
color: Colors.red,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: const Icon(Icons.close, color: Colors.white),
|
||||||
|
),
|
||||||
|
secondaryBackground: Container(
|
||||||
|
color: Colors.green,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: const Icon(Icons.check, color: Colors.white),
|
||||||
|
),
|
||||||
|
direction: getDismissDirection(element),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(otherside.nick),
|
||||||
|
subtitle: Text(otherside.name),
|
||||||
|
leading: CircleAvatar(
|
||||||
|
backgroundImage: NetworkImage(getAvatarUrl(otherside.avatar)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDismissed: (direction) {
|
||||||
|
if (direction == DismissDirection.startToEnd) {
|
||||||
|
updateFriendship(element, 2);
|
||||||
|
}
|
||||||
|
if (direction == DismissDirection.endToStart) {
|
||||||
|
updateFriendship(element, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IndentWrapper(
|
||||||
|
title: AppLocalizations.of(context)!.friend,
|
||||||
|
child: RefreshIndicator(
|
||||||
|
onRefresh: () => fetchFriendships(),
|
||||||
|
child: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: _isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(),
|
||||||
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12),
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.8),
|
||||||
|
child: Text(AppLocalizations.of(context)!.friendPending),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverList.builder(
|
||||||
|
itemCount: filterWithStatus(0).length,
|
||||||
|
itemBuilder: (_, __) => friendshipTileBuilder(_, __, 0),
|
||||||
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12),
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.8),
|
||||||
|
child: Text(AppLocalizations.of(context)!.friendActive),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverList.builder(
|
||||||
|
itemCount: filterWithStatus(1).length,
|
||||||
|
itemBuilder: (_, __) => friendshipTileBuilder(_, __, 1),
|
||||||
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12),
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.8),
|
||||||
|
child: Text(AppLocalizations.of(context)!.friendBlocked),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverList.builder(
|
||||||
|
itemCount: filterWithStatus(2).length,
|
||||||
|
itemBuilder: (_, __) => friendshipTileBuilder(_, __, 2),
|
||||||
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.8),
|
||||||
|
width: 0.3,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(top: 16),
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.friendListHint,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -83,26 +83,21 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
onRefresh: () => Future.sync(
|
onRefresh: () => Future.sync(
|
||||||
() => _pagingController.refresh(),
|
() => _pagingController.refresh(),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: PagedListView<int, Post>(
|
||||||
child: Container(
|
pagingController: _pagingController,
|
||||||
constraints: const BoxConstraints(maxWidth: 640),
|
builderDelegate: PagedChildBuilderDelegate<Post>(
|
||||||
child: PagedListView<int, Post>(
|
itemBuilder: (context, item, index) => PostItem(
|
||||||
pagingController: _pagingController,
|
item: item,
|
||||||
builderDelegate: PagedChildBuilderDelegate<Post>(
|
onUpdate: () => _pagingController.refresh(),
|
||||||
itemBuilder: (context, item, index) => PostItem(
|
onTap: () {
|
||||||
item: item,
|
router.pushNamed(
|
||||||
onUpdate: () => _pagingController.refresh(),
|
'posts.screen',
|
||||||
onTap: () {
|
pathParameters: {
|
||||||
router.pushNamed(
|
'alias': item.alias,
|
||||||
'posts.screen',
|
'dataset': '${item.modelType}s',
|
||||||
pathParameters: {
|
|
||||||
'alias': item.alias,
|
|
||||||
'dataset': '${item.modelType}s',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -49,7 +49,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
index: index,
|
index: index,
|
||||||
item: element,
|
item: element,
|
||||||
onDismiss: () => setState(() {
|
onDismiss: () => setState(() {
|
||||||
nty.notifications.removeAt(index);
|
nty.clearAt(index);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -23,12 +23,10 @@ class PostScreen extends StatefulWidget {
|
|||||||
class _PostScreenState extends State<PostScreen> {
|
class _PostScreenState extends State<PostScreen> {
|
||||||
final _client = http.Client();
|
final _client = http.Client();
|
||||||
|
|
||||||
final PagingController<int, Post> _commentPagingController =
|
final PagingController<int, Post> _commentPagingController = PagingController(firstPageKey: 0);
|
||||||
PagingController(firstPageKey: 0);
|
|
||||||
|
|
||||||
Future<Post?> fetchPost(BuildContext context) async {
|
Future<Post?> fetchPost(BuildContext context) async {
|
||||||
final uri = getRequestUri(
|
final uri = getRequestUri('interactive', '/api/p/${widget.dataset}/${widget.alias}');
|
||||||
'interactive', '/api/p/${widget.dataset}/${widget.alias}');
|
|
||||||
final res = await _client.get(uri);
|
final res = await _client.get(uri);
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
final err = utf8.decode(res.bodyBytes);
|
final err = utf8.decode(res.bodyBytes);
|
||||||
@ -51,32 +49,27 @@ class _PostScreenState extends State<PostScreen> {
|
|||||||
future: fetchPost(context),
|
future: fetchPost(context),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData && snapshot.data != null) {
|
if (snapshot.hasData && snapshot.data != null) {
|
||||||
return Center(
|
return CustomScrollView(
|
||||||
child: Container(
|
slivers: [
|
||||||
constraints: const BoxConstraints(maxWidth: 640),
|
SliverToBoxAdapter(
|
||||||
child: CustomScrollView(
|
child: PostItem(
|
||||||
slivers: [
|
item: snapshot.data!,
|
||||||
SliverToBoxAdapter(
|
brief: false,
|
||||||
child: PostItem(
|
ripple: false,
|
||||||
item: snapshot.data!,
|
),
|
||||||
brief: false,
|
|
||||||
ripple: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: CommentListHeader(
|
|
||||||
related: snapshot.data!,
|
|
||||||
paging: _commentPagingController,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
CommentList(
|
|
||||||
related: snapshot.data!,
|
|
||||||
dataset: widget.dataset,
|
|
||||||
paging: _commentPagingController,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
SliverToBoxAdapter(
|
||||||
|
child: CommentListHeader(
|
||||||
|
related: snapshot.data!,
|
||||||
|
paging: _commentPagingController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CommentList(
|
||||||
|
related: snapshot.data!,
|
||||||
|
dataset: widget.dataset,
|
||||||
|
paging: _commentPagingController,
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return const Center(
|
return const Center(
|
||||||
|
Loading…
Reference in New Issue
Block a user