mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-12-10 14:06:38 -05:00
contact invitation accept notifications
This commit is contained in:
parent
6080c2f0c6
commit
1455aabe6c
27 changed files with 718 additions and 220 deletions
26
lib/notifications/cubits/notifications_cubit.dart
Normal file
26
lib/notifications/cubits/notifications_cubit.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../notifications.dart';
|
||||
|
||||
class NotificationsCubit extends Cubit<NotificationsState> {
|
||||
NotificationsCubit(super.initialState);
|
||||
|
||||
void info({required String text, String? title}) {
|
||||
emit(state.copyWith(
|
||||
queue: state.queue.add(NotificationItem(
|
||||
type: NotificationType.info, text: text, title: title))));
|
||||
}
|
||||
|
||||
void error({required String text, String? title}) {
|
||||
emit(state.copyWith(
|
||||
queue: state.queue.add(NotificationItem(
|
||||
type: NotificationType.info, text: text, title: title))));
|
||||
}
|
||||
|
||||
IList<NotificationItem> popAll() {
|
||||
final out = state.queue;
|
||||
emit(state.copyWith(queue: state.queue.clear()));
|
||||
return out;
|
||||
}
|
||||
}
|
||||
23
lib/notifications/models/notifications_state.dart
Normal file
23
lib/notifications/models/notifications_state.dart
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'notifications_state.freezed.dart';
|
||||
|
||||
enum NotificationType {
|
||||
info,
|
||||
error,
|
||||
}
|
||||
|
||||
@freezed
|
||||
class NotificationItem with _$NotificationItem {
|
||||
const factory NotificationItem(
|
||||
{required NotificationType type,
|
||||
required String text,
|
||||
String? title}) = _NotificationItem;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class NotificationsState with _$NotificationsState {
|
||||
const factory NotificationsState({required IList<NotificationItem> queue}) =
|
||||
_NotificationsState;
|
||||
}
|
||||
290
lib/notifications/models/notifications_state.freezed.dart
Normal file
290
lib/notifications/models/notifications_state.freezed.dart
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'notifications_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$NotificationItem {
|
||||
NotificationType get type => throw _privateConstructorUsedError;
|
||||
String get text => throw _privateConstructorUsedError;
|
||||
String? get title => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$NotificationItemCopyWith<NotificationItem> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $NotificationItemCopyWith<$Res> {
|
||||
factory $NotificationItemCopyWith(
|
||||
NotificationItem value, $Res Function(NotificationItem) then) =
|
||||
_$NotificationItemCopyWithImpl<$Res, NotificationItem>;
|
||||
@useResult
|
||||
$Res call({NotificationType type, String text, String? title});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$NotificationItemCopyWithImpl<$Res, $Val extends NotificationItem>
|
||||
implements $NotificationItemCopyWith<$Res> {
|
||||
_$NotificationItemCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? type = null,
|
||||
Object? text = null,
|
||||
Object? title = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
type: null == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as NotificationType,
|
||||
text: null == text
|
||||
? _value.text
|
||||
: text // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
title: freezed == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$NotificationItemImplCopyWith<$Res>
|
||||
implements $NotificationItemCopyWith<$Res> {
|
||||
factory _$$NotificationItemImplCopyWith(_$NotificationItemImpl value,
|
||||
$Res Function(_$NotificationItemImpl) then) =
|
||||
__$$NotificationItemImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({NotificationType type, String text, String? title});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$NotificationItemImplCopyWithImpl<$Res>
|
||||
extends _$NotificationItemCopyWithImpl<$Res, _$NotificationItemImpl>
|
||||
implements _$$NotificationItemImplCopyWith<$Res> {
|
||||
__$$NotificationItemImplCopyWithImpl(_$NotificationItemImpl _value,
|
||||
$Res Function(_$NotificationItemImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? type = null,
|
||||
Object? text = null,
|
||||
Object? title = freezed,
|
||||
}) {
|
||||
return _then(_$NotificationItemImpl(
|
||||
type: null == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as NotificationType,
|
||||
text: null == text
|
||||
? _value.text
|
||||
: text // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
title: freezed == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$NotificationItemImpl implements _NotificationItem {
|
||||
const _$NotificationItemImpl(
|
||||
{required this.type, required this.text, this.title});
|
||||
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final String text;
|
||||
@override
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NotificationItem(type: $type, text: $text, title: $title)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$NotificationItemImpl &&
|
||||
(identical(other.type, type) || other.type == type) &&
|
||||
(identical(other.text, text) || other.text == text) &&
|
||||
(identical(other.title, title) || other.title == title));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, type, text, title);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$NotificationItemImplCopyWith<_$NotificationItemImpl> get copyWith =>
|
||||
__$$NotificationItemImplCopyWithImpl<_$NotificationItemImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _NotificationItem implements NotificationItem {
|
||||
const factory _NotificationItem(
|
||||
{required final NotificationType type,
|
||||
required final String text,
|
||||
final String? title}) = _$NotificationItemImpl;
|
||||
|
||||
@override
|
||||
NotificationType get type;
|
||||
@override
|
||||
String get text;
|
||||
@override
|
||||
String? get title;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$NotificationItemImplCopyWith<_$NotificationItemImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$NotificationsState {
|
||||
IList<NotificationItem> get queue => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$NotificationsStateCopyWith<NotificationsState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $NotificationsStateCopyWith<$Res> {
|
||||
factory $NotificationsStateCopyWith(
|
||||
NotificationsState value, $Res Function(NotificationsState) then) =
|
||||
_$NotificationsStateCopyWithImpl<$Res, NotificationsState>;
|
||||
@useResult
|
||||
$Res call({IList<NotificationItem> queue});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$NotificationsStateCopyWithImpl<$Res, $Val extends NotificationsState>
|
||||
implements $NotificationsStateCopyWith<$Res> {
|
||||
_$NotificationsStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? queue = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
queue: null == queue
|
||||
? _value.queue
|
||||
: queue // ignore: cast_nullable_to_non_nullable
|
||||
as IList<NotificationItem>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$NotificationsStateImplCopyWith<$Res>
|
||||
implements $NotificationsStateCopyWith<$Res> {
|
||||
factory _$$NotificationsStateImplCopyWith(_$NotificationsStateImpl value,
|
||||
$Res Function(_$NotificationsStateImpl) then) =
|
||||
__$$NotificationsStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({IList<NotificationItem> queue});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$NotificationsStateImplCopyWithImpl<$Res>
|
||||
extends _$NotificationsStateCopyWithImpl<$Res, _$NotificationsStateImpl>
|
||||
implements _$$NotificationsStateImplCopyWith<$Res> {
|
||||
__$$NotificationsStateImplCopyWithImpl(_$NotificationsStateImpl _value,
|
||||
$Res Function(_$NotificationsStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? queue = null,
|
||||
}) {
|
||||
return _then(_$NotificationsStateImpl(
|
||||
queue: null == queue
|
||||
? _value.queue
|
||||
: queue // ignore: cast_nullable_to_non_nullable
|
||||
as IList<NotificationItem>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$NotificationsStateImpl implements _NotificationsState {
|
||||
const _$NotificationsStateImpl({required this.queue});
|
||||
|
||||
@override
|
||||
final IList<NotificationItem> queue;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NotificationsState(queue: $queue)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$NotificationsStateImpl &&
|
||||
const DeepCollectionEquality().equals(other.queue, queue));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(queue));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$NotificationsStateImplCopyWith<_$NotificationsStateImpl> get copyWith =>
|
||||
__$$NotificationsStateImplCopyWithImpl<_$NotificationsStateImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _NotificationsState implements NotificationsState {
|
||||
const factory _NotificationsState(
|
||||
{required final IList<NotificationItem> queue}) =
|
||||
_$NotificationsStateImpl;
|
||||
|
||||
@override
|
||||
IList<NotificationItem> get queue;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$NotificationsStateImplCopyWith<_$NotificationsStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
3
lib/notifications/notifications.dart
Normal file
3
lib/notifications/notifications.dart
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export 'cubits/notifications_cubit.dart';
|
||||
export 'models/notifications_state.dart';
|
||||
export 'views/notifications_widget.dart';
|
||||
91
lib/notifications/views/notifications_widget.dart
Normal file
91
lib/notifications/views/notifications_widget.dart
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:motion_toast/motion_toast.dart';
|
||||
|
||||
import '../../theme/theme.dart';
|
||||
import '../notifications.dart';
|
||||
|
||||
class NotificationsWidget extends StatelessWidget {
|
||||
const NotificationsWidget({required Widget child, super.key})
|
||||
: _child = child;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Public API
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final notificationsCubit = context.read<NotificationsCubit>();
|
||||
|
||||
return BlocListener<NotificationsCubit, NotificationsState>(
|
||||
bloc: notificationsCubit,
|
||||
listener: (context, state) {
|
||||
if (state.queue.isNotEmpty) {
|
||||
final queue = notificationsCubit.popAll();
|
||||
for (final notificationItem in queue) {
|
||||
switch (notificationItem.type) {
|
||||
case NotificationType.info:
|
||||
_info(
|
||||
context: context,
|
||||
text: notificationItem.text,
|
||||
title: notificationItem.title);
|
||||
case NotificationType.error:
|
||||
_error(
|
||||
context: context,
|
||||
text: notificationItem.text,
|
||||
title: notificationItem.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: _child);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Private Implementation
|
||||
|
||||
void _info(
|
||||
{required BuildContext context, required String text, String? title}) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
MotionToast(
|
||||
title: title != null ? Text(title) : null,
|
||||
description: Text(text),
|
||||
constraints: BoxConstraints.loose(const Size(400, 100)),
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
primaryColor: scale.tertiaryScale.elementBackground,
|
||||
secondaryColor: scale.tertiaryScale.calloutBackground,
|
||||
borderRadius: 12 * scaleConfig.borderRadiusScale,
|
||||
toastDuration: const Duration(seconds: 2),
|
||||
animationDuration: const Duration(milliseconds: 500),
|
||||
displayBorder: scaleConfig.useVisualIndicators,
|
||||
icon: Icons.info,
|
||||
).show(context);
|
||||
}
|
||||
|
||||
void _error(
|
||||
{required BuildContext context, required String text, String? title}) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
MotionToast(
|
||||
title: title != null ? Text(title) : null,
|
||||
description: Text(text),
|
||||
constraints: BoxConstraints.loose(const Size(400, 100)),
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
primaryColor: scale.errorScale.elementBackground,
|
||||
secondaryColor: scale.errorScale.calloutBackground,
|
||||
borderRadius: 12 * scaleConfig.borderRadiusScale,
|
||||
toastDuration: const Duration(seconds: 4),
|
||||
animationDuration: const Duration(milliseconds: 1000),
|
||||
displayBorder: scaleConfig.useVisualIndicators,
|
||||
icon: Icons.error,
|
||||
).show(context);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
final Widget _child;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue