🎨 Use feature based folder structure

This commit is contained in:
2026-02-06 00:37:02 +08:00
parent 62a3ea26e3
commit 862e3b451b
539 changed files with 8406 additions and 5056 deletions

View File

@@ -1,6 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/abuse_report.dart'; import 'package:island/accounts/accounts_models/abuse_report.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
final abuseReportServiceProvider = Provider<AbuseReportService>((ref) { final abuseReportServiceProvider = Provider<AbuseReportService>((ref) {
return AbuseReportService(ref); return AbuseReportService(ref);

View File

@@ -4,11 +4,11 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/pods/paging.dart'; import 'package:island/pagination/pagination.dart';
import 'package:island/services/time.dart'; import 'package:island/core/services/time.dart';
import 'package:island/widgets/paging/pagination_list.dart'; import 'package:island/shared/widgets/pagination_list.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -3,17 +3,17 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/network.dart'; import 'package:island/accounts/accounts_widgets/account/leveling_progress.dart';
import 'package:island/pods/paging.dart'; import 'package:island/accounts/accounts_widgets/account/stellar_program_tab.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/core/network.dart';
import 'package:island/screens/account/credits.dart'; import 'package:island/pagination/pagination.dart';
import 'package:island/services/time.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/widgets/account/leveling_progress.dart'; import 'package:island/accounts/account/credits.dart';
import 'package:island/widgets/account/stellar_program_tab.dart'; import 'package:island/core/services/time.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:island/widgets/paging/pagination_list.dart'; import 'package:island/shared/widgets/pagination_list.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
final levelingHistoryNotifierProvider = final levelingHistoryNotifierProvider =

View File

@@ -6,19 +6,19 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/auth.dart'; import 'package:island/accounts/accounts_widgets/account/account_devices.dart';
import 'package:island/models/account.dart'; import 'package:island/auth/auth_models/auth.dart';
import 'package:island/pods/network.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/core/network.dart';
import 'package:island/screens/account/me/settings_auth_factors.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/screens/account/me/settings_connections.dart'; import 'package:island/accounts/account/me/settings_auth_factors.dart';
import 'package:island/screens/account/me/settings_contacts.dart'; import 'package:island/accounts/account/me/settings_connections.dart';
import 'package:island/screens/auth/captcha.dart'; import 'package:island/accounts/account/me/settings_contacts.dart';
import 'package:island/screens/auth/login.dart'; import 'package:island/auth/captcha.dart';
import 'package:island/widgets/account/account_devices.dart'; import 'package:island/auth/login.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'package:island/widgets/response.dart'; import 'package:island/shared/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -7,17 +7,17 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:island/models/file.dart'; import 'package:island/accounts/accounts_widgets/account/account_name.dart';
import 'package:island/models/account.dart'; import 'package:island/core/services/image.dart';
import 'package:island/pods/network.dart'; import 'package:island/drive/drive_models/file.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/services/file.dart'; import 'package:island/core/network.dart';
import 'package:island/services/file_uploader.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/services/timezone.dart'; import 'package:island/drive/drive_service.dart';
import 'package:island/widgets/account/account_name.dart'; import 'package:island/core/services/timezone.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/drive/drive_widgets/cloud_files.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -7,11 +7,11 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_otp_text_field/flutter_otp_text_field.dart'; import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/auth.dart'; import 'package:island/auth/auth_models/auth.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/screens/auth/login.dart'; import 'package:island/auth/login.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:qr_flutter/qr_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -64,38 +64,37 @@ class AuthFactorSheet extends HookConsumerWidget {
if ([3].contains(factor.type)) { if ([3].contains(factor.type)) {
final confirmed = await showDialog<bool>( final confirmed = await showDialog<bool>(
context: context, context: context,
builder: builder: (context) => AlertDialog(
(context) => AlertDialog( title: Text('authFactorEnable').tr(),
title: Text('authFactorEnable').tr(), content: Column(
content: Column( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, children: [
children: [ Text('authFactorEnableHint').tr(),
Text('authFactorEnableHint').tr(), const SizedBox(height: 16),
const SizedBox(height: 16), OtpTextField(
OtpTextField( showCursor: false,
showCursor: false, numberOfFields: 6,
numberOfFields: 6, obscureText: false,
obscureText: false, showFieldAsBox: true,
showFieldAsBox: true, focusedBorderColor: Theme.of(context).colorScheme.primary,
focusedBorderColor: Theme.of(context).colorScheme.primary, onSubmit: (String verificationCode) {
onSubmit: (String verificationCode) { password = verificationCode;
password = verificationCode; },
}, textStyle: Theme.of(context).textTheme.titleLarge!,
textStyle: Theme.of(context).textTheme.titleLarge!,
),
],
), ),
actions: [ ],
TextButton( ),
onPressed: () => Navigator.of(context).pop(false), actions: [
child: Text('cancel').tr(), TextButton(
), onPressed: () => Navigator.of(context).pop(false),
TextButton( child: Text('cancel').tr(),
onPressed: () => Navigator.of(context).pop(true),
child: Text('confirm').tr(),
),
],
), ),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('confirm').tr(),
),
],
),
); );
if (confirmed == false || if (confirmed == false ||
(password?.isEmpty ?? true) || (password?.isEmpty ?? true) ||
@@ -233,19 +232,18 @@ class AuthFactorNewSheet extends HookConsumerWidget {
labelText: 'authFactor'.tr(), labelText: 'authFactor'.tr(),
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
), ),
items: items: kFactorTypes.entries.map((entry) {
kFactorTypes.entries.map((entry) { return DropdownMenuItem<int>(
return DropdownMenuItem<int>( value: entry.key,
value: entry.key, child: Row(
child: Row( children: [
children: [ Icon(entry.value.$3),
Icon(entry.value.$3), const Gap(8),
const Gap(8), Text(entry.value.$1).tr(),
Text(entry.value.$1).tr(), ],
], ),
), );
); }).toList(),
}).toList(),
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
factorType.value = value; factorType.value = value;
@@ -261,8 +259,8 @@ class AuthFactorNewSheet extends HookConsumerWidget {
hintText: 'authFactorSecretHint'.tr(), hintText: 'authFactorSecretHint'.tr(),
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
), ),
onTapOutside: onTapOutside: (_) =>
(_) => FocusManager.instance.primaryFocus?.unfocus(), FocusManager.instance.primaryFocus?.unfocus(),
) )
else if ([4].contains(factorType.value)) else if ([4].contains(factorType.value))
OtpTextField( OtpTextField(

View File

@@ -4,15 +4,15 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/auth.dart'; import 'package:island/auth/auth_models/auth.dart';
import 'package:island/pods/config.dart'; import 'package:island/core/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/screens/account/me/account_settings.dart'; import 'package:island/accounts/account/me/account_settings.dart';
import 'package:island/utils/text.dart'; import 'package:island/core/utils/text.dart';
import 'package:island/services/time.dart'; import 'package:island/core/services/time.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:island/widgets/response.dart'; import 'package:island/shared/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart'; import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -233,17 +233,14 @@ class AccountConnectionNewSheet extends HookConsumerWidget {
labelText: 'accountConnectionProvider'.tr(), labelText: 'accountConnectionProvider'.tr(),
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
), ),
items: items: providers.map((String provider) {
providers.map((String provider) { return DropdownMenuItem<String>(
return DropdownMenuItem<String>( value: provider,
value: provider, child: Row(
child: Row( children: [Text(getLocalizedProviderName(provider)).tr()],
children: [ ),
Text(getLocalizedProviderName(provider)).tr(), );
], }).toList(),
),
);
}).toList(),
onChanged: (String? newValue) { onChanged: (String? newValue) {
if (newValue != null) { if (newValue != null) {
selectedProvider.value = newValue; selectedProvider.value = newValue;
@@ -296,104 +293,88 @@ class AccountConnectionsSheet extends HookConsumerWidget {
), ),
], ],
child: connections.when( child: connections.when(
data: data: (data) => RefreshIndicator(
(data) => RefreshIndicator( onRefresh: () =>
onRefresh: Future.sync(() => ref.invalidate(accountConnectionsProvider)),
() => Future.sync( child: data.isEmpty
() => ref.invalidate(accountConnectionsProvider), ? Center(
), child: Text(
child: 'accountConnectionsEmpty'.tr(),
data.isEmpty textAlign: TextAlign.center,
? Center( ).padding(horizontal: 32),
child: Text( )
'accountConnectionsEmpty'.tr(), : ListView.builder(
textAlign: TextAlign.center, padding: EdgeInsets.zero,
).padding(horizontal: 32), itemCount: data.length,
) itemBuilder: (context, index) {
: ListView.builder( final connection = data[index];
padding: EdgeInsets.zero, return Dismissible(
itemCount: data.length, key: Key('connection-${connection.id}'),
itemBuilder: (context, index) { direction: DismissDirection.endToStart,
final connection = data[index]; background: Container(
return Dismissible( color: Colors.red,
key: Key('connection-${connection.id}'), alignment: Alignment.centerRight,
direction: DismissDirection.endToStart, padding: const EdgeInsets.symmetric(horizontal: 20),
background: Container( child: const Icon(Icons.delete, color: Colors.white),
color: Colors.red, ),
alignment: Alignment.centerRight, confirmDismiss: (direction) async {
padding: const EdgeInsets.symmetric( final confirm = await showConfirmAlert(
horizontal: 20, 'accountConnectionDeleteHint'.tr(),
), 'accountConnectionDelete'.tr(),
child: const Icon( isDanger: true,
Icons.delete, );
color: Colors.white, if (confirm && context.mounted) {
), try {
), final client = ref.read(apiClientProvider);
confirmDismiss: (direction) async { await client.delete(
final confirm = await showConfirmAlert( '/pass/accounts/me/connections/${connection.id}',
'accountConnectionDeleteHint'.tr(), );
'accountConnectionDelete'.tr(), ref.invalidate(accountConnectionsProvider);
isDanger: true, return true;
); } catch (err) {
if (confirm && context.mounted) { showErrorAlert(err);
try { return false;
final client = ref.read(apiClientProvider); }
await client.delete( }
'/pass/accounts/me/connections/${connection.id}', return false;
); },
ref.invalidate(accountConnectionsProvider); child: ListTile(
return true; leading: getProviderIcon(
} catch (err) { connection.provider,
showErrorAlert(err); color: Theme.of(context).colorScheme.onSurface,
return false; ),
} title: Text(
} getLocalizedProviderName(connection.provider),
return false; ).tr(),
}, subtitle: connection.meta['email'] != null
child: ListTile( ? Text(connection.meta['email'])
leading: getProviderIcon( : Text(connection.providedIdentifier),
connection.provider, trailing: Text(
color: Theme.of(context).colorScheme.onSurface, DateFormat.yMd().format(
), connection.lastUsedAt.toLocal(),
title: ),
Text( style: Theme.of(context).textTheme.bodySmall,
getLocalizedProviderName( ),
connection.provider, onTap: () async {
), final result = await showModalBottomSheet<bool>(
).tr(), context: context,
subtitle: isScrollControlled: true,
connection.meta['email'] != null builder: (context) =>
? Text(connection.meta['email']) AccountConnectionSheet(connection: connection),
: Text(connection.providedIdentifier),
trailing: Text(
DateFormat.yMd().format(
connection.lastUsedAt.toLocal(),
),
style: Theme.of(context).textTheme.bodySmall,
),
onTap: () async {
final result = await showModalBottomSheet<bool>(
context: context,
isScrollControlled: true,
builder:
(context) => AccountConnectionSheet(
connection: connection,
),
);
if (result == true) {
ref.invalidate(accountConnectionsProvider);
}
},
),
); );
if (result == true) {
ref.invalidate(accountConnectionsProvider);
}
}, },
), ),
), );
error: },
(err, _) => ResponseErrorWidget( ),
error: err, ),
onRetry: () => ref.invalidate(accountConnectionsProvider), error: (err, _) => ResponseErrorWidget(
), error: err,
onRetry: () => ref.invalidate(accountConnectionsProvider),
),
loading: () => const ResponseLoadingWidget(), loading: () => const ResponseLoadingWidget(),
), ),
); );

View File

@@ -3,10 +3,10 @@ import 'package:flutter/material.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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -153,10 +153,9 @@ class ContactMethodSheet extends HookConsumerWidget {
child: Badge( child: Badge(
label: Text('contactMethodPrivate'.tr()), label: Text('contactMethodPrivate'.tr()),
textColor: Theme.of(context).colorScheme.onSurface, textColor: Theme.of(context).colorScheme.onSurface,
backgroundColor: backgroundColor: Theme.of(
Theme.of( context,
context, ).colorScheme.surfaceContainerHighest,
).colorScheme.surfaceContainerHighest,
), ),
), ),
], ],
@@ -319,12 +318,11 @@ class ContactMethodNewSheet extends HookConsumerWidget {
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: child: Text(switch (contactType.value) {
Text(switch (contactType.value) { 0 => 'contactMethodEmailDescription',
0 => 'contactMethodEmailDescription', 1 => 'contactMethodPhoneDescription',
1 => 'contactMethodPhoneDescription', _ => 'contactMethodAddressDescription',
_ => 'contactMethodAddressDescription', }).tr(),
}).tr(),
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,

View File

@@ -7,33 +7,33 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/chat.dart'; import 'package:island/accounts/accounts_widgets/account/account_name.dart';
import 'package:island/models/developer.dart'; import 'package:island/accounts/accounts_widgets/account/activity_presence.dart';
import 'package:island/models/publisher.dart'; import 'package:island/accounts/accounts_widgets/account/badge.dart';
import 'package:island/models/relationship.dart'; import 'package:island/accounts/accounts_widgets/account/fortune_graph.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_widgets/account/leveling_progress.dart';
import 'package:island/pods/config.dart'; import 'package:island/accounts/accounts_widgets/account/status.dart';
import 'package:island/pods/event_calendar.dart'; import 'package:island/chat/chat_models/chat.dart';
import 'package:island/pods/network.dart'; import 'package:island/developers/developers_models/developer.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/posts/posts_models/publisher.dart';
import 'package:island/services/color.dart'; import 'package:island/accounts/accounts_models/relationship.dart';
import 'package:island/services/responsive.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/utils/text.dart'; import 'package:island/core/config.dart';
import 'package:island/services/time.dart'; import 'package:island/accounts/event_calendar.dart';
import 'package:island/services/timezone/native.dart'; import 'package:island/core/network.dart';
import 'package:island/widgets/account/account_name.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/widgets/account/activity_presence.dart'; import 'package:island/core/services/color.dart';
import 'package:island/widgets/account/badge.dart'; import 'package:island/core/services/responsive.dart';
import 'package:island/widgets/account/fortune_graph.dart'; import 'package:island/core/utils/text.dart';
import 'package:island/widgets/account/leveling_progress.dart'; import 'package:island/core/services/time.dart';
import 'package:island/widgets/account/status.dart'; import 'package:island/core/services/timezone/native.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/drive/drive_widgets/cloud_files.dart';
import 'package:island/widgets/content/markdown.dart'; import 'package:island/core/widgets/content/markdown.dart';
import 'package:island/widgets/safety/abuse_report_helper.dart'; import 'package:island/reports/reports_widgets/safety/abuse_report_helper.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:island/services/color_extraction.dart'; import 'package:island/core/services/color_extraction.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -6,19 +6,19 @@ import 'package:flutter/services.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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/paging.dart'; import 'package:island/accounts/accounts_widgets/account/account_pfc.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/accounts/accounts_widgets/account/account_picker.dart';
import 'package:island/widgets/account/account_pfc.dart'; import 'package:island/pagination/pagination.dart';
import 'package:island/widgets/account/account_picker.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/drive/drive_widgets/cloud_files.dart';
import 'package:island/widgets/paging/pagination_list.dart'; import 'package:island/shared/widgets/pagination_list.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:relative_time/relative_time.dart'; import 'package:relative_time/relative_time.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:island/models/relationship.dart'; import 'package:island/accounts/accounts_models/relationship.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
part 'relationship.g.dart'; part 'relationship.g.dart';
@@ -112,7 +112,7 @@ class RelationshipListTile extends StatelessWidget {
return ListTile( return ListTile(
contentPadding: const EdgeInsets.only(left: 16, right: 12), contentPadding: const EdgeInsets.only(left: 16, right: 12),
leading: AccountPfcGestureDetector( leading: AccountPfcRegion(
uname: account.name, uname: account.name,
child: ProfilePictureWidget(file: account.profile.picture), child: ProfilePictureWidget(file: account.profile.picture),
), ),

View File

@@ -1,8 +1,8 @@
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/models/activity.dart'; import 'package:island/core/models/activity.dart';
import 'package:island/models/auth.dart'; import 'package:island/auth/auth_models/auth.dart';
import 'package:island/models/file.dart'; import 'package:island/drive/drive_models/file.dart';
import 'package:island/models/wallet.dart'; import 'package:island/wallet/wallet_models/wallet.dart';
part 'account.freezed.dart'; part 'account.freezed.dart';
part 'account.g.dart'; part 'account.g.dart';

View File

@@ -1,6 +1,6 @@
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
part 'relationship.freezed.dart'; part 'relationship.freezed.dart';
part 'relationship.g.dart'; part 'relationship.g.dart';

View File

@@ -2,14 +2,14 @@ import 'dart:convert';
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/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/config.dart'; import 'package:island/core/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/talker.dart'; import 'package:island/talker.dart';
import 'package:island/services/analytics_service.dart'; import 'package:island/core/services/analytics_service.dart';
class UserInfoNotifier extends AsyncNotifier<SnAccount?> { class UserInfoNotifier extends AsyncNotifier<SnAccount?> {
@override @override

View File

@@ -3,20 +3,20 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/message.dart'; import 'package:island/accounts/accounts_widgets/account/account_name.dart';
import 'package:island/pods/network.dart'; import 'package:island/accounts/accounts_widgets/account/activity_presence.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/accounts/accounts_widgets/account/leveling_progress.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/accounts/accounts_widgets/account/status.dart';
import 'package:island/screens/notification.dart'; import 'package:island/core/websocket.dart';
import 'package:island/services/responsive.dart'; import 'package:island/core/database.dart';
import 'package:island/widgets/account/account_name.dart'; import 'package:island/core/network.dart';
import 'package:island/widgets/account/activity_presence.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/widgets/account/status.dart'; import 'package:island/core/services/responsive.dart';
import 'package:island/widgets/account/leveling_progress.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/drive/drive_widgets/cloud_files.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/core/debug_sheet.dart';
import 'package:island/widgets/debug_sheet.dart'; import 'package:island/notifications/notification.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -4,19 +4,19 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/services/responsive.dart'; import 'package:island/core/services/responsive.dart';
import 'package:island/services/time.dart'; import 'package:island/core/services/time.dart';
import 'package:island/services/udid.dart'; import 'package:island/core/services/udid.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:island/widgets/response.dart'; import 'package:island/shared/widgets/response.dart';
import 'package:island/widgets/sites/info_row.dart'; import 'package:island/shared/widgets/info_row.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:island/widgets/extended_refresh_indicator.dart'; import 'package:island/shared/widgets/extended_refresh_indicator.dart';
part 'account_devices.g.dart'; part 'account_devices.g.dart';
@@ -26,11 +26,10 @@ Future<List<SnAuthDeviceWithSession>> authDevices(Ref ref) async {
.watch(apiClientProvider) .watch(apiClientProvider)
.get('/pass/accounts/me/devices'); .get('/pass/accounts/me/devices');
final currentId = await getUdid(); final currentId = await getUdid();
final data = final data = resp.data.map<SnAuthDeviceWithSession>((e) {
resp.data.map<SnAuthDeviceWithSession>((e) { final ele = SnAuthDeviceWithSession.fromJson(e);
final ele = SnAuthDeviceWithSession.fromJson(e); return ele.copyWith(isCurrent: ele.deviceId == currentId);
return ele.copyWith(isCurrent: ele.deviceId == currentId); }).toList();
}).toList();
return data; return data;
} }
@@ -91,25 +90,24 @@ class _DeviceListTile extends StatelessWidget {
6 => Icons.computer, // Linux 6 => Icons.computer, // Linux
_ => Icons.device_unknown, // fallback _ => Icons.device_unknown, // fallback
}).padding(top: 4), }).padding(top: 4),
trailing: trailing: isWideScreen(context)
isWideScreen(context) ? Row(
? Row( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, children: [
children: [ IconButton(
icon: Icon(Icons.edit),
tooltip: 'authDeviceEditLabel'.tr(),
onPressed: () => updateDeviceLabel(device.deviceId),
),
if (!device.isCurrent)
IconButton( IconButton(
icon: Icon(Icons.edit), icon: Icon(Icons.logout),
tooltip: 'authDeviceEditLabel'.tr(), tooltip: 'authDeviceLogout'.tr(),
onPressed: () => updateDeviceLabel(device.deviceId), onPressed: () => logoutDevice(device.deviceId),
), ),
if (!device.isCurrent) ],
IconButton( )
icon: Icon(Icons.logout), : null,
tooltip: 'authDeviceLogout'.tr(),
onPressed: () => logoutDevice(device.deviceId),
),
],
)
: null,
expandedCrossAxisAlignment: CrossAxisAlignment.stretch, expandedCrossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Container( Container(
@@ -216,29 +214,28 @@ class AccountSessionSheet extends HookConsumerWidget {
final controller = TextEditingController(); final controller = TextEditingController();
final label = await showDialog<String>( final label = await showDialog<String>(
context: context, context: context,
builder: builder: (context) => AlertDialog(
(context) => AlertDialog( title: Text('authDeviceLabelTitle'.tr()),
title: Text('authDeviceLabelTitle'.tr()), content: TextField(
content: TextField( controller: controller,
controller: controller, decoration: InputDecoration(
decoration: InputDecoration( isDense: true,
isDense: true, border: const OutlineInputBorder(),
border: const OutlineInputBorder(), hintText: 'authDeviceLabelHint'.tr(),
hintText: 'authDeviceLabelHint'.tr(),
),
autofocus: true,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('cancel'.tr()),
),
TextButton(
onPressed: () => Navigator.pop(context, controller.text),
child: Text('confirm'.tr()),
),
],
), ),
autofocus: true,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('cancel'.tr()),
),
TextButton(
onPressed: () => Navigator.pop(context, controller.text),
child: Text('confirm'.tr()),
),
],
),
); );
if (label == null || label.isEmpty || !context.mounted) return; if (label == null || label.isEmpty || !context.mounted) return;
try { try {
@@ -283,90 +280,83 @@ class AccountSessionSheet extends HookConsumerWidget {
), ),
Expanded( Expanded(
child: authDevices.when( child: authDevices.when(
data: data: (data) => ExtendedRefreshIndicator(
(data) => ExtendedRefreshIndicator( onRefresh: () =>
onRefresh: Future.sync(() => ref.invalidate(authDevicesProvider)),
() => Future.sync( child: ListView.builder(
() => ref.invalidate(authDevicesProvider), padding: EdgeInsets.zero,
itemCount: data.length,
itemBuilder: (context, index) {
final device = data[index];
if (wideScreen) {
return _DeviceListTile(
device: device,
updateDeviceLabel: updateDeviceLabel,
logoutDevice: logoutDevice,
logoutSession: logoutSession,
);
} else {
return Dismissible(
key: Key('device-${device.id}'),
direction: device.isCurrent
? DismissDirection.startToEnd
: DismissDirection.horizontal,
background: Container(
color: Colors.blue,
alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(horizontal: 20),
child: Icon(Icons.edit, color: Colors.white),
), ),
child: ListView.builder( secondaryBackground: Container(
padding: EdgeInsets.zero, color: Colors.red,
itemCount: data.length, alignment: Alignment.centerRight,
itemBuilder: (context, index) { padding: EdgeInsets.symmetric(horizontal: 20),
final device = data[index]; child: Icon(Icons.logout, color: Colors.white),
if (wideScreen) { ),
return _DeviceListTile( confirmDismiss: (direction) async {
device: device, if (direction == DismissDirection.startToEnd) {
updateDeviceLabel: updateDeviceLabel, updateDeviceLabel(device.deviceId);
logoutDevice: logoutDevice, return false;
logoutSession: logoutSession, } else {
); final confirm = await showConfirmAlert(
} else { 'authDeviceLogoutHint'.tr(),
return Dismissible( 'authDeviceLogout'.tr(),
key: Key('device-${device.id}'), isDanger: true,
direction: );
device.isCurrent if (confirm && context.mounted) {
? DismissDirection.startToEnd try {
: DismissDirection.horizontal, showLoadingModal(context);
background: Container( final apiClient = ref.watch(apiClientProvider);
color: Colors.blue, await apiClient.delete(
alignment: Alignment.centerLeft, '/pass/accounts/me/devices/${device.deviceId}',
padding: EdgeInsets.symmetric(horizontal: 20),
child: Icon(Icons.edit, color: Colors.white),
),
secondaryBackground: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: EdgeInsets.symmetric(horizontal: 20),
child: Icon(Icons.logout, color: Colors.white),
),
confirmDismiss: (direction) async {
if (direction == DismissDirection.startToEnd) {
updateDeviceLabel(device.deviceId);
return false;
} else {
final confirm = await showConfirmAlert(
'authDeviceLogoutHint'.tr(),
'authDeviceLogout'.tr(),
isDanger: true,
); );
if (confirm && context.mounted) { ref.invalidate(authDevicesProvider);
try { } catch (err) {
showLoadingModal(context); showErrorAlert(err);
final apiClient = ref.watch( } finally {
apiClientProvider, if (context.mounted) {
); hideLoadingModal(context);
await apiClient.delete(
'/pass/accounts/me/devices/${device.deviceId}',
);
ref.invalidate(authDevicesProvider);
} catch (err) {
showErrorAlert(err);
} finally {
if (context.mounted) {
hideLoadingModal(context);
}
}
} }
return confirm;
} }
}, }
child: _DeviceListTile( return confirm;
device: device, }
updateDeviceLabel: updateDeviceLabel, },
logoutDevice: logoutDevice, child: _DeviceListTile(
logoutSession: logoutSession, device: device,
), updateDeviceLabel: updateDeviceLabel,
); logoutDevice: logoutDevice,
} logoutSession: logoutSession,
}, ),
), );
), }
error: },
(err, _) => ResponseErrorWidget( ),
error: err, ),
onRetry: () => ref.invalidate(authDevicesProvider), error: (err, _) => ResponseErrorWidget(
), error: err,
onRetry: () => ref.invalidate(authDevicesProvider),
),
loading: () => ResponseLoadingWidget(), loading: () => ResponseLoadingWidget(),
), ),
), ),

View File

@@ -2,10 +2,10 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/models/wallet.dart'; import 'package:island/wallet/wallet_models/wallet.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:island/widgets/account/account_name.dart'; import 'package:island/accounts/account/profile.dart';
import 'package:island/accounts/accounts_widgets/account/account_name.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/screens/account/profile.dart'; import 'package:island/drive/drive_widgets/cloud_files.dart';
import 'package:island/widgets/content/cloud_files.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';

View File

@@ -7,16 +7,15 @@ import 'package:go_router/go_router.dart';
import 'package:flutter_popup_card/flutter_popup_card.dart'; import 'package:flutter_popup_card/flutter_popup_card.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/screens/account/profile.dart'; import 'package:island/accounts/account/profile.dart';
import 'package:island/services/time.dart'; import 'package:island/accounts/accounts_widgets/account/account_name.dart';
import 'package:island/services/timezone/native.dart'; import 'package:island/accounts/accounts_widgets/account/activity_presence.dart';
import 'package:island/widgets/account/account_name.dart'; import 'package:island/accounts/accounts_widgets/account/badge.dart';
import 'package:island/widgets/account/activity_presence.dart'; import 'package:island/accounts/accounts_widgets/account/status.dart';
import 'package:island/widgets/account/badge.dart'; import 'package:island/core/services/time.dart';
import 'package:island/core/services/timezone/native.dart';
import 'package:island/widgets/account/status.dart'; import 'package:island/drive/drive_widgets/cloud_files.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/shared/widgets/response.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -225,14 +224,10 @@ class AccountProfileCard extends HookConsumerWidget {
} }
} }
class AccountPfcGestureDetector extends StatelessWidget { class AccountPfcRegion extends StatelessWidget {
final String? uname; final String? uname;
final Widget child; final Widget child;
const AccountPfcGestureDetector({ const AccountPfcRegion({super.key, required this.uname, required this.child});
super.key,
required this.uname,
required this.child,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@@ -4,9 +4,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/drive/drive_widgets/cloud_files.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'account_picker.g.dart'; part 'account_picker.g.dart';

View File

@@ -0,0 +1,618 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/core/models/activity.dart';
import 'package:island/activity/activity_rpc.dart';
import 'package:island/core/widgets/content/image.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:url_launcher/url_launcher_string.dart';
part 'activity_presence.g.dart';
@riverpod
Future<Map<String, String>?> discordAssets(
Ref ref,
SnPresenceActivity activity,
) async {
final hasDiscordSmall =
activity.smallImage != null &&
activity.smallImage!.startsWith('discord:');
final hasDiscordLarge =
activity.largeImage != null &&
activity.largeImage!.startsWith('discord:');
if (hasDiscordSmall || hasDiscordLarge) {
final dio = Dio();
final response = await dio.get(
'https://discordapp.com/api/oauth2/applications/${activity.manualId}/assets',
);
final data = response.data as List<dynamic>;
return {
for (final item in data) item['name'] as String: item['id'] as String,
};
}
return null;
}
@riverpod
Future<String?> discordAssetsUrl(
Ref ref,
SnPresenceActivity activity,
String key,
) async {
final assets = await ref.watch(discordAssetsProvider(activity).future);
if (assets != null && assets.containsKey(key)) {
final assetId = assets[key]!;
return 'https://cdn.discordapp.com/app-assets/${activity.manualId}/$assetId.png';
}
return null;
}
const kPresenceActivityTypes = [
'unknown',
'presenceTypeGaming',
'presenceTypeMusic',
'presenceTypeWorkout',
];
const kPresenceActivityIcons = <IconData>[
Symbols.question_mark_rounded,
Symbols.play_arrow_rounded,
Symbols.music_note_rounded,
Symbols.running_with_errors,
];
class ActivityPresenceWidget extends StatefulWidget {
final String uname;
final bool isCompact;
final EdgeInsets compactPadding;
const ActivityPresenceWidget({
super.key,
required this.uname,
this.isCompact = false,
this.compactPadding = EdgeInsets.zero,
});
@override
State<ActivityPresenceWidget> createState() => _ActivityPresenceWidgetState();
}
class _ActivityPresenceWidgetState extends State<ActivityPresenceWidget>
with TickerProviderStateMixin {
late AnimationController _progressController;
late Animation<double> _progressAnimation;
double _startProgress = 0.0;
double _endProgress = 0.0;
@override
void initState() {
super.initState();
_progressController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
_progressAnimation = Tween<double>(
begin: 0.0,
end: 0.0,
).animate(_progressController);
}
@override
void dispose() {
_progressController.dispose();
super.dispose();
}
List<Widget> _buildImages(WidgetRef ref, SnPresenceActivity activity) {
final List<Widget> images = [];
if (activity.largeImage != null) {
if (activity.largeImage!.startsWith('discord:')) {
final key = activity.largeImage!.substring('discord:'.length);
final urlAsync = ref.watch(discordAssetsUrlProvider(activity, key));
images.add(
urlAsync.when(
data: (url) => url != null
? ClipRRect(
borderRadius: BorderRadius.circular(8),
child: CachedNetworkImage(
imageUrl: url,
width: 64,
height: 64,
),
)
: const SizedBox.shrink(),
loading: () => const SizedBox(
width: 64,
height: 64,
child: CircularProgressIndicator(strokeWidth: 2),
),
error: (error, stack) => const SizedBox.shrink(),
),
);
} else {
images.add(
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: UniversalImage(
uri: activity.largeImage!,
width: 64,
height: 64,
),
),
);
}
}
if (activity.smallImage != null) {
if (activity.smallImage!.startsWith('discord:')) {
final key = activity.smallImage!.substring('discord:'.length);
final urlAsync = ref.watch(discordAssetsUrlProvider(activity, key));
images.add(
urlAsync.when(
data: (url) => url != null
? ClipRRect(
borderRadius: BorderRadius.circular(8),
child: CachedNetworkImage(
imageUrl: url,
width: 32,
height: 32,
),
)
: const SizedBox.shrink(),
loading: () => const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
),
error: (error, stack) => const SizedBox.shrink(),
),
);
} else {
images.add(
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: UniversalImage(
uri: activity.smallImage!,
width: 32,
height: 32,
),
),
);
}
}
return images;
}
@override
Widget build(BuildContext context) {
return Consumer(
builder: (BuildContext context, WidgetRef ref, Widget? child) {
final activitiesAsync = ref.watch(
presenceActivitiesProvider(widget.uname),
);
if (widget.isCompact) {
return activitiesAsync.when(
data: (activities) {
if (activities.isEmpty) return const SizedBox.shrink();
final activity = activities.first;
return Padding(
padding: widget.compactPadding,
child: Row(
spacing: 8,
children: [
if (activity.largeImage != null)
activity.largeImage!.startsWith('discord:')
? ref
.watch(
discordAssetsUrlProvider(
activity,
activity.largeImage!.substring(
'discord:'.length,
),
),
)
.when(
data: (url) => url != null
? ClipRRect(
borderRadius: BorderRadius.circular(
4,
),
child: CachedNetworkImage(
imageUrl: url,
width: 32,
height: 32,
),
)
: const SizedBox.shrink(),
loading: () => const SizedBox(
width: 32,
height: 32,
child: CircularProgressIndicator(
strokeWidth: 1,
),
),
error: (error, stack) =>
const SizedBox.shrink(),
)
: ClipRRect(
borderRadius: BorderRadius.circular(4),
child: UniversalImage(
uri: activity.largeImage!,
width: 32,
height: 32,
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
(activity.title?.isEmpty ?? true)
? 'unknown'.tr()
: activity.title!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
).fontSize(13),
Row(
children: [
Text(
kPresenceActivityTypes[activity.type],
).tr().fontSize(11),
Icon(
kPresenceActivityIcons[activity.type],
size: 15,
fill: 1,
),
],
),
],
),
),
StreamBuilder(
stream: Stream.periodic(const Duration(seconds: 1)),
builder: (context, snapshot) {
final now = DateTime.now();
if (activity.manualId == 'spotify' &&
activity.meta != null) {
final meta = activity.meta as Map<String, dynamic>;
final progressMs = meta['progress_ms'] as int? ?? 0;
final durationMs =
meta['track_duration_ms'] as int? ?? 1;
final elapsed = now
.difference(activity.createdAt)
.inMilliseconds;
final currentProgressMs =
(progressMs + elapsed) % durationMs;
final progressValue = currentProgressMs / durationMs;
if (progressValue != _endProgress) {
_startProgress = _endProgress;
_endProgress = progressValue;
_progressAnimation = Tween<double>(
begin: _startProgress,
end: _endProgress,
).animate(_progressController);
_progressController.forward(from: 0.0);
}
return AnimatedBuilder(
animation: _progressAnimation,
builder: (context, child) {
final animatedValue = _progressAnimation.value;
final animatedProgressMs =
(animatedValue * durationMs).toInt();
final currentMin = animatedProgressMs ~/ 60000;
final currentSec =
(animatedProgressMs % 60000) ~/ 1000;
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
spacing: 2,
children: [
Text(
'${currentMin.toString().padLeft(2, '0')}:${currentSec.toString().padLeft(2, '0')}',
style: TextStyle(
fontSize: 10,
color: Colors.green,
),
),
SizedBox(
width: 120,
child: LinearProgressIndicator(
value: animatedValue,
backgroundColor: Colors.grey.shade300,
stopIndicatorColor: Colors.green,
trackGap: 0,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.green,
),
),
).padding(top: 2),
],
);
},
);
} else {
final duration = now.difference(activity.createdAt);
final hours = duration.inHours.toString().padLeft(
2,
'0',
);
final minutes = (duration.inMinutes % 60)
.toString()
.padLeft(2, '0');
final seconds = (duration.inSeconds % 60)
.toString()
.padLeft(2, '0');
return Text(
'$hours:$minutes:$seconds',
).textColor(Colors.green).fontSize(12);
}
},
),
],
),
);
},
loading: () => const SizedBox.shrink(),
error: (error, stack) => const SizedBox.shrink(),
);
}
return activitiesAsync.when(
data: (activities) => Card(
margin: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: [
Text(
'activities',
).tr().bold().padding(horizontal: 16, vertical: 4),
if (activities.isEmpty)
Row(
spacing: 4,
children: [
const Icon(Symbols.inbox, size: 16),
Text('dataEmpty').tr().fontSize(13),
],
).opacity(0.75).padding(horizontal: 16, bottom: 8),
...activities.map((activity) {
final images = _buildImages(ref, activity);
return Stack(
children: [
Card(
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
borderRadius: BorderRadius.circular(8),
),
margin: EdgeInsets.zero,
child: ListTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (images.isNotEmpty)
Row(
crossAxisAlignment: CrossAxisAlignment.end,
spacing: 8,
children: images,
).padding(vertical: 4),
Row(
spacing: 2,
children: [
Flexible(
child: Text(
(activity.title?.isEmpty ?? true)
? 'unknown'.tr()
: activity.title!,
),
),
if (activity.titleUrl != null &&
activity.titleUrl!.isNotEmpty)
IconButton(
visualDensity: const VisualDensity(
vertical: -4,
),
onPressed: () {
launchUrlString(activity.titleUrl!);
},
icon: const Icon(Symbols.launch_rounded),
iconSize: 16,
padding: EdgeInsets.all(4),
constraints: const BoxConstraints(
maxWidth: 28,
maxHeight: 28,
),
),
],
),
],
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
spacing: 4,
children: [
Text(
kPresenceActivityTypes[activity.type],
).tr(),
Icon(
kPresenceActivityIcons[activity.type],
size: 16,
fill: 1,
),
],
),
if (activity.manualId == 'spotify' &&
activity.meta != null)
StreamBuilder(
stream: Stream.periodic(
const Duration(seconds: 1),
),
builder: (context, snapshot) {
final now = DateTime.now();
final meta =
activity.meta as Map<String, dynamic>;
final progressMs =
meta['progress_ms'] as int? ?? 0;
final durationMs =
meta['track_duration_ms'] as int? ?? 1;
final elapsed = now
.difference(activity.createdAt)
.inMilliseconds;
final currentProgressMs =
(progressMs + elapsed) % durationMs;
final progressValue =
currentProgressMs / durationMs;
if (progressValue != _endProgress) {
_startProgress = _endProgress;
_endProgress = progressValue;
_progressAnimation = Tween<double>(
begin: _startProgress,
end: _endProgress,
).animate(_progressController);
_progressController.forward(from: 0.0);
}
return AnimatedBuilder(
animation: _progressAnimation,
builder: (context, child) {
final animatedValue =
_progressAnimation.value;
final animatedProgressMs =
(animatedValue * durationMs)
.toInt();
final currentMin =
animatedProgressMs ~/ 60000;
final currentSec =
(animatedProgressMs % 60000) ~/
1000;
final totalMin = durationMs ~/ 60000;
final totalSec =
(durationMs % 60000) ~/ 1000;
return Column(
crossAxisAlignment:
CrossAxisAlignment.start,
spacing: 4,
children: [
LinearProgressIndicator(
value: animatedValue,
backgroundColor:
Colors.grey.shade300,
trackGap: 0,
stopIndicatorColor: Colors.green,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.green,
),
).padding(top: 3),
Text(
'${currentMin.toString().padLeft(2, '0')}:${currentSec.toString().padLeft(2, '0')} / ${totalMin.toString().padLeft(2, '0')}:${totalSec.toString().padLeft(2, '0')}',
style: TextStyle(
fontSize: 12,
color: Colors.green,
),
),
],
);
},
);
},
)
else
StreamBuilder(
stream: Stream.periodic(
const Duration(seconds: 1),
),
builder: (context, snapshot) {
final now = DateTime.now();
final duration = now.difference(
activity.createdAt,
);
final hours = duration.inHours
.toString()
.padLeft(2, '0');
final minutes = (duration.inMinutes % 60)
.toString()
.padLeft(2, '0');
final seconds = (duration.inSeconds % 60)
.toString()
.padLeft(2, '0');
return Text(
'$hours:$minutes:$seconds',
).textColor(Colors.green);
},
),
if (activity.subtitle?.isNotEmpty ?? false)
Row(
spacing: 2,
children: [
Flexible(child: Text(activity.subtitle!)),
if (activity.titleUrl != null &&
activity.titleUrl!.isNotEmpty)
IconButton(
visualDensity: const VisualDensity(
vertical: -4,
),
onPressed: () {
launchUrlString(activity.titleUrl!);
},
icon: const Icon(
Symbols.launch_rounded,
),
iconSize: 16,
padding: EdgeInsets.all(4),
constraints: const BoxConstraints(
maxWidth: 28,
maxHeight: 28,
),
),
],
),
if (activity.caption?.isNotEmpty ?? false)
Text(activity.caption!),
],
),
),
).padding(horizontal: 8),
if (activity.manualId == 'spotify')
Positioned(
top: 16,
right: 24,
child: Tooltip(
message: 'Listening on Spotify',
child: Image.asset(
'assets/images/oidc/spotify.png',
width: 24,
height: 24,
color: Theme.of(context).colorScheme.onSurface,
),
),
),
],
);
}),
],
).padding(horizontal: 8, top: 8, bottom: 16),
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) =>
Center(child: Text('Error loading activities: $error')),
);
},
);
}
}

View File

@@ -1,7 +1,7 @@
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/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/models/badge.dart'; import 'package:island/accounts/accounts_models/badge.dart';
class BadgeList extends StatelessWidget { class BadgeList extends StatelessWidget {
final List<SnAccountBadge> badges; final List<SnAccountBadge> badges;

View File

@@ -2,11 +2,11 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/models/activity.dart'; import 'package:island/accounts/accounts_widgets/account/event_details_widget.dart';
import 'package:island/core/models/activity.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:island/widgets/account/event_details_widget.dart';
import 'package:table_calendar/table_calendar.dart'; import 'package:table_calendar/table_calendar.dart';
/// A reusable widget for displaying an event calendar with event details /// A reusable widget for displaying an event calendar with event details
@@ -89,28 +89,28 @@ class EventCalendarWidget extends HookConsumerWidget {
return Center(child: Text(text)); return Center(child: Text(text));
}, },
markerBuilder: (context, day, events) { markerBuilder: (context, day, events) {
final checkInResult = final checkInResult = events
events.whereType<SnCheckInResult>().firstOrNull; .whereType<SnCheckInResult>()
.firstOrNull;
final statuses = events.whereType<SnAccountStatus>().toList(); final statuses = events.whereType<SnAccountStatus>().toList();
final textColor = final textColor = isSameDay(selectedDay.value, day)
isSameDay(selectedDay.value, day) ? Colors.white
? Colors.white : isSameDay(DateTime.now(), day)
: isSameDay(DateTime.now(), day) ? Colors.white
? Colors.white : Theme.of(context).colorScheme.onSurface;
: Theme.of(context).colorScheme.onSurface;
final shadow = final shadow =
isSameDay(selectedDay.value, day) || isSameDay(selectedDay.value, day) ||
isSameDay(DateTime.now(), day) isSameDay(DateTime.now(), day)
? [ ? [
Shadow( Shadow(
color: Colors.black.withOpacity(0.5), color: Colors.black.withOpacity(0.5),
offset: const Offset(0, 1), offset: const Offset(0, 1),
blurRadius: 4, blurRadius: 4,
), ),
] ]
: null; : null;
if (checkInResult != null) { if (checkInResult != null) {
return Positioned( return Positioned(
@@ -152,10 +152,9 @@ class EventCalendarWidget extends HookConsumerWidget {
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
child: Builder( child: Builder(
builder: (context) { builder: (context) {
final event = final event = events.value
events.value ?.where((e) => isSameDay(e.date, selectedDay.value))
?.where((e) => isSameDay(e.date, selectedDay.value)) .firstOrNull;
.firstOrNull;
return EventDetailsWidget( return EventDetailsWidget(
selectedDay: selectedDay.value, selectedDay: selectedDay.value,
event: event, event: event,

View File

@@ -2,11 +2,11 @@ import 'package:flutter/material.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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/event_calendar.dart'; import 'package:island/accounts/account/profile.dart';
import 'package:island/screens/account/profile.dart'; import 'package:island/accounts/accounts_widgets/account/account_nameplate.dart';
import 'package:island/widgets/account/account_nameplate.dart'; import 'package:island/accounts/accounts_widgets/account/event_calendar.dart';
import 'package:island/widgets/account/event_calendar.dart'; import 'package:island/accounts/accounts_widgets/account/fortune_graph.dart';
import 'package:island/widgets/account/fortune_graph.dart'; import 'package:island/accounts/event_calendar.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
/// A reusable content widget for event calendar that can be used in screens or sheets /// A reusable content widget for event calendar that can be used in screens or sheets
@@ -85,65 +85,64 @@ class EventCalendarContent extends HookConsumerWidget {
} else { } else {
// Screen layout - with responsive design // Screen layout - with responsive design
return SingleChildScrollView( return SingleChildScrollView(
child: child: MediaQuery.of(context).size.width > 480
MediaQuery.of(context).size.width > 480 ? ConstrainedBox(
? ConstrainedBox( constraints: BoxConstraints(maxWidth: 480),
constraints: BoxConstraints(maxWidth: 480), child: Column(
child: Column(
children: [
Card(
margin: EdgeInsets.only(left: 16, right: 16, top: 16),
child: Column(
children: [
// Use the reusable EventCalendarWidget
EventCalendarWidget(
events: events,
initialDate: now,
showEventDetails: true,
onMonthChanged: onMonthChanged,
onDaySelected: onDaySelected,
),
],
),
),
// Add the fortune graph widget
FortuneGraphWidget(
events: events,
constrainWidth: true,
onPointSelected: onDaySelected,
),
// Show user profile if viewing someone else's calendar
if (name != 'me' && user.value != null)
AccountNameplate(name: name),
],
),
).center()
: Column(
children: [ children: [
// Use the reusable EventCalendarWidget Card(
EventCalendarWidget( margin: EdgeInsets.only(left: 16, right: 16, top: 16),
events: events, child: Column(
initialDate: now, children: [
showEventDetails: true, // Use the reusable EventCalendarWidget
onMonthChanged: onMonthChanged, EventCalendarWidget(
onDaySelected: onDaySelected, events: events,
initialDate: now,
showEventDetails: true,
onMonthChanged: onMonthChanged,
onDaySelected: onDaySelected,
),
],
),
), ),
// Add the fortune graph widget // Add the fortune graph widget
const Divider(height: 1),
FortuneGraphWidget( FortuneGraphWidget(
events: events, events: events,
constrainWidth: true,
onPointSelected: onDaySelected, onPointSelected: onDaySelected,
).padding(horizontal: 8, vertical: 4), ),
// Show user profile if viewing someone else's calendar // Show user profile if viewing someone else's calendar
if (name != 'me' && user.value != null) if (name != 'me' && user.value != null)
AccountNameplate(name: name), AccountNameplate(name: name),
Gap(MediaQuery.of(context).padding.bottom + 16),
], ],
), ),
).center()
: Column(
children: [
// Use the reusable EventCalendarWidget
EventCalendarWidget(
events: events,
initialDate: now,
showEventDetails: true,
onMonthChanged: onMonthChanged,
onDaySelected: onDaySelected,
),
// Add the fortune graph widget
const Divider(height: 1),
FortuneGraphWidget(
events: events,
onPointSelected: onDaySelected,
).padding(horizontal: 8, vertical: 4),
// Show user profile if viewing someone else's calendar
if (name != 'me' && user.value != null)
AccountNameplate(name: name),
Gap(MediaQuery.of(context).padding.bottom + 16),
],
),
); );
} }
} }

View File

@@ -1,9 +1,9 @@
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:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:island/models/activity.dart'; import 'package:island/core/models/activity.dart';
import 'package:island/services/time.dart'; import 'package:island/core/services/time.dart';
import 'package:island/utils/activity_utils.dart'; import 'package:island/core/utils/activity_utils.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -2,9 +2,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/activity.dart'; import 'package:island/accounts/accounts_widgets/account/event_calendar_content.dart';
import 'package:island/widgets/account/event_calendar_content.dart'; import 'package:island/core/models/activity.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
/// A widget that displays a graph of fortune levels over time /// A widget that displays a graph of fortune levels over time
@@ -73,14 +73,13 @@ class FortuneGraphWidget extends HookConsumerWidget {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
builder: builder: (context) => SheetScaffold(
(context) => SheetScaffold( titleText: 'eventCalendar'.tr(),
titleText: 'eventCalendar'.tr(), child: EventCalendarContent(
child: EventCalendarContent( name: eventCalandarUser!,
name: eventCalandarUser!, isSheet: true,
isSheet: true, ),
), ),
),
); );
}, },
), ),
@@ -95,15 +94,14 @@ class FortuneGraphWidget extends HookConsumerWidget {
} }
// Create spots for the line chart // Create spots for the line chart
final spots = final spots = data
data .map(
.map( (e) => FlSpot(
(e) => FlSpot( e.date.millisecondsSinceEpoch.toDouble(),
e.date.millisecondsSinceEpoch.toDouble(), e.checkInResult!.level.toDouble(),
e.checkInResult!.level.toDouble(), ),
), )
) .toList();
.toList();
// Get min and max dates for the x-axis // Get min and max dates for the x-axis
final minDate = data.first.date; final minDate = data.first.date;
@@ -194,8 +192,9 @@ class FortuneGraphWidget extends HookConsumerWidget {
TextSpan( TextSpan(
text: 'checkInResultLevel$level'.tr(), text: 'checkInResultLevel$level'.tr(),
style: TextStyle( style: TextStyle(
color: color: Theme.of(
Theme.of(context).colorScheme.onSurface, context,
).colorScheme.onSurface,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
), ),
@@ -204,21 +203,19 @@ class FortuneGraphWidget extends HookConsumerWidget {
}).toList(); }).toList();
}, },
), ),
touchCallback: ( touchCallback:
FlTouchEvent event, (FlTouchEvent event, LineTouchResponse? response) {
LineTouchResponse? response, if (event is FlTapUpEvent &&
) { response != null &&
if (event is FlTapUpEvent && response.lineBarSpots != null &&
response != null && response.lineBarSpots!.isNotEmpty) {
response.lineBarSpots != null && final spot = response.lineBarSpots!.first;
response.lineBarSpots!.isNotEmpty) { final date = DateTime.fromMillisecondsSinceEpoch(
final spot = response.lineBarSpots!.first; spot.x.toInt(),
final date = DateTime.fromMillisecondsSinceEpoch( );
spot.x.toInt(), onPointSelected?.call(date);
); }
onPointSelected?.call(date); },
}
},
), ),
lineBarsData: [ lineBarsData: [
LineChartBarData( LineChartBarData(
@@ -234,8 +231,9 @@ class FortuneGraphWidget extends HookConsumerWidget {
radius: 4, radius: 4,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
strokeWidth: 2, strokeWidth: 2,
strokeColor: strokeColor: Theme.of(
Theme.of(context).colorScheme.surface, context,
).colorScheme.surface,
); );
}, },
), ),

View File

@@ -4,10 +4,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/network.dart'; import 'package:island/accounts/accounts_widgets/account/account_pfc.dart';
import 'package:island/pods/config.dart'; import 'package:island/core/network.dart';
import 'package:island/widgets/account/account_pfc.dart'; import 'package:island/core/config.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -101,7 +101,7 @@ class FriendsOverviewWidget extends HookConsumerWidget {
itemCount: onlineFriends.length, itemCount: onlineFriends.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final friend = onlineFriends[index]; final friend = onlineFriends[index];
return AccountPfcGestureDetector( return AccountPfcRegion(
uname: friend.account.name, uname: friend.account.name,
child: _FriendTile(friend: friend), child: _FriendTile(friend: friend),
); );

View File

@@ -2,10 +2,10 @@ import 'package:flutter/material.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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/screens/account/me/settings_connections.dart'; import 'package:island/accounts/account/me/settings_connections.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -2,13 +2,13 @@ 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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/account/profile.dart';
import 'package:island/pods/network.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/accounts/accounts_widgets/account/status_creation.dart';
import 'package:island/screens/account/profile.dart'; import 'package:island/core/network.dart';
import 'package:island/services/time.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/utils/activity_utils.dart'; import 'package:island/core/services/time.dart';
import 'package:island/widgets/account/status_creation.dart'; import 'package:island/core/utils/activity_utils.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -78,67 +78,61 @@ class AccountStatusCreationWidget extends HookConsumerWidget {
return InkWell( return InkWell(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: userStatus.when( child: userStatus.when(
data: data: (status) => (status?.isCustomized ?? false)
(status) => ? Padding(
(status?.isCustomized ?? false) padding: const EdgeInsets.only(left: 4),
? Padding( child: AccountStatusWidget(
padding: const EdgeInsets.only(left: 4), uname: uname,
child: AccountStatusWidget( padding: renderPadding,
uname: uname, ),
padding: renderPadding, )
), : Padding(
) padding: renderPadding,
: Padding( child: Column(
padding: renderPadding, crossAxisAlignment: CrossAxisAlignment.start,
child: Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, Row(
children: [ children: [
Row( Icon(Symbols.keyboard_arrow_up),
children: [ SizedBox(width: 4),
Icon(Symbols.keyboard_arrow_up), Text('Create Status').tr(),
SizedBox(width: 4), ],
Text('Create Status').tr(), ),
], SizedBox(height: 4),
), Text(
SizedBox(height: 4), 'Tap to set your current activity and let others know what you\'re up to',
Text( style: TextStyle(fontSize: 12),
'Tap to set your current activity and let others know what you\'re up to', ).tr().opacity(0.75),
style: TextStyle(fontSize: 12), ],
).tr().opacity(0.75), ),
], ).opacity(0.85),
), error: (error, _) => Padding(
).opacity(0.85), padding:
error: padding ?? EdgeInsets.symmetric(horizontal: 26, vertical: 12),
(error, _) => Padding( child: Row(
padding: spacing: 4,
padding ?? EdgeInsets.symmetric(horizontal: 26, vertical: 12), children: [Icon(Symbols.close), Text('Error: $error')],
child: Row( ),
spacing: 4, ).opacity(0.85),
children: [Icon(Symbols.close), Text('Error: $error')], loading: () => Padding(
), padding:
).opacity(0.85), padding ?? EdgeInsets.symmetric(horizontal: 26, vertical: 12),
loading: child: Row(
() => Padding( spacing: 4,
padding: children: [Icon(Symbols.more_vert), Text('loading').tr()],
padding ?? EdgeInsets.symmetric(horizontal: 26, vertical: 12), ),
child: Row( ).opacity(0.85),
spacing: 4,
children: [Icon(Symbols.more_vert), Text('loading').tr()],
),
).opacity(0.85),
), ),
onTap: () { onTap: () {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
useRootNavigator: true, useRootNavigator: true,
builder: builder: (context) => AccountStatusCreationSheet(
(context) => AccountStatusCreationSheet( initialStatus: (userStatus.value?.isCustomized ?? false)
initialStatus: ? userStatus.value
(userStatus.value?.isCustomized ?? false) : null,
? userStatus.value ),
: null,
),
); );
}, },
); );
@@ -156,11 +150,11 @@ class AccountStatusWidget extends HookConsumerWidget {
final localStatus = ref.watch(currentAccountStatusProvider); final localStatus = ref.watch(currentAccountStatusProvider);
final status = final status =
(uname == 'me' || (uname == 'me' ||
(userInfo.value != null && (userInfo.value != null &&
uname == userInfo.value!.name && uname == userInfo.value!.name &&
localStatus != null)) localStatus != null))
? AsyncValue.data(localStatus) ? AsyncValue.data(localStatus)
: ref.watch(accountStatusProvider(uname)); : ref.watch(accountStatusProvider(uname));
final account = ref.watch(accountProvider(uname)); final account = ref.watch(accountProvider(uname));
return Padding( return Padding(
@@ -187,17 +181,16 @@ class AccountStatusWidget extends HookConsumerWidget {
onLongPress: () { onLongPress: () {
showDialog( showDialog(
context: context, context: context,
builder: builder: (context) => AlertDialog(
(context) => AlertDialog( title: Text('Activity Details'),
title: Text('Activity Details'), content: buildActivityDetails(status.value),
content: buildActivityDetails(status.value), actions: [
actions: [ TextButton(
TextButton( onPressed: () => Navigator.of(context).pop(),
onPressed: () => Navigator.of(context).pop(), child: Text('Close'),
child: Text('Close'),
),
],
), ),
],
),
); );
}, },
child: Tooltip( child: Tooltip(
@@ -213,12 +206,11 @@ class AccountStatusWidget extends HookConsumerWidget {
) )
else else
Flexible( Flexible(
child: child: Text(
Text( (status.value?.label ?? 'offline').toLowerCase(),
(status.value?.label ?? 'offline').toLowerCase(), maxLines: 1,
maxLines: 1, overflow: TextOverflow.ellipsis,
overflow: TextOverflow.ellipsis, ).tr(),
).tr(),
), ),
if (getActivitySubtitle(status.value?.meta) != null) if (getActivitySubtitle(status.value?.meta) != null)
Flexible( Flexible(

View File

@@ -4,12 +4,12 @@ import 'package:flutter/material.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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/pods/network.dart'; import 'package:island/accounts/accounts_widgets/account/status.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/core/network.dart';
import 'package:island/widgets/account/status.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
class AccountStatusCreationSheet extends HookConsumerWidget { class AccountStatusCreationSheet extends HookConsumerWidget {
@@ -74,16 +74,16 @@ class AccountStatusCreationSheet extends HookConsumerWidget {
return SheetScaffold( return SheetScaffold(
heightFactor: 0.6, heightFactor: 0.6,
titleText: titleText: initialStatus == null
initialStatus == null ? 'statusCreate'.tr() : 'statusUpdate'.tr(), ? 'statusCreate'.tr()
: 'statusUpdate'.tr(),
actions: [ actions: [
TextButton.icon( TextButton.icon(
onPressed: onPressed: submitting.value
submitting.value ? null
? null : () {
: () { submitStatus();
submitStatus(); },
},
icon: const Icon(Symbols.upload), icon: const Icon(Symbols.upload),
label: Text(initialStatus == null ? 'create' : 'update').tr(), label: Text(initialStatus == null ? 'create' : 'update').tr(),
style: ButtonStyle( style: ButtonStyle(
@@ -116,8 +116,8 @@ class AccountStatusCreationSheet extends HookConsumerWidget {
borderRadius: BorderRadius.all(Radius.circular(12)), borderRadius: BorderRadius.all(Radius.circular(12)),
), ),
), ),
onTapOutside: onTapOutside: (_) =>
(_) => FocusManager.instance.primaryFocus?.unfocus(), FocusManager.instance.primaryFocus?.unfocus(),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
Text( Text(

View File

@@ -7,18 +7,18 @@ import 'package:flutter/material.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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/models/wallet.dart'; import 'package:island/accounts/accounts_widgets/account/account_pfc.dart';
import 'package:island/pods/network.dart'; import 'package:island/accounts/accounts_widgets/account/account_picker.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/accounts/accounts_widgets/account/restore_purchase_sheet.dart';
import 'package:island/services/time.dart'; import 'package:island/wallet/wallet_models/wallet.dart';
import 'package:island/widgets/account/account_pfc.dart'; import 'package:island/core/network.dart';
import 'package:island/widgets/account/account_picker.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/widgets/account/restore_purchase_sheet.dart'; import 'package:island/core/services/time.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/drive/drive_widgets/cloud_files.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:island/widgets/payment/payment_overlay.dart'; import 'package:island/core/widgets/payment/payment_overlay.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -1047,7 +1047,7 @@ class StellarProgramTab extends HookConsumerWidget {
), ),
), ),
if (gift.status == 2 && gift.redeemer != null) if (gift.status == 2 && gift.redeemer != null)
AccountPfcGestureDetector( AccountPfcRegion(
uname: gift.redeemer!.name, uname: gift.redeemer!.name,
child: ProfilePictureWidget( child: ProfilePictureWidget(
file: gift.redeemer!.profile.picture, file: gift.redeemer!.profile.picture,

View File

@@ -50,7 +50,7 @@ final class AccountStellarSubscriptionProvider
} }
String _$accountStellarSubscriptionHash() => String _$accountStellarSubscriptionHash() =>
r'7cdfc7ca29aac240fc8704f4493498d87f307400'; r'fd0aa9b7110e5d0ba68d8a57bd0e4dc191586e3b';
@ProviderFor(accountSentGifts) @ProviderFor(accountSentGifts)
final accountSentGiftsProvider = AccountSentGiftsFamily._(); final accountSentGiftsProvider = AccountSentGiftsFamily._();
@@ -109,7 +109,7 @@ final class AccountSentGiftsProvider
} }
} }
String _$accountSentGiftsHash() => r'460af8d22e16dc402848cb94e9b8a8a26d023c41'; String _$accountSentGiftsHash() => r'9fa99729b9efa1a74695645ee1418677b5e63027';
final class AccountSentGiftsFamily extends $Family final class AccountSentGiftsFamily extends $Family
with with
@@ -198,7 +198,7 @@ final class AccountReceivedGiftsProvider
} }
String _$accountReceivedGiftsHash() => String _$accountReceivedGiftsHash() =>
r'1208c27cca49e154af073071a197b37a2703f56d'; r'b9e9ad5e8de8916f881ceeca7f2032f344c5c58b';
final class AccountReceivedGiftsFamily extends $Family final class AccountReceivedGiftsFamily extends $Family
with with
@@ -280,7 +280,7 @@ final class AccountGiftProvider
} }
} }
String _$accountGiftHash() => r'70ca553e0b84cba9dfbee428f9bf44207138713a'; String _$accountGiftHash() => r'78890be44865accadeabdc26a96447bb3e841a5d';
final class AccountGiftFamily extends $Family final class AccountGiftFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<SnWalletGift>, String> { with $FunctionalFamilyOverride<FutureOr<SnWalletGift>, String> {

View File

@@ -1,6 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:island/models/activitypub.dart'; import 'package:island/core/models/activitypub.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
class ApActorListItem extends StatelessWidget { class ApActorListItem extends StatelessWidget {

View File

@@ -1,6 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:island/models/activitypub.dart'; import 'package:island/core/models/activitypub.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
class ActorPictureWidget extends StatelessWidget { class ActorPictureWidget extends StatelessWidget {

View File

@@ -1,6 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:island/models/activitypub.dart'; import 'package:island/core/models/activitypub.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:relative_time/relative_time.dart'; import 'package:relative_time/relative_time.dart';

View File

@@ -7,15 +7,15 @@ import 'package:flutter/material.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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/activity.dart'; import 'package:island/accounts/accounts_widgets/account/event_calendar_content.dart';
import 'package:island/models/fortune.dart'; import 'package:island/core/models/activity.dart';
import 'package:island/pods/network.dart'; import 'package:island/accounts/accounts_models/fortune.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/core/network.dart';
import 'package:island/screens/auth/captcha.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/auth/captcha.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/drive/drive_widgets/cloud_files.dart';
import 'package:island/widgets/account/event_calendar_content.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -1,5 +1,5 @@
import 'package:island/models/activity.dart'; import 'package:island/core/models/activity.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'event_calendar.g.dart'; part 'event_calendar.g.dart';

View File

@@ -1,7 +1,7 @@
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:island/utils/format.dart'; import 'package:island/core/utils/format.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
class UsageOverviewWidget extends StatelessWidget { class UsageOverviewWidget extends StatelessWidget {

View File

@@ -4,8 +4,8 @@ import 'dart:io';
import 'package:dio/dio.dart' hide Response; import 'package:dio/dio.dart' hide Response;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/activity.dart'; import 'package:island/core/models/activity.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/talker.dart'; import 'package:island/talker.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shelf/shelf.dart'; import 'package:shelf/shelf.dart';

View File

@@ -1,4 +1,4 @@
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'captcha.config.g.dart'; part 'captcha.config.g.dart';

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:island/screens/auth/captcha.config.dart'; import 'package:island/auth/captcha.config.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
class CaptchaScreen extends ConsumerWidget { class CaptchaScreen extends ConsumerWidget {
static Future<String?> show(BuildContext context) { static Future<String?> show(BuildContext context) {

View File

@@ -1,8 +1,8 @@
import 'dart:ui_web' as ui; import 'dart:ui_web' as ui;
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/config.dart'; import 'package:island/core/config.dart';
import 'package:island/screens/auth/captcha.config.dart'; import 'package:island/auth/captcha.config.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'package:web/web.dart' as web; import 'package:web/web.dart' as web;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View File

@@ -1,7 +1,7 @@
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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'create_account_content.dart'; import 'create_account_content.dart';

View File

@@ -9,14 +9,14 @@ import 'package:go_router/go_router.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:island/pods/config.dart'; import 'package:island/core/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/core/websocket.dart';
import 'package:island/services/event_bus.dart'; import 'package:island/core/services/event_bus.dart';
import 'package:island/services/notify.dart'; import 'package:island/core/services/notify.dart';
import 'package:island/services/udid.dart'; import 'package:island/core/services/udid.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
@@ -42,8 +42,9 @@ Widget getProviderIcon(String provider, {double size = 24, Color? color}) {
'assets/images/oidc/$providerLower.svg', 'assets/images/oidc/$providerLower.svg',
width: size, width: size,
height: size, height: size,
colorFilter: colorFilter: color != null
color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null, ? ColorFilter.mode(color, BlendMode.srcIn)
: null,
); );
case 'spotify': case 'spotify':
return Image.asset( return Image.asset(
@@ -844,10 +845,9 @@ class CreateAccountContent extends HookConsumerWidget {
.toString(); .toString();
final isLaunched = await launchUrlString( final isLaunched = await launchUrlString(
url, url,
mode: mode: kIsWeb
kIsWeb ? LaunchMode.platformDefault
? LaunchMode.platformDefault : LaunchMode.externalApplication,
: LaunchMode.externalApplication,
); );
if (!isLaunched) { if (!isLaunched) {
waitingForOidc.value = false; waitingForOidc.value = false;
@@ -875,10 +875,10 @@ class CreateAccountContent extends HookConsumerWidget {
value: period.value / 5, value: period.value / 5,
), ),
Expanded( Expanded(
child: child: SingleChildScrollView(
SingleChildScrollView( child: PageTransitionSwitcher(
child: PageTransitionSwitcher( transitionBuilder:
transitionBuilder: ( (
Widget child, Widget child,
Animation<double> primaryAnimation, Animation<double> primaryAnimation,
Animation<double> secondaryAnimation, Animation<double> secondaryAnimation,
@@ -893,51 +893,51 @@ class CreateAccountContent extends HookConsumerWidget {
), ),
); );
}, },
child: switch (period.value % 5) { child: switch (period.value % 5) {
1 => _CreateAccountPasswordScreen( 1 => _CreateAccountPasswordScreen(
key: const ValueKey(1), key: const ValueKey(1),
passwordController: passwordController, passwordController: passwordController,
onNext: () => period.value++, onNext: () => period.value++,
onBack: () => period.value--, onBack: () => period.value--,
onBusy: (value) => isBusy.value = value, onBusy: (value) => isBusy.value = value,
), ),
2 => _CreateAccountProfileScreen( 2 => _CreateAccountProfileScreen(
key: const ValueKey(2), key: const ValueKey(2),
usernameController: usernameController, usernameController: usernameController,
nicknameController: nicknameController, nicknameController: nicknameController,
isOidcFlow: onboardingToken.value != null, isOidcFlow: onboardingToken.value != null,
onNext: () => period.value++, onNext: () => period.value++,
onBack: () => period.value--, onBack: () => period.value--,
onBusy: (value) => isBusy.value = value, onBusy: (value) => isBusy.value = value,
), ),
3 => _CreateAccountTermsScreen( 3 => _CreateAccountTermsScreen(
key: const ValueKey(3), key: const ValueKey(3),
onNext: () => period.value++, onNext: () => period.value++,
onBack: () => period.value--, onBack: () => period.value--,
onBusy: (value) => isBusy.value = value, onBusy: (value) => isBusy.value = value,
), ),
4 => _CreateAccountCompleteScreen( 4 => _CreateAccountCompleteScreen(
key: const ValueKey(4), key: const ValueKey(4),
emailController: emailController, emailController: emailController,
passwordController: passwordController, passwordController: passwordController,
usernameController: usernameController, usernameController: usernameController,
nicknameController: nicknameController, nicknameController: nicknameController,
affiliationSpellController: affiliationSpellController, affiliationSpellController: affiliationSpellController,
onboardingToken: onboardingToken.value, onboardingToken: onboardingToken.value,
onBack: () => period.value--, onBack: () => period.value--,
onBusy: (value) => isBusy.value = value, onBusy: (value) => isBusy.value = value,
), ),
_ => _CreateAccountEmailScreen( _ => _CreateAccountEmailScreen(
key: const ValueKey(0), key: const ValueKey(0),
emailController: emailController, emailController: emailController,
affiliationSpellController: affiliationSpellController, affiliationSpellController: affiliationSpellController,
onNext: () => period.value++, onNext: () => period.value++,
onBusy: (value) => isBusy.value = value, onBusy: (value) => isBusy.value = value,
onOidc: withOidc, onOidc: withOidc,
), ),
}, },
).padding(all: 24), ).padding(all: 24),
).center(), ).center(),
), ),
const Gap(4), const Gap(4),
], ],

View File

@@ -1,7 +1,7 @@
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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'create_account_content.dart'; import 'create_account_content.dart';

View File

@@ -1,7 +1,7 @@
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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'login_content.dart'; import 'login_content.dart';

View File

@@ -9,16 +9,16 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_otp_text_field/flutter_otp_text_field.dart'; import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:island/models/auth.dart'; import 'package:island/auth/auth_models/auth.dart';
import 'package:island/pods/config.dart'; import 'package:island/core/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/core/websocket.dart';
import 'package:island/screens/account/me/settings_connections.dart'; import 'package:island/accounts/account/me/settings_connections.dart';
import 'package:island/services/event_bus.dart'; import 'package:island/core/services/event_bus.dart';
import 'package:island/services/notify.dart'; import 'package:island/core/services/notify.dart';
import 'package:island/services/udid.dart'; import 'package:island/core/services/udid.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart'; import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -274,10 +274,10 @@ class LoginContent extends HookConsumerWidget {
else else
const Gap(4), const Gap(4),
Expanded( Expanded(
child: child: SingleChildScrollView(
SingleChildScrollView( child: PageTransitionSwitcher(
child: PageTransitionSwitcher( transitionBuilder:
transitionBuilder: ( (
Widget child, Widget child,
Animation<double> primaryAnimation, Animation<double> primaryAnimation,
Animation<double> secondaryAnimation, Animation<double> secondaryAnimation,
@@ -292,41 +292,39 @@ class LoginContent extends HookConsumerWidget {
), ),
); );
}, },
child: switch (period.value % 3) { child: switch (period.value % 3) {
1 => _LoginPickerScreen( 1 => _LoginPickerScreen(
key: const ValueKey(1), key: const ValueKey(1),
challenge: currentTicket.value, challenge: currentTicket.value,
factors: factors.value, factors: factors.value,
onChallenge: onChallenge: (SnAuthChallenge? p0) =>
(SnAuthChallenge? p0) => currentTicket.value = p0, currentTicket.value = p0,
onPickFactor: onPickFactor: (SnAuthFactor p0) => factorPicked.value = p0,
(SnAuthFactor p0) => factorPicked.value = p0, onNext: () => period.value++,
onNext: () => period.value++, onBusy: (value) => isBusy.value = value,
onBusy: (value) => isBusy.value = value, ),
), 2 => _LoginCheckScreen(
2 => _LoginCheckScreen( key: const ValueKey(2),
key: const ValueKey(2), challenge: currentTicket.value,
challenge: currentTicket.value, factor: factorPicked.value,
factor: factorPicked.value, onChallenge: (SnAuthChallenge? p0) =>
onChallenge: currentTicket.value = p0,
(SnAuthChallenge? p0) => currentTicket.value = p0, onNext: () => period.value = 1,
onNext: () => period.value = 1, onBusy: (value) => isBusy.value = value,
onBusy: (value) => isBusy.value = value, ),
), _ => _LoginLookupScreen(
_ => _LoginLookupScreen( key: const ValueKey(0),
key: const ValueKey(0), ticket: currentTicket.value,
ticket: currentTicket.value, onChallenge: (SnAuthChallenge? p0) =>
onChallenge: currentTicket.value = p0,
(SnAuthChallenge? p0) => currentTicket.value = p0, onFactor: (List<SnAuthFactor>? p0) =>
onFactor: factors.value = p0 ?? [],
(List<SnAuthFactor>? p0) => onNext: () => period.value++,
factors.value = p0 ?? [], onBusy: (value) => isBusy.value = value,
onNext: () => period.value++, ),
onBusy: (value) => isBusy.value = value, },
), ).padding(all: 24),
}, ).center(),
).padding(all: 24),
).center(),
), ),
const Gap(4), const Gap(4),
@@ -388,10 +386,9 @@ class _LoginPickerScreen extends HookConsumerWidget {
try { try {
await client.post( await client.post(
'/pass/auth/challenge/${challenge!.id}/factors/${factorPicked.value!.id}', '/pass/auth/challenge/${challenge!.id}/factors/${factorPicked.value!.id}',
data: data: hintController.text.isNotEmpty
hintController.text.isNotEmpty ? jsonEncode(hintController.text)
? jsonEncode(hintController.text) : null,
: null,
); );
onPickFactor(factors!.where((x) => x == factorPicked.value).first); onPickFactor(factors!.where((x) => x == factorPicked.value).first);
onNext(); onNext();
@@ -581,17 +578,16 @@ class _LoginLookupScreen extends HookConsumerWidget {
'account': uname, 'account': uname,
'device_id': await getUdid(), 'device_id': await getUdid(),
'device_name': await getDeviceName(), 'device_name': await getDeviceName(),
'platform': 'platform': kIsWeb
kIsWeb ? 1
? 1 : switch (defaultTargetPlatform) {
: switch (defaultTargetPlatform) { TargetPlatform.iOS => 2,
TargetPlatform.iOS => 2, TargetPlatform.android => 3,
TargetPlatform.android => 3, TargetPlatform.macOS => 4,
TargetPlatform.macOS => 4, TargetPlatform.windows => 5,
TargetPlatform.windows => 5, TargetPlatform.linux => 6,
TargetPlatform.linux => 6, _ => 0,
_ => 0, },
},
}, },
); );
final result = SnAuthChallenge.fromJson(resp.data); final result = SnAuthChallenge.fromJson(resp.data);
@@ -663,18 +659,17 @@ class _LoginLookupScreen extends HookConsumerWidget {
if (token?.token != null) { if (token?.token != null) {
queryParams['token'] = token!.token; queryParams['token'] = token!.token;
} }
final url = final url = Uri.parse(
Uri.parse( '$serverUrl/pass/auth/login/${provider.toLowerCase()}',
'$serverUrl/pass/auth/login/${provider.toLowerCase()}', ).replace(queryParameters: queryParams).toString();
).replace(queryParameters: queryParams).toString();
final isLaunched = await launchUrlString( final isLaunched = await launchUrlString(
url, url,
mode: mode: kIsWeb
kIsWeb ? LaunchMode.platformDefault
? LaunchMode.platformDefault : LaunchMode.externalApplication,
: LaunchMode.externalApplication, webOnlyWindowName: token?.token != null
webOnlyWindowName: ? 'auth-${token!.token}'
token?.token != null ? 'auth-${token!.token}' : 'auth', : 'auth',
); );
if (!isLaunched) { if (!isLaunched) {
waitingForOidc.value = false; waitingForOidc.value = false;

View File

@@ -1,7 +1,7 @@
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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/core/widgets/content/sheet.dart';
import 'login_content.dart'; import 'login_content.dart';

View File

@@ -7,11 +7,11 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:island/pods/config.dart'; import 'package:island/core/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/services/udid.dart'; import 'package:island/core/services/udid.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
class OidcScreen extends ConsumerStatefulWidget { class OidcScreen extends ConsumerStatefulWidget {
@@ -70,14 +70,13 @@ class _OidcScreenState extends ConsumerState<OidcScreen> {
Expanded( Expanded(
child: InAppWebView( child: InAppWebView(
initialSettings: InAppWebViewSettings( initialSettings: InAppWebViewSettings(
userAgent: userAgent: kIsWeb
kIsWeb ? null
? null : Platform.isIOS
: Platform.isIOS ? 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1'
? 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1' : Platform.isAndroid
: Platform.isAndroid ? 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36'
? 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
), ),
initialUrlRequest: URLRequest( initialUrlRequest: URLRequest(
url: WebUri( url: WebUri(
@@ -106,32 +105,30 @@ class _OidcScreenState extends ConsumerState<OidcScreen> {
}, },
); );
}, },
shouldOverrideUrlLoading: ( shouldOverrideUrlLoading:
controller, (controller, navigationAction) async {
navigationAction, final url = navigationAction.request.url;
) async { if (url != null) {
final url = navigationAction.request.url; setState(() {
if (url != null) { currentUrl = url.toString();
setState(() { _urlController.text = currentUrl ?? '';
currentUrl = url.toString(); _isLoading = true;
_urlController.text = currentUrl ?? ''; });
_isLoading = true;
});
final path = url.path; final path = url.path;
final queryParams = url.queryParameters; final queryParams = url.queryParameters;
// Check if we're on the token page // Check if we're on the token page
if (path.endsWith('/auth/callback')) { if (path.endsWith('/auth/callback')) {
// Extract token from URL // Extract token from URL
final challenge = queryParams['challenge']; final challenge = queryParams['challenge'];
// Return the token and close the webview // Return the token and close the webview
Navigator.of(context).pop(challenge); Navigator.of(context).pop(challenge);
return NavigationActionPolicy.CANCEL; return NavigationActionPolicy.CANCEL;
} }
} }
return NavigationActionPolicy.ALLOW; return NavigationActionPolicy.ALLOW;
}, },
onUpdateVisitedHistory: (controller, url, androidIsReload) { onUpdateVisitedHistory: (controller, url, androidIsReload) {
if (url != null) { if (url != null) {
setState(() { setState(() {

View File

@@ -3,9 +3,9 @@
import 'dart:ui_web' as ui; import 'dart:ui_web' as ui;
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:island/pods/config.dart'; import 'package:island/core/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/shared/widgets/app_scaffold.dart';
import 'package:web/web.dart' as web; import 'package:web/web.dart' as web;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -38,15 +38,13 @@ class _OidcScreenState extends ConsumerState<OidcScreen> {
// Create the iframe for the OIDC login // Create the iframe for the OIDC login
final token = ref.watch(tokenProvider); final token = ref.watch(tokenProvider);
final iframe = final iframe = web.HTMLIFrameElement()
web.HTMLIFrameElement() ..src = (token?.token.isNotEmpty ?? false)
..src = ? '$serverUrl/auth/login/${widget.provider}?tk=${token!.token}'
(token?.token.isNotEmpty ?? false) : '$serverUrl/auth/login/${widget.provider}'
? '$serverUrl/auth/login/${widget.provider}?tk=${token!.token}' ..style.border = 'none'
: '$serverUrl/auth/login/${widget.provider}' ..width = '100%'
..style.border = 'none' ..height = '100%';
..width = '100%'
..height = '100%';
// Add the iframe to the document body // Add the iframe to the document body
web.document.body!.append(iframe); web.document.body!.append(iframe);
@@ -77,10 +75,9 @@ class _OidcScreenState extends ConsumerState<OidcScreen> {
appBar: AppBar( appBar: AppBar(
title: widget.title != null ? Text(widget.title!) : Text('login').tr(), title: widget.title != null ? Text(widget.title!) : Text('login').tr(),
), ),
body: body: _isInitialized
_isInitialized ? HtmlElementView(viewType: _viewType)
? HtmlElementView(viewType: _viewType) : Center(child: CircularProgressIndicator()),
: Center(child: CircularProgressIndicator()),
); );
} }
} }

View File

@@ -3,7 +3,7 @@ import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/talker.dart'; import 'package:island/talker.dart';
class WebAuthServer { class WebAuthServer {

View File

@@ -1,7 +1,7 @@
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/models/file.dart'; import 'package:island/drive/drive_models/file.dart';
import 'package:island/models/realm.dart'; import 'package:island/realms/realms_models/realm.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
part 'chat.freezed.dart'; part 'chat.freezed.dart';
part 'chat.g.dart'; part 'chat.g.dart';

View File

@@ -1,8 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/core/websocket.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
part 'chat_online_count.g.dart'; part 'chat_online_count.g.dart';

View File

@@ -3,13 +3,13 @@ import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/chat/chat_widgets/call_button.dart';
import 'package:island/widgets/chat/call_button.dart'; import 'package:island/shared/widgets/alert.dart';
import 'package:livekit_client/livekit_client.dart' as lk; import 'package:livekit_client/livekit_client.dart' as lk;
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/models/chat.dart'; import 'package:island/chat/chat_models/chat.dart';
import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:island/talker.dart'; import 'package:island/talker.dart';

View File

@@ -0,0 +1,50 @@
import 'dart:async';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:island/core/network.dart';
import 'package:island/core/websocket.dart';
import 'package:island/accounts/accounts_models/account.dart';
part 'chat_online_count.g.dart';
@riverpod
class ChatOnlineCountNotifier extends _$ChatOnlineCountNotifier {
@override
Future<int> build(String chatroomId) async {
final apiClient = ref.watch(apiClientProvider);
final ws = ref.watch(websocketProvider);
// Fetch initial online count
final response = await apiClient.get(
'/messager/chat/$chatroomId/members/online',
);
final initialCount = response.data as int;
// Listen for websocket status updates
final subscription = ws.dataStream.listen((WebSocketPacket packet) {
if (packet.type == 'accounts.status.update') {
final data = packet.data;
if (data != null && data['chat_room_id'] == chatroomId) {
final status = SnAccountStatus.fromJson(data['status']);
var delta = status.isOnline ? 1 : -1;
if (status.clearedAt != null &&
status.clearedAt!.isBefore(DateTime.now())) {
if (status.isInvisible) delta = 1;
}
// Update count based on online status
state.whenData((currentCount) {
final newCount = currentCount + delta;
state = AsyncData(
newCount.clamp(0, double.infinity).toInt(),
); // Ensure non-negative
});
}
}
});
ref.onDispose(() {
subscription.cancel();
});
return initialCount;
}
}

View File

@@ -0,0 +1,101 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'chat_online_count.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(ChatOnlineCountNotifier)
final chatOnlineCountProvider = ChatOnlineCountNotifierFamily._();
final class ChatOnlineCountNotifierProvider
extends $AsyncNotifierProvider<ChatOnlineCountNotifier, int> {
ChatOnlineCountNotifierProvider._({
required ChatOnlineCountNotifierFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'chatOnlineCountProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$chatOnlineCountNotifierHash();
@override
String toString() {
return r'chatOnlineCountProvider'
''
'($argument)';
}
@$internal
@override
ChatOnlineCountNotifier create() => ChatOnlineCountNotifier();
@override
bool operator ==(Object other) {
return other is ChatOnlineCountNotifierProvider &&
other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$chatOnlineCountNotifierHash() =>
r'b2f9f17bfece1937ec90590b8f11db2bec923156';
final class ChatOnlineCountNotifierFamily extends $Family
with
$ClassFamilyOverride<
ChatOnlineCountNotifier,
AsyncValue<int>,
int,
FutureOr<int>,
String
> {
ChatOnlineCountNotifierFamily._()
: super(
retry: null,
name: r'chatOnlineCountProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
ChatOnlineCountNotifierProvider call(String chatroomId) =>
ChatOnlineCountNotifierProvider._(argument: chatroomId, from: this);
@override
String toString() => r'chatOnlineCountProvider';
}
abstract class _$ChatOnlineCountNotifier extends $AsyncNotifier<int> {
late final _$args = ref.$arg as String;
String get chatroomId => _$args;
FutureOr<int> build(String chatroomId);
@$mustCallSuper
@override
void runBuild() {
final ref = this.ref as $Ref<AsyncValue<int>, int>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<int>, int>,
AsyncValue<int>,
Object?,
Object?
>;
element.handleCreate(ref, () => build(_$args));
}
}

View File

@@ -1,13 +1,13 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/database/drift_db.dart'; import 'package:island/data/drift_db.dart';
import 'package:island/models/account.dart'; import 'package:island/accounts/accounts_models/account.dart';
import 'package:island/models/chat.dart'; import 'package:island/chat/chat_models/chat.dart';
import 'package:island/models/file.dart'; import 'package:island/drive/drive_models/file.dart';
import 'package:island/models/realm.dart'; import 'package:island/realms/realms_models/realm.dart';
import 'package:island/pods/database.dart'; import 'package:island/core/database.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/accounts/accounts_pod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'chat_room.g.dart'; part 'chat_room.g.dart';

View File

@@ -2,15 +2,15 @@ import "dart:async";
import "dart:convert"; import "dart:convert";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:island/chat/chat_widgets/call_button.dart";
import "package:island/chat/messages_notifier.dart";
import "package:just_audio/just_audio.dart"; import "package:just_audio/just_audio.dart";
import "package:island/pods/config.dart"; import "package:island/core/config.dart";
import "package:island/models/chat.dart"; import "package:island/chat/chat_models/chat.dart";
import "package:island/pods/chat/chat_room.dart"; import "package:island/chat/chat_pod/chat_room.dart";
import "package:island/pods/lifecycle.dart"; import "package:island/core/lifecycle.dart";
import "package:island/pods/chat/messages_notifier.dart"; import "package:island/core/websocket.dart";
import "package:island/pods/websocket.dart";
import "package:island/talker.dart"; import "package:island/talker.dart";
import "package:island/widgets/chat/call_button.dart";
import "package:riverpod_annotation/riverpod_annotation.dart"; import "package:riverpod_annotation/riverpod_annotation.dart";
part 'chat_subscribe.g.dart'; part 'chat_subscribe.g.dart';

View File

@@ -1,10 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:island/models/chat.dart'; import 'package:island/chat/chat_models/chat.dart';
import 'package:island/pods/network.dart'; import 'package:island/core/network.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/core/websocket.dart';
import 'package:island/pods/chat/chat_subscribe.dart'; import 'package:island/chat/chat_pod/chat_subscribe.dart';
part 'chat_summary.g.dart'; part 'chat_summary.g.dart';

Some files were not shown because too many files have changed in this diff Show More