🎨 Use feature based folder structure
This commit is contained in:
@@ -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);
|
||||||
@@ -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';
|
||||||
@@ -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 =
|
||||||
@@ -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';
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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(
|
||||||
@@ -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(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -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,
|
||||||
@@ -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';
|
||||||
@@ -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),
|
||||||
),
|
),
|
||||||
@@ -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';
|
||||||
@@ -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';
|
||||||
@@ -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
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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) {
|
||||||
@@ -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';
|
||||||
618
lib/accounts/accounts_widgets/account/activity_presence.dart
Normal file
618
lib/accounts/accounts_widgets/account/activity_presence.dart
Normal 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')),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -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,
|
||||||
@@ -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),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -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),
|
||||||
);
|
);
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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(
|
||||||
@@ -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(
|
||||||
@@ -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,
|
||||||
@@ -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> {
|
||||||
@@ -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 {
|
||||||
@@ -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 {
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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';
|
||||||
@@ -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';
|
||||||
@@ -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 {
|
||||||
@@ -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';
|
||||||
@@ -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';
|
||||||
@@ -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) {
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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),
|
||||||
],
|
],
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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';
|
||||||
@@ -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;
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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(() {
|
||||||
@@ -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()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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 {
|
||||||
@@ -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';
|
||||||
@@ -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';
|
||||||
|
|
||||||
@@ -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';
|
||||||
|
|
||||||
50
lib/chat/chat_pod/chat_online_count.dart
Normal file
50
lib/chat/chat_pod/chat_online_count.dart
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
101
lib/chat/chat_pod/chat_online_count.g.dart
Normal file
101
lib/chat/chat_pod/chat_online_count.g.dart
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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';
|
||||||
@@ -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';
|
||||||
@@ -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
Reference in New Issue
Block a user