import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:localization/localization.dart'; import 'package:loris/business_logic/account/account.dart'; import 'package:loris/business_logic/posting/mentions.dart'; import 'package:loris/business_logic/timeline/media.dart'; import '../../global.dart' as global; class TimelinePartModel { late List threads; late int minId; late int maxId; } enum Visibility { public, unlisted, private, direct } extension VisibilityExtension on Visibility { bool get boostable { switch (this) { case Visibility.direct: return false; case Visibility.private: return false; case Visibility.public: return true; case Visibility.unlisted: return true; } } String get name { switch (this) { case Visibility.direct: return "direct-visibility".i18n(); case Visibility.private: return "private-visibility".i18n(); case Visibility.public: return "public-visibility".i18n(); case Visibility.unlisted: return "unlisted-visibility".i18n(); } } String get queryParam { switch (this) { case Visibility.direct: return "direct"; case Visibility.private: return "private"; case Visibility.public: return "public"; case Visibility.unlisted: return "unlisted"; } } IconData get icon { switch (this) { case Visibility.direct: return Icons.message; case Visibility.private: return Icons.lock; case Visibility.public: return Icons.public; case Visibility.unlisted: return Icons.lock_open; } } } class PostModel implements Comparable { late String identity; late String id; late String? reblogId; late String createdAt; late String uri; late String content; late Visibility visibility; late bool sensitive; late String spoilerText; late bool favourited; late bool reblogged; late AccountModel account; late AccountModel? rebloggedBy; late List attachments; late List mentions = []; // exists if post is a reblog late String originalId; PostModel.fromJson(Map json, this.identity) { id = json["id"] as String; reblogId = null; createdAt = json["created_at"]; // in case of reblog if (json["reblog"] != null) { rebloggedBy = AccountModel.fromJson(json["account"]); json = json["reblog"]; reblogId = json["id"]; } else { rebloggedBy = null; } originalId = json["id"]; uri = json["uri"] as String; content = json["content"] as String; visibility = Visibility.values.firstWhere( // ignore: prefer_interpolation_to_compose_strings (element) => element.toString() == "Visibility." + json["visibility"]); sensitive = json["sensitive"] as bool; spoilerText = json["spoiler_text"] as String; favourited = json["favourited"] as bool; reblogged = json["reblogged"] as bool; account = AccountModel.fromJson(json["account"]); attachments = []; List jsonAttachmentList = json["media_attachments"]; for (int i = 0; i < jsonAttachmentList.length; i++) { attachments.add(MediaAttachmentModel.fromJson(jsonAttachmentList[i])); } List jsonMentionList = json["mentions"]; for (var element in jsonMentionList) { mentions.add(MentionModel.fromJson(element)); } } @override int compareTo(dynamic b) { return id.compareTo(b.id); } Future getThread() async { final activeId = global.settings!.activeIdentity; if (global.settings!.identities[activeId] == null) { return ThreadModel([this]); } final token = global.settings!.identities[activeId]!.token; final baseUrl = global.settings!.identities[activeId]!.instanceUrl; Map headers = {"Authorization": "Bearer $token"}; headers.addAll(global.defaultHeaders); String currentId = reblogId ?? id; final url = Uri( scheme: "https", host: baseUrl, path: "/api/v1/statuses/$currentId/context", ); http.Response response = await http.get(url, headers: headers); if (response.statusCode != 200 || response.body.isEmpty) { return ThreadModel([this]); } final json = jsonDecode(response.body); final List ancestorsJson = json["ancestors"]; List posts = [this]; int i = 0; while (i < ancestorsJson.length) { posts.add(PostModel.fromJson(ancestorsJson[i], activeId)); i++; } return ThreadModel(posts); } } class ThreadModel { late List posts; ThreadModel(List allPosts) { allPosts.sort(); posts = allPosts; } } enum TimelineType { public, local, home, } Future> getTimelineFromServer( String? index, String identity, TimelineType timelineType, ) async { final activeId = identity; if (global.settings!.identities[activeId] == null) { return []; } final limit = global.settings?.batchSize; final token = global.settings?.identities[activeId]?.token; Map query = {"limit": limit.toString()}; if (index != null) { query.addAll({"max_id": index}); } if (timelineType == TimelineType.local) { query.addAll({"local": "true"}); } String tlType = "home"; if (timelineType == TimelineType.public || timelineType == TimelineType.local) { tlType = "public"; } final baseUrl = global.settings!.identities[activeId]!.instanceUrl; final url = Uri( scheme: "https", host: baseUrl, path: "/api/v1/timelines/$tlType", queryParameters: query, ); Map headers = {"Authorization": "Bearer $token"}; headers.addAll(global.defaultHeaders); http.Response response = await http.get(url, headers: headers); while (response.statusCode != 200) { await Future.delayed(const Duration(seconds: 5)); response = await http.get(url, headers: headers); } final List json = await jsonDecode(response.body); List threads = []; for (int i = 0; i < json.length; i++) { threads.add(await PostModel.fromJson(json[i], identity).getThread()); } return threads; }