✨ Chat room details, invitions and members management
This commit is contained in:
107
lib/widgets/account/account_picker.dart
Normal file
107
lib/widgets/account/account_picker.dart
Normal file
@ -0,0 +1,107 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/user.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'account_picker.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<List<SnAccount>> searchAccounts(Ref ref, {required String query}) async {
|
||||
if (query.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
final response = await apiClient.get(
|
||||
'/accounts/search',
|
||||
queryParameters: {'query': query},
|
||||
);
|
||||
|
||||
return response.data!
|
||||
.map((json) => SnAccount.fromJson(json))
|
||||
.cast<SnAccount>()
|
||||
.toList();
|
||||
}
|
||||
|
||||
class AccountPickerSheet extends HookConsumerWidget {
|
||||
const AccountPickerSheet({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final searchController = useTextEditingController();
|
||||
final debounceTimer = useState<Timer?>(null);
|
||||
|
||||
void onSearchChanged(String query) {
|
||||
debounceTimer.value?.cancel();
|
||||
debounceTimer.value = Timer(const Duration(milliseconds: 300), () {
|
||||
ref.read(searchAccountsProvider(query: query));
|
||||
});
|
||||
}
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height * 0.4,
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: TextField(
|
||||
controller: searchController,
|
||||
onChanged: onSearchChanged,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Search accounts...',
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 18,
|
||||
vertical: 16,
|
||||
),
|
||||
),
|
||||
autofocus: true,
|
||||
onTapOutside:
|
||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Consumer(
|
||||
builder: (context, ref, child) {
|
||||
final searchResult = ref.watch(
|
||||
searchAccountsProvider(query: searchController.text),
|
||||
);
|
||||
|
||||
return searchResult.when(
|
||||
data:
|
||||
(accounts) => ListView.builder(
|
||||
itemCount: accounts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final account = accounts[index];
|
||||
return ListTile(
|
||||
leading: ProfilePictureWidget(
|
||||
fileId: account.profile.pictureId,
|
||||
),
|
||||
title: Text(account.nick),
|
||||
subtitle: Text('@${account.name}'),
|
||||
onTap: () => Navigator.of(context).pop(account),
|
||||
);
|
||||
},
|
||||
),
|
||||
loading:
|
||||
() => const Center(child: CircularProgressIndicator()),
|
||||
error:
|
||||
(error, stack) => Center(child: Text('Error: $error')),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
153
lib/widgets/account/account_picker.g.dart
Normal file
153
lib/widgets/account/account_picker.g.dart
Normal file
@ -0,0 +1,153 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'account_picker.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$searchAccountsHash() => r'4923cd06876d04515d95d3c58ee3ea9e05c58e4a';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
_SystemHash._();
|
||||
|
||||
static int combine(int hash, int value) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + value);
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||
return hash ^ (hash >> 6);
|
||||
}
|
||||
|
||||
static int finish(int hash) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||
// ignore: parameter_assignments
|
||||
hash = hash ^ (hash >> 11);
|
||||
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [searchAccounts].
|
||||
@ProviderFor(searchAccounts)
|
||||
const searchAccountsProvider = SearchAccountsFamily();
|
||||
|
||||
/// See also [searchAccounts].
|
||||
class SearchAccountsFamily extends Family<AsyncValue<List<SnAccount>>> {
|
||||
/// See also [searchAccounts].
|
||||
const SearchAccountsFamily();
|
||||
|
||||
/// See also [searchAccounts].
|
||||
SearchAccountsProvider call({required String query}) {
|
||||
return SearchAccountsProvider(query: query);
|
||||
}
|
||||
|
||||
@override
|
||||
SearchAccountsProvider getProviderOverride(
|
||||
covariant SearchAccountsProvider provider,
|
||||
) {
|
||||
return call(query: provider.query);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'searchAccountsProvider';
|
||||
}
|
||||
|
||||
/// See also [searchAccounts].
|
||||
class SearchAccountsProvider
|
||||
extends AutoDisposeFutureProvider<List<SnAccount>> {
|
||||
/// See also [searchAccounts].
|
||||
SearchAccountsProvider({required String query})
|
||||
: this._internal(
|
||||
(ref) => searchAccounts(ref as SearchAccountsRef, query: query),
|
||||
from: searchAccountsProvider,
|
||||
name: r'searchAccountsProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$searchAccountsHash,
|
||||
dependencies: SearchAccountsFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
SearchAccountsFamily._allTransitiveDependencies,
|
||||
query: query,
|
||||
);
|
||||
|
||||
SearchAccountsProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.query,
|
||||
}) : super.internal();
|
||||
|
||||
final String query;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<List<SnAccount>> Function(SearchAccountsRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SearchAccountsProvider._internal(
|
||||
(ref) => create(ref as SearchAccountsRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
query: query,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<List<SnAccount>> createElement() {
|
||||
return _SearchAccountsProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SearchAccountsProvider && other.query == query;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, query.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin SearchAccountsRef on AutoDisposeFutureProviderRef<List<SnAccount>> {
|
||||
/// The parameter `query` of this provider.
|
||||
String get query;
|
||||
}
|
||||
|
||||
class _SearchAccountsProviderElement
|
||||
extends AutoDisposeFutureProviderElement<List<SnAccount>>
|
||||
with SearchAccountsRef {
|
||||
_SearchAccountsProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get query => (origin as SearchAccountsProvider).query;
|
||||
}
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/route.dart';
|
||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:responsive_framework/responsive_framework.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
@ -157,14 +158,21 @@ class AppScaffold extends StatelessWidget {
|
||||
}
|
||||
|
||||
class PageBackButton extends StatelessWidget {
|
||||
const PageBackButton({super.key});
|
||||
final List<Shadow>? shadows;
|
||||
const PageBackButton({super.key, this.shadows});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BackButton(
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
context.router.maybePop();
|
||||
},
|
||||
icon: Icon(
|
||||
(!kIsWeb && (Platform.isMacOS || Platform.isIOS))
|
||||
? Symbols.arrow_back_ios_new
|
||||
: Symbols.arrow_back,
|
||||
shadows: shadows,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,30 @@ class CloudFileWidget extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class CloudImageWidget extends ConsumerWidget {
|
||||
final String fileId;
|
||||
final BoxFit fit;
|
||||
final double aspectRatio;
|
||||
final String? blurHash;
|
||||
const CloudImageWidget({
|
||||
super.key,
|
||||
required this.fileId,
|
||||
this.aspectRatio = 1,
|
||||
this.fit = BoxFit.cover,
|
||||
this.blurHash,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final serverUrl = ref.watch(serverUrlProvider);
|
||||
final uri = '$serverUrl/files/$fileId';
|
||||
return AspectRatio(
|
||||
aspectRatio: aspectRatio,
|
||||
child: UniversalImage(uri: uri, blurHash: blurHash),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ProfilePictureWidget extends ConsumerWidget {
|
||||
final String? fileId;
|
||||
final double radius;
|
||||
|
Reference in New Issue
Block a user