add search

This commit is contained in:
zoe 2022-09-23 15:03:28 +02:00
parent 1992182f27
commit 55c6c92678
4 changed files with 223 additions and 9 deletions

View File

@ -0,0 +1,76 @@
import 'dart:convert';
import 'package:loris/business_logic/account/account.dart';
import 'package:loris/business_logic/timeline/timeline.dart';
import 'package:loris/global.dart' as global;
import 'package:http/http.dart' as http;
class SearchResult {
final String identitiy;
final List<AccountModel> accountModels;
final List<PostModel> postModels;
final String query;
SearchResult(
this.identitiy,
this.query, {
this.postModels = const [],
this.accountModels = const [],
});
}
Future<MapEntry<int, SearchResult?>> searchForEntry(
String q, String identityName) async {
final identity = global.settings!.identities[identityName]!;
final headers = {
...identity.getAuthHeaders(),
...global.defaultHeaders,
};
final uri = Uri(
scheme: "https",
host: identity.instanceUrl,
path: "/api/v2/search",
queryParameters: {"q": q},
);
var response = await http.get(uri, headers: headers);
if (response.statusCode != 200) {
response = await http.get(
Uri(
scheme: uri.scheme,
host: uri.host,
path: "/api/v1/search",
queryParameters: uri.queryParameters,
),
headers: headers);
}
if (response.statusCode != 200) {
return MapEntry(response.statusCode, null);
}
final json = jsonDecode(response.body);
List<AccountModel> accounts = [];
for (var account in json["accounts"]) {
accounts.add(AccountModel.fromJson(
account,
identityName,
));
}
List<PostModel> posts = [];
for (var post in json["statuses"]) {
posts.add(PostModel.fromJson(post, identityName));
}
return MapEntry(
200,
SearchResult(
identityName,
q,
postModels: posts,
accountModels: accounts,
),
);
}

126
lib/dialogues/search.dart Normal file
View File

@ -0,0 +1,126 @@
import 'package:flutter/material.dart';
import 'package:localization/localization.dart';
import 'package:loris/business_logic/account/account.dart';
import 'package:loris/business_logic/timeline/timeline.dart';
import 'package:loris/global.dart' as global;
import 'package:loris/business_logic/search/search.dart' as logic;
import '../partials/post.dart';
class SearchDialogue extends StatefulWidget {
const SearchDialogue({super.key});
@override
State<SearchDialogue> createState() => _SearchDialogueState();
}
class _SearchDialogueState extends State<SearchDialogue> {
String searchText = "";
String searchTextControl = "";
Map<String, logic.SearchResult> results = {};
int searched = 0;
void searchSingle(String id) async {
final result = await logic.searchForEntry(searchText, id);
if (result.key == 200 && mounted) {
setState(() {
results.addAll({id: result.value!});
searched++;
});
}
}
void search() {
searched = 0;
searchTextControl = searchText;
for (var id in global.settings!.identities.keys.toList()) {
searchSingle(id);
}
}
@override
Widget build(BuildContext context) {
List<AccountModel> accountModels = [];
List<PostModel> postModels = [];
results.forEach(
(key, value) {
for (var m in value.accountModels) {
accountModels.add(m);
}
for (var m in value.postModels) {
postModels.add(m);
}
},
);
return SimpleDialog(
contentPadding: const EdgeInsets.all(0),
titlePadding: const EdgeInsets.all(0),
title: Column(
children: [
TextField(
style: Theme.of(context).textTheme.bodyMedium,
autofocus: true,
keyboardType: TextInputType.url,
onEditingComplete: () {
if (searchText.isNotEmpty) {
search();
}
},
onChanged: ((value) => setState(() {
searchText = value;
})),
decoration: const InputDecoration(
prefixIcon: Icon(
Icons.search,
))),
if (searched < global.settings!.identities.length &&
searchTextControl.isNotEmpty)
Column(
children: [
SelectableText(
"${"searched".i18n()} $searched ${searched == 1 ? "instance".i18n() : "instances".i18n()}"),
const LinearProgressIndicator(),
],
),
],
),
children: [
Container(
width: global.getWidth(context),
constraints: BoxConstraints(
maxWidth: global.getConstraints(context).maxWidth,
maxHeight: MediaQuery.of(context).size.height * 2 / 3,
),
child: ListView.builder(
itemCount: accountModels.length + postModels.length,
itemBuilder: (context, index) {
if (index < accountModels.length) {
final model = accountModels[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
SelectableText(model.identity),
DisplayName(account: model)
],
),
);
}
final model = postModels[index - accountModels.length];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
SelectableText(model.identity),
Post(
model: model,
)
],
),
);
}),
),
],
);
}
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:localization/localization.dart';
import 'package:loris/dialogues/search.dart';
import 'package:loris/partials/thread.dart';
import '../../business_logic/timeline/timeline.dart' as tl;
import '../../global.dart' as global;
@ -115,6 +116,12 @@ class TimelineState extends State<Timeline> {
},
icon: const Icon(Icons.refresh),
),
IconButton(
onPressed: () => showDialog(
context: context,
builder: (context) => const SearchDialogue(),
),
icon: const Icon(Icons.search)),
DropdownButtonHideUnderline(
child: DropdownButton(
alignment: Alignment.center,

View File

@ -103,20 +103,25 @@ class _ThreadState extends State<Thread> {
Padding(
padding: const EdgeInsets.all(4),
child: Container(
padding: const EdgeInsets.all(24),
clipBehavior: Clip.none,
foregroundDecoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(8)),
border: Border.all(
width: 2,
color: Theme.of(context).colorScheme.secondary,
),
),
width: (MediaQuery.of(context).size.width *
global.settings!.postWidth) -
56,
constraints: global.getConstraints(context),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border.all(
color: Theme.of(context).colorScheme.secondary, width: 2),
borderRadius: BorderRadius.circular(8),
),
child: Material(
child: Column(
children: c,
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: c,
),
),
),
),