✨ Join the realm by user own
This commit is contained in:
parent
047cb9dc0d
commit
180fbcc558
@ -620,5 +620,6 @@
|
|||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
"tagsHint": "Enter tags, separated by commas",
|
"tagsHint": "Enter tags, separated by commas",
|
||||||
"categories": "Categories",
|
"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:flutter_native_splash/flutter_native_splash.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.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 {
|
void main() async {
|
||||||
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||||
@ -43,6 +49,7 @@ void main() async {
|
|||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
options: DefaultFirebaseOptions.currentPlatform,
|
options: DefaultFirebaseOptions.currentPlatform,
|
||||||
);
|
);
|
||||||
|
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||||
log("[SplashScreen] Firebase is ready!");
|
log("[SplashScreen] Firebase is ready!");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showErrorAlert(err);
|
showErrorAlert(err);
|
||||||
@ -151,17 +158,30 @@ class IslandApp extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
Future(() async {
|
// When the app is opened from a terminated state.
|
||||||
RemoteMessage? initialMessage =
|
FirebaseMessaging.instance.getInitialMessage().then((message) {
|
||||||
await FirebaseMessaging.instance.getInitialMessage();
|
if (message != null) {
|
||||||
if (initialMessage != null) {
|
handleMessage(message);
|
||||||
handleMessage(initialMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(() {
|
useEffect(() {
|
||||||
@ -185,7 +205,7 @@ class IslandApp extends HookConsumerWidget {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
final router = ref.watch(routerProvider);
|
final router = ref.watch(routerProvider);
|
||||||
|
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
theme: theme?.light,
|
theme: theme?.light,
|
||||||
darkTheme: theme?.dark,
|
darkTheme: theme?.dark,
|
||||||
@ -204,9 +224,8 @@ class IslandApp extends HookConsumerWidget {
|
|||||||
initialEntries: [
|
initialEntries: [
|
||||||
OverlayEntry(
|
OverlayEntry(
|
||||||
builder:
|
builder:
|
||||||
(_) => WindowScaffold(
|
(_) =>
|
||||||
child: child ?? const SizedBox.shrink(),
|
WindowScaffold(child: child ?? const SizedBox.shrink()),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.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:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/realm.dart';
|
import 'package:island/models/realm.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:island/screens/realm/realms.dart';
|
import 'package:island/screens/realm/realms.dart';
|
||||||
import 'package:island/widgets/account/account_picker.dart';
|
import 'package:island/widgets/account/account_picker.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
@ -19,6 +22,21 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
|
|
||||||
part 'detail.g.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
|
@riverpod
|
||||||
Future<SnRealmMember?> realmIdentity(Ref ref, String realmSlug) async {
|
Future<SnRealmMember?> realmIdentity(Ref ref, String realmSlug) async {
|
||||||
final apiClient = ref.watch(apiClientProvider);
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
@ -34,9 +52,10 @@ class RealmDetailScreen extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final realmState = ref.watch(realmProvider(slug));
|
final realmState = ref.watch(realmProvider(slug));
|
||||||
|
final appbarColor = ref.watch(realmAppbarForegroundColorProvider(slug));
|
||||||
|
|
||||||
const iconShadow = Shadow(
|
final iconShadow = Shadow(
|
||||||
color: Colors.black54,
|
color: appbarColor.value?.invert ?? Colors.black54,
|
||||||
blurRadius: 5.0,
|
blurRadius: 5.0,
|
||||||
offset: Offset(1.0, 1.0),
|
offset: Offset(1.0, 1.0),
|
||||||
);
|
);
|
||||||
@ -51,7 +70,11 @@ class RealmDetailScreen extends HookConsumerWidget {
|
|||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
expandedHeight: 180,
|
expandedHeight: 180,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
leading: PageBackButton(shadows: [iconShadow]),
|
foregroundColor: appbarColor.value,
|
||||||
|
leading: PageBackButton(
|
||||||
|
color: appbarColor.value,
|
||||||
|
shadows: [iconShadow],
|
||||||
|
),
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
background:
|
background:
|
||||||
realm!.background?.id != null
|
realm!.background?.id != null
|
||||||
@ -63,14 +86,16 @@ class RealmDetailScreen extends HookConsumerWidget {
|
|||||||
title: Text(
|
title: Text(
|
||||||
realm.name,
|
realm.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).appBarTheme.foregroundColor,
|
color:
|
||||||
|
appbarColor.value ??
|
||||||
|
Theme.of(context).appBarTheme.foregroundColor,
|
||||||
shadows: [iconShadow],
|
shadows: [iconShadow],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.people, shadows: [iconShadow]),
|
icon: Icon(Icons.people, shadows: [iconShadow]),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
@ -86,17 +111,58 @@ class RealmDetailScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Padding(
|
child: Column(
|
||||||
padding: const EdgeInsets.all(16.0),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
ref
|
||||||
children: [
|
.watch(realmIdentityProvider(slug))
|
||||||
Text(
|
.when(
|
||||||
realm.description,
|
loading: () => const SizedBox.shrink(),
|
||||||
style: const TextStyle(fontSize: 16),
|
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
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final realmIdentityAsync = ref.watch(realmIdentityProvider(realmSlug));
|
final realmIdentity = ref.watch(realmIdentityProvider(realmSlug));
|
||||||
final isModerator = realmIdentityAsync.when(
|
final isModerator = realmIdentity.when(
|
||||||
data: (identity) => (identity?.role ?? 0) >= 50,
|
data: (identity) => (identity?.role ?? 0) >= 50,
|
||||||
loading: () => false,
|
loading: () => false,
|
||||||
error: (_, _) => false,
|
error: (_, _) => false,
|
||||||
@ -141,7 +207,7 @@ class _RealmActionMenu extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
realmIdentityAsync.when(
|
realmIdentity.when(
|
||||||
data:
|
data:
|
||||||
(identity) =>
|
(identity) =>
|
||||||
(identity?.role ?? 0) >= 100
|
(identity?.role ?? 0) >= 100
|
||||||
|
@ -6,7 +6,8 @@ part of 'detail.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$realmIdentityHash() => r'eac6e829b5b46bcfadbf201ab6f918d78c894b9f';
|
String _$realmAppbarForegroundColorHash() =>
|
||||||
|
r'14b5563d861996ea182d0d2db7aa5c2bb3bbaf48';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
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].
|
/// See also [realmIdentity].
|
||||||
@ProviderFor(realmIdentity)
|
@ProviderFor(realmIdentity)
|
||||||
const realmIdentityProvider = RealmIdentityFamily();
|
const realmIdentityProvider = RealmIdentityFamily();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user