✨ Join the realm by user own
This commit is contained in:
parent
047cb9dc0d
commit
180fbcc558
@ -620,5 +620,6 @@
|
||||
"tags": "Tags",
|
||||
"tagsHint": "Enter tags, separated by commas",
|
||||
"categories": "Categories",
|
||||
"categoriesHint": "Enter categories, separated by commas"
|
||||
"categoriesHint": "Enter categories, separated by commas",
|
||||
"joinRealm": "Join the Realm"
|
||||
}
|
||||
|
@ -29,6 +29,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface.
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
log('Handling a background message: ${message.messageId}');
|
||||
}
|
||||
|
||||
void main() async {
|
||||
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||
@ -43,6 +49,7 @@ void main() async {
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
log("[SplashScreen] Firebase is ready!");
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
@ -151,17 +158,30 @@ class IslandApp extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
useEffect(() {
|
||||
Future(() async {
|
||||
RemoteMessage? initialMessage =
|
||||
await FirebaseMessaging.instance.getInitialMessage();
|
||||
if (initialMessage != null) {
|
||||
handleMessage(initialMessage);
|
||||
// When the app is opened from a terminated state.
|
||||
FirebaseMessaging.instance.getInitialMessage().then((message) {
|
||||
if (message != null) {
|
||||
handleMessage(message);
|
||||
}
|
||||
|
||||
FirebaseMessaging.onMessageOpenedApp.listen(handleMessage);
|
||||
});
|
||||
|
||||
return null;
|
||||
// When the app is in the background and opened.
|
||||
final onMessageOpenedAppSubscription = FirebaseMessaging
|
||||
.onMessageOpenedApp
|
||||
.listen(handleMessage);
|
||||
|
||||
// When the app is in the foreground.
|
||||
final onMessageSubscription = FirebaseMessaging.onMessage.listen((
|
||||
message,
|
||||
) {
|
||||
log('Foreground message received: ${message.messageId}');
|
||||
handleMessage(message);
|
||||
});
|
||||
|
||||
return () {
|
||||
onMessageOpenedAppSubscription.cancel();
|
||||
onMessageSubscription.cancel();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() {
|
||||
@ -185,7 +205,7 @@ class IslandApp extends HookConsumerWidget {
|
||||
}, []);
|
||||
|
||||
final router = ref.watch(routerProvider);
|
||||
|
||||
|
||||
return MaterialApp.router(
|
||||
theme: theme?.light,
|
||||
darkTheme: theme?.dark,
|
||||
@ -204,9 +224,8 @@ class IslandApp extends HookConsumerWidget {
|
||||
initialEntries: [
|
||||
OverlayEntry(
|
||||
builder:
|
||||
(_) => WindowScaffold(
|
||||
child: child ?? const SizedBox.shrink(),
|
||||
),
|
||||
(_) =>
|
||||
WindowScaffold(child: child ?? const SizedBox.shrink()),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -1,12 +1,15 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:island/services/color.dart';
|
||||
import 'package:palette_generator/palette_generator.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/realm.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/screens/realm/realms.dart';
|
||||
import 'package:island/widgets/account/account_picker.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
@ -19,6 +22,21 @@ import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
part 'detail.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<Color?> realmAppbarForegroundColor(Ref ref, String realmSlug) async {
|
||||
final realm = await ref.watch(realmProvider(realmSlug).future);
|
||||
if (realm?.background == null) return null;
|
||||
final palette = await PaletteGenerator.fromImageProvider(
|
||||
CloudImageWidget.provider(
|
||||
fileId: realm!.background!.id,
|
||||
serverUrl: ref.watch(serverUrlProvider),
|
||||
),
|
||||
);
|
||||
final dominantColor = palette.dominantColor?.color;
|
||||
if (dominantColor == null) return null;
|
||||
return dominantColor.computeLuminance() > 0.5 ? Colors.black : Colors.white;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<SnRealmMember?> realmIdentity(Ref ref, String realmSlug) async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
@ -34,9 +52,10 @@ class RealmDetailScreen extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final realmState = ref.watch(realmProvider(slug));
|
||||
final appbarColor = ref.watch(realmAppbarForegroundColorProvider(slug));
|
||||
|
||||
const iconShadow = Shadow(
|
||||
color: Colors.black54,
|
||||
final iconShadow = Shadow(
|
||||
color: appbarColor.value?.invert ?? Colors.black54,
|
||||
blurRadius: 5.0,
|
||||
offset: Offset(1.0, 1.0),
|
||||
);
|
||||
@ -51,7 +70,11 @@ class RealmDetailScreen extends HookConsumerWidget {
|
||||
SliverAppBar(
|
||||
expandedHeight: 180,
|
||||
pinned: true,
|
||||
leading: PageBackButton(shadows: [iconShadow]),
|
||||
foregroundColor: appbarColor.value,
|
||||
leading: PageBackButton(
|
||||
color: appbarColor.value,
|
||||
shadows: [iconShadow],
|
||||
),
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background:
|
||||
realm!.background?.id != null
|
||||
@ -63,14 +86,16 @@ class RealmDetailScreen extends HookConsumerWidget {
|
||||
title: Text(
|
||||
realm.name,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).appBarTheme.foregroundColor,
|
||||
color:
|
||||
appbarColor.value ??
|
||||
Theme.of(context).appBarTheme.foregroundColor,
|
||||
shadows: [iconShadow],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.people, shadows: [iconShadow]),
|
||||
icon: Icon(Icons.people, shadows: [iconShadow]),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
isScrollControlled: true,
|
||||
@ -86,17 +111,58 @@ class RealmDetailScreen extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
realm.description,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ref
|
||||
.watch(realmIdentityProvider(slug))
|
||||
.when(
|
||||
loading: () => const SizedBox.shrink(),
|
||||
error: (_, _) => const SizedBox.shrink(),
|
||||
data:
|
||||
(identity) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ExpansionTile(
|
||||
title: const Text('description').tr(),
|
||||
initiallyExpanded: identity == null,
|
||||
children: [
|
||||
Text(
|
||||
realm.description,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
).padding(horizontal: 16, vertical: 16),
|
||||
],
|
||||
),
|
||||
const Gap(4),
|
||||
if (identity != null && realm.isPublic)
|
||||
FilledButton.tonalIcon(
|
||||
onPressed: () async {
|
||||
try {
|
||||
final apiClient = ref.read(
|
||||
apiClientProvider,
|
||||
);
|
||||
await apiClient.post(
|
||||
'/realms/$slug/members/me',
|
||||
);
|
||||
ref.invalidate(
|
||||
realmIdentityProvider(slug),
|
||||
);
|
||||
ref.invalidate(
|
||||
realmsJoinedProvider,
|
||||
);
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Symbols.add),
|
||||
label: const Text('joinRealm').tr(),
|
||||
).padding(horizontal: 16)
|
||||
else
|
||||
const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -114,8 +180,8 @@ class _RealmActionMenu extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final realmIdentityAsync = ref.watch(realmIdentityProvider(realmSlug));
|
||||
final isModerator = realmIdentityAsync.when(
|
||||
final realmIdentity = ref.watch(realmIdentityProvider(realmSlug));
|
||||
final isModerator = realmIdentity.when(
|
||||
data: (identity) => (identity?.role ?? 0) >= 50,
|
||||
loading: () => false,
|
||||
error: (_, _) => false,
|
||||
@ -141,7 +207,7 @@ class _RealmActionMenu extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
realmIdentityAsync.when(
|
||||
realmIdentity.when(
|
||||
data:
|
||||
(identity) =>
|
||||
(identity?.role ?? 0) >= 100
|
||||
|
@ -6,7 +6,8 @@ part of 'detail.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$realmIdentityHash() => r'eac6e829b5b46bcfadbf201ab6f918d78c894b9f';
|
||||
String _$realmAppbarForegroundColorHash() =>
|
||||
r'14b5563d861996ea182d0d2db7aa5c2bb3bbaf48';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
@ -29,6 +30,133 @@ class _SystemHash {
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [realmAppbarForegroundColor].
|
||||
@ProviderFor(realmAppbarForegroundColor)
|
||||
const realmAppbarForegroundColorProvider = RealmAppbarForegroundColorFamily();
|
||||
|
||||
/// See also [realmAppbarForegroundColor].
|
||||
class RealmAppbarForegroundColorFamily extends Family<AsyncValue<Color?>> {
|
||||
/// See also [realmAppbarForegroundColor].
|
||||
const RealmAppbarForegroundColorFamily();
|
||||
|
||||
/// See also [realmAppbarForegroundColor].
|
||||
RealmAppbarForegroundColorProvider call(String realmSlug) {
|
||||
return RealmAppbarForegroundColorProvider(realmSlug);
|
||||
}
|
||||
|
||||
@override
|
||||
RealmAppbarForegroundColorProvider getProviderOverride(
|
||||
covariant RealmAppbarForegroundColorProvider provider,
|
||||
) {
|
||||
return call(provider.realmSlug);
|
||||
}
|
||||
|
||||
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'realmAppbarForegroundColorProvider';
|
||||
}
|
||||
|
||||
/// See also [realmAppbarForegroundColor].
|
||||
class RealmAppbarForegroundColorProvider
|
||||
extends AutoDisposeFutureProvider<Color?> {
|
||||
/// See also [realmAppbarForegroundColor].
|
||||
RealmAppbarForegroundColorProvider(String realmSlug)
|
||||
: this._internal(
|
||||
(ref) => realmAppbarForegroundColor(
|
||||
ref as RealmAppbarForegroundColorRef,
|
||||
realmSlug,
|
||||
),
|
||||
from: realmAppbarForegroundColorProvider,
|
||||
name: r'realmAppbarForegroundColorProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$realmAppbarForegroundColorHash,
|
||||
dependencies: RealmAppbarForegroundColorFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
RealmAppbarForegroundColorFamily._allTransitiveDependencies,
|
||||
realmSlug: realmSlug,
|
||||
);
|
||||
|
||||
RealmAppbarForegroundColorProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.realmSlug,
|
||||
}) : super.internal();
|
||||
|
||||
final String realmSlug;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<Color?> Function(RealmAppbarForegroundColorRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: RealmAppbarForegroundColorProvider._internal(
|
||||
(ref) => create(ref as RealmAppbarForegroundColorRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
realmSlug: realmSlug,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<Color?> createElement() {
|
||||
return _RealmAppbarForegroundColorProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is RealmAppbarForegroundColorProvider &&
|
||||
other.realmSlug == realmSlug;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, realmSlug.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin RealmAppbarForegroundColorRef on AutoDisposeFutureProviderRef<Color?> {
|
||||
/// The parameter `realmSlug` of this provider.
|
||||
String get realmSlug;
|
||||
}
|
||||
|
||||
class _RealmAppbarForegroundColorProviderElement
|
||||
extends AutoDisposeFutureProviderElement<Color?>
|
||||
with RealmAppbarForegroundColorRef {
|
||||
_RealmAppbarForegroundColorProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get realmSlug =>
|
||||
(origin as RealmAppbarForegroundColorProvider).realmSlug;
|
||||
}
|
||||
|
||||
String _$realmIdentityHash() => r'eac6e829b5b46bcfadbf201ab6f918d78c894b9f';
|
||||
|
||||
/// See also [realmIdentity].
|
||||
@ProviderFor(realmIdentity)
|
||||
const realmIdentityProvider = RealmIdentityFamily();
|
||||
|
Loading…
x
Reference in New Issue
Block a user