multi account boosting!!!!!!!!!!!!!!!!!!!!!!!!!!!!
This commit is contained in:
parent
d973c921bd
commit
af002b875f
|
@ -0,0 +1,119 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:loris/global.dart' as global;
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
enum InteractionType {
|
||||
favorite,
|
||||
reblog,
|
||||
}
|
||||
|
||||
extension InteractionTypeExtension on InteractionType {
|
||||
IconData get icon {
|
||||
switch (this) {
|
||||
case InteractionType.favorite:
|
||||
return Icons.favorite_border;
|
||||
case InteractionType.reblog:
|
||||
return Icons.repeat_sharp;
|
||||
}
|
||||
}
|
||||
|
||||
IconData get revokeIcon {
|
||||
switch (this) {
|
||||
case InteractionType.favorite:
|
||||
return Icons.favorite;
|
||||
case InteractionType.reblog:
|
||||
return Icons.repeat_on_sharp;
|
||||
}
|
||||
}
|
||||
|
||||
String get slug {
|
||||
switch (this) {
|
||||
case InteractionType.favorite:
|
||||
return "favourite";
|
||||
case InteractionType.reblog:
|
||||
return "reblog";
|
||||
}
|
||||
}
|
||||
|
||||
String get revokeSlug {
|
||||
switch (this) {
|
||||
case InteractionType.favorite:
|
||||
return "unfavourite";
|
||||
case InteractionType.reblog:
|
||||
return "unreblog";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> makeInteractionFromId(
|
||||
String id,
|
||||
String postid,
|
||||
InteractionType type, {
|
||||
bool revoke = false,
|
||||
}) async {
|
||||
Map<String, String> headers =
|
||||
global.settings!.identities[id]!.getAuthHeaders();
|
||||
headers.addAll(global.defaultHeaders);
|
||||
|
||||
final uri = Uri(
|
||||
scheme: "https",
|
||||
path: "/api/v1/statuses/$postid/${revoke ? type.revokeSlug : type.slug}",
|
||||
host: global.settings!.identities[id]!.instanceUrl,
|
||||
);
|
||||
|
||||
final response = await http.post(uri, headers: headers);
|
||||
return response.statusCode;
|
||||
}
|
||||
|
||||
Future<int> makeInteractionFromUrl(
|
||||
String id,
|
||||
String posturl,
|
||||
InteractionType type, {
|
||||
bool revoke = false,
|
||||
}) async {
|
||||
Map<String, String> headers =
|
||||
global.settings!.identities[id]!.getAuthHeaders();
|
||||
headers.addAll(global.defaultHeaders);
|
||||
|
||||
final uri = Uri(
|
||||
scheme: "https",
|
||||
host: global.settings!.identities[id]!.instanceUrl,
|
||||
path: "api/v1/search",
|
||||
queryParameters: {
|
||||
"type": "statuses",
|
||||
"q": posturl,
|
||||
},
|
||||
);
|
||||
|
||||
final response = await http.get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return response.statusCode;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> json = jsonDecode(response.body);
|
||||
final List<dynamic> statuses = json["statuses"];
|
||||
if (statuses.isEmpty) {
|
||||
return 404;
|
||||
}
|
||||
print(response.body);
|
||||
final String postid = statuses[0]["id"];
|
||||
|
||||
return await makeInteractionFromId(id, postid, type);
|
||||
}
|
||||
|
||||
Future<int> makeFullInteraction(
|
||||
String id,
|
||||
String postid,
|
||||
String posturl,
|
||||
InteractionType type, {
|
||||
bool revoke = false,
|
||||
}) async {
|
||||
int i = await makeInteractionFromId(id, postid, type);
|
||||
if (i == 200) {
|
||||
return i;
|
||||
}
|
||||
return await makeInteractionFromUrl(id, posturl, type);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:loris/business_logic/interactions/interactions.dart';
|
||||
|
||||
class InteractionMenu extends StatefulWidget {
|
||||
const InteractionMenu({required this.type, Key? key}) : super(key: key);
|
||||
final InteractionType type;
|
||||
|
||||
@override
|
||||
State<InteractionMenu> createState() => _InteractionMenuState();
|
||||
}
|
||||
|
||||
class _InteractionMenuState extends State<InteractionMenu> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SimpleDialog();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import '../business_logic/posting/posting.dart' as logic;
|
||||
|
||||
class MakePost extends StatelessWidget {
|
||||
const MakePost({Key? key}) : super(key: key);
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:loris/business_logic/timeline/timeline.dart';
|
||||
import '../business_logic/interactions/interactions.dart' as interactions;
|
||||
import 'package:loris/global.dart' as global;
|
||||
|
||||
class InteractionButton extends StatefulWidget {
|
||||
const InteractionButton({required this.model, required this.type, Key? key})
|
||||
: super(key: key);
|
||||
final interactions.InteractionType type;
|
||||
final PostModel model;
|
||||
|
||||
@override
|
||||
State<InteractionButton> createState() => _InteractionButtonState();
|
||||
}
|
||||
|
||||
class _InteractionButtonState extends State<InteractionButton> {
|
||||
IconData icon = Icons.question_mark;
|
||||
String idkey = global.settings!.activeIdentity;
|
||||
bool active = false;
|
||||
bool busy = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
switch (widget.type) {
|
||||
case interactions.InteractionType.favorite:
|
||||
active = widget.model.favourited;
|
||||
break;
|
||||
case interactions.InteractionType.reblog:
|
||||
active = widget.model.reblogged;
|
||||
break;
|
||||
}
|
||||
icon = active ? widget.type.revokeIcon : widget.type.icon;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> sendRequest({
|
||||
required String id,
|
||||
bool fromUrl = false,
|
||||
}) async {
|
||||
if (!busy) {
|
||||
busy = true;
|
||||
int status = fromUrl
|
||||
? await interactions.makeFullInteraction(
|
||||
id,
|
||||
widget.model.id,
|
||||
widget.model.uri,
|
||||
widget.type,
|
||||
revoke: active,
|
||||
)
|
||||
: await interactions.makeInteractionFromId(
|
||||
id,
|
||||
widget.model.id,
|
||||
widget.type,
|
||||
revoke: active,
|
||||
);
|
||||
|
||||
if (status == 200 && mounted) {
|
||||
setState(() {
|
||||
active = !active;
|
||||
icon = active ? widget.type.revokeIcon : widget.type.icon;
|
||||
});
|
||||
} else {
|
||||
await showError(status);
|
||||
}
|
||||
busy = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showError(int status) async {
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(content: Text("error: $status")));
|
||||
setState(() {
|
||||
icon = Icons.dangerous;
|
||||
});
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
setState(() {
|
||||
icon = active ? widget.type.revokeIcon : widget.type.icon;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (global.settings!.identities.length == 1) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
await sendRequest(id: global.settings!.identities.keys.first);
|
||||
},
|
||||
icon: Icon(icon),
|
||||
);
|
||||
}
|
||||
|
||||
// user is logged into multiple accounts
|
||||
List<PopupMenuItem> identityPickers = [];
|
||||
global.settings!.identities.forEach(
|
||||
(key, value) {
|
||||
identityPickers.add(
|
||||
PopupMenuItem(
|
||||
value: key,
|
||||
child: Text(key),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return PopupMenuButton(
|
||||
onSelected: ((value) {
|
||||
setState(() {
|
||||
idkey = value as String;
|
||||
});
|
||||
sendRequest(
|
||||
id: value as String,
|
||||
fromUrl: true,
|
||||
);
|
||||
}),
|
||||
itemBuilder: (context) => identityPickers,
|
||||
initialValue: idkey,
|
||||
icon: Icon(icon),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -22,9 +22,11 @@ class _MainScaffoldState extends State<MainScaffold> {
|
|||
void initState() {
|
||||
notifStream.stream.listen((event) {
|
||||
if (index != 2) {
|
||||
setState(() {
|
||||
unreadNotifs = event;
|
||||
});
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
unreadNotifs = event;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
super.initState();
|
||||
|
|
|
@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
|
|||
import 'package:localization/localization.dart';
|
||||
import 'package:loris/business_logic/account/account.dart';
|
||||
import 'package:loris/business_logic/timeline/media.dart';
|
||||
import 'package:loris/partials/interaction_button.dart';
|
||||
import 'package:loris/partials/media_attachment.dart';
|
||||
import 'package:loris/partials/post_options.dart';
|
||||
import 'package:loris/partials/post_text_renderer.dart';
|
||||
import '../business_logic/timeline/timeline.dart' as tl;
|
||||
import '../business_logic/interactions/interactions.dart' as interactions;
|
||||
|
||||
class Post extends StatefulWidget {
|
||||
const Post({required this.model, Key? key}) : super(key: key);
|
||||
|
@ -29,7 +31,7 @@ class _PostState extends State<Post> {
|
|||
spoilerText: widget.model.spoilerText,
|
||||
media: widget.model.attachments,
|
||||
),
|
||||
postActionBar(context, widget.model),
|
||||
PostActionBar(model: widget.model),
|
||||
RebloggedBy(account: widget.model.rebloggedBy),
|
||||
],
|
||||
);
|
||||
|
@ -226,28 +228,36 @@ class _PostBodyState extends State<PostBody> {
|
|||
}
|
||||
}
|
||||
|
||||
Widget postActionBar(context, tl.PostModel model) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.reply),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.repeat),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.favorite_outline),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
popupPostOptions(context, model);
|
||||
},
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
)
|
||||
],
|
||||
);
|
||||
class PostActionBar extends StatefulWidget {
|
||||
const PostActionBar({required this.model, Key? key}) : super(key: key);
|
||||
final tl.PostModel model;
|
||||
|
||||
@override
|
||||
State<PostActionBar> createState() => _PostActionBarState();
|
||||
}
|
||||
|
||||
class _PostActionBarState extends State<PostActionBar> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.reply)),
|
||||
InteractionButton(
|
||||
model: widget.model,
|
||||
type: interactions.InteractionType.reblog,
|
||||
),
|
||||
InteractionButton(
|
||||
model: widget.model,
|
||||
type: interactions.InteractionType.favorite,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
popupPostOptions(context, widget.model);
|
||||
},
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ class Thread extends StatelessWidget {
|
|||
56,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: global.settings!.maxPostWidth,
|
||||
minWidth: 375,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
|
|
Loading…
Reference in New Issue