follow requests

This commit is contained in:
zoe 2022-09-30 20:06:24 +02:00
parent ee226713ae
commit 481dbfa623
5 changed files with 233 additions and 4 deletions

View File

@ -0,0 +1,58 @@
import 'dart:convert';
import 'package:loris/business_logic/account/account.dart';
import 'package:loris/global.dart' as global;
import 'package:http/http.dart' as http;
Future<List<AccountModel>> _getFollowRequest(String identityName) async {
final identity = global.settings!.identities[identityName]!;
final headers = {...identity.getAuthHeaders(), ...global.defaultHeaders};
final Uri uri = Uri(
scheme: "https",
host: identity.instanceUrl,
path: "/api/v1/follow_requests",
);
final result = await http.get(uri, headers: headers);
if (result.statusCode != 200) return [];
List<AccountModel> models = [];
for (var m in jsonDecode(result.body)) {
models.add(AccountModel.fromJson(m, identityName));
}
return models;
}
Future<List<AccountModel>> getFollowRequests() async {
List<Future<List<AccountModel>>> pending = [];
global.settings!.identities.forEach((key, value) {
pending.add(_getFollowRequest(key));
});
List<AccountModel> finished = [];
for (var m in pending) {
finished.addAll(await m);
}
return finished;
}
// accept follow request or deny
// request is accepted unless deny is true
Future<RelationshipModel?> handleFollowRequest(AccountModel account,
{deny = false}) async {
final identity = global.settings!.identities[account.identity]!;
final headers = {...identity.getAuthHeaders(), ...global.defaultHeaders};
final Uri uri = Uri(
scheme: "https",
host: identity.instanceUrl,
path:
"/api/v1/follow_requests/${account.id}/${deny ? "reject" : "authorize"}",
);
final result = await http.post(uri, headers: headers);
print(uri);
print(result.body);
if (result.statusCode != 200) {
return null;
}
return RelationshipModel.fromJson(jsonDecode(result.body), account.identity);
}

View File

@ -0,0 +1,146 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:localization/localization.dart';
import 'package:loris/business_logic/account/account.dart';
import 'package:loris/business_logic/follow_request/followrequest.dart';
import 'package:loris/global.dart' as global;
import 'package:loris/partials/post.dart';
import 'package:loris/themes/themes.dart' as themes;
class FollowReqScreen extends StatefulWidget {
const FollowReqScreen({super.key});
@override
State<FollowReqScreen> createState() => _FollowReqScreenState();
}
class _FollowReqScreenState extends State<FollowReqScreen> {
List<AccountModel> requests = [];
bool loading = true;
Future<void> fetchRequests() async {
final results = await getFollowRequests();
if (mounted) {
setState(() {
requests = results;
loading = false;
});
}
}
@override
void initState() {
fetchRequests();
super.initState();
}
void handleFailure(RelationshipModel? model) {}
void updateList(AccountModel model) {
if (mounted) {
setState(() {
requests.remove(model);
});
}
}
@override
Widget build(BuildContext context) {
return BackdropFilter(
filter:
ImageFilter.blur(sigmaX: 10, sigmaY: 10, tileMode: TileMode.mirror),
child: SimpleDialog(
titlePadding: EdgeInsets.fromLTRB(0, themes.defaultRadius.x, 0, 0),
title: loading ? const LinearProgressIndicator() : null,
contentPadding: const EdgeInsets.all(0),
children: [
Container(
width: global.getWidth(context),
height: MediaQuery.of(context).size.height * 2 / 3,
constraints: global.getConstraints(context),
child: ListView.separated(
itemBuilder: (context, index) {
final account = requests[index];
return FollowRequestDisplay(
account: account,
accept: () async {
if (mounted) {
setState(() {
loading = true;
});
}
final result = await handleFollowRequest(account);
handleFailure(result);
if (result != null) {
updateList(account);
}
if (mounted) {
setState(() {
loading = false;
});
}
},
deny: (() async {
if (mounted) {
setState(() {
loading = true;
});
}
final result =
await handleFollowRequest(account, deny: true);
handleFailure(result);
if (result != null) {
updateList(account);
}
if (mounted) {
setState(() {
loading = false;
});
}
}),
);
},
separatorBuilder: (context, index) =>
Divider(color: Theme.of(context).hintColor),
itemCount: requests.length),
)
],
));
}
}
class FollowRequestDisplay extends StatelessWidget {
const FollowRequestDisplay(
{super.key,
required this.deny,
required this.accept,
required this.account});
final void Function()? deny;
final void Function()? accept;
final AccountModel account;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
DisplayName(account: account),
SelectableText("${"requested-to-follow".i18n()} ${account.identity}"),
Wrap(alignment: WrapAlignment.spaceBetween, children: [
TextButton.icon(
onPressed: deny,
icon: const Icon(Icons.do_not_disturb_alt_outlined),
label: Text("deny".i18n())),
TextButton.icon(
onPressed: accept,
icon: const Icon(Icons.check),
label: Text("accept".i18n()))
]),
],
),
);
}
}

View File

@ -87,6 +87,8 @@
"sending-file": "sending file",
"open-media-in-browser": "open media in browser",
"unread": "unread",
"expand": "expand"
"expand": "expand",
"requested-to-follow": "has requested to follow:",
"follow-requests": "follow requests"
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:localization/localization.dart';
import 'package:loris/dialogues/followreqs.dart';
import '../../global.dart' as global;
class AccountSettings extends StatelessWidget {
@ -13,6 +14,7 @@ class AccountSettings extends StatelessWidget {
LogoutButton(identity: global.settings!.identities.keys.toList()[i]));
}
children.add(const NewAccountButton());
children.add(const FollowreqButton());
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
@ -73,3 +75,19 @@ Future<void> logout(context, String identity) async {
(Navigator.of(context).pushReplacementNamed("/"));
}
}
class FollowreqButton extends StatelessWidget {
const FollowreqButton({super.key});
@override
Widget build(BuildContext context) {
return TextButton.icon(
onPressed: (() => showDialog(
barrierColor: Colors.transparent,
context: context,
builder: (context) => const FollowReqScreen(),
)),
icon: const Icon(Icons.check),
label: Text("follow-requests".i18n()));
}
}

View File

@ -3,6 +3,8 @@ import 'package:localization/localization.dart';
import './account.dart' as account;
import './about.dart' as about;
import './app.dart' as app;
import 'package:loris/global.dart' as global;
import 'package:loris/themes/themes.dart' as themes;
Widget settings(context) {
final List<Widget> categories = [
@ -42,8 +44,11 @@ class SettingsPanel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(24.0),
padding: const EdgeInsets.symmetric(
horizontal: themes.defaultSeperatorHeight * 2,
vertical: themes.defaultSeperatorHeight),
child: Container(
constraints: global.getConstraints(context),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border.all(
@ -56,9 +61,9 @@ class SettingsPanel extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
SelectableText(
title,
style: Theme.of(context).textTheme.headlineMedium,
style: Theme.of(context).textTheme.displayMedium,
),
content,
],