Compare commits
4 Commits
3395f3dbd0
...
8bb62b5992
Author | SHA1 | Date | |
---|---|---|---|
8bb62b5992 | |||
1e8a6dea5b | |||
5c2804cc4d | |||
0dbb8f132a |
@ -106,6 +106,8 @@
|
|||||||
},
|
},
|
||||||
"loginEnterPassword": "Enter the code",
|
"loginEnterPassword": "Enter the code",
|
||||||
"loginSuccess": "Logged in as {}",
|
"loginSuccess": "Logged in as {}",
|
||||||
|
"authFactorDelete": "Delete Auth Factor",
|
||||||
|
"authFactorDeleteDescription": "Are you sure you want delete auth factor {}?",
|
||||||
"authFactorPassword": "Password",
|
"authFactorPassword": "Password",
|
||||||
"authFactorPasswordDescription": "The password you set when you registered.",
|
"authFactorPasswordDescription": "The password you set when you registered.",
|
||||||
"authFactorEmail": "Email verification code",
|
"authFactorEmail": "Email verification code",
|
||||||
@ -579,5 +581,8 @@
|
|||||||
"newsReadingFromReader": "You're reading from HyperNet.Reader",
|
"newsReadingFromReader": "You're reading from HyperNet.Reader",
|
||||||
"newsReadingFromOriginal": "You're reading the original article",
|
"newsReadingFromOriginal": "You're reading the original article",
|
||||||
"newsDisclaimer": "This article is fetched from the Internet, we do not guarantee its authenticity, please judge for yourself. All content in this article belongs to the original author.",
|
"newsDisclaimer": "This article is fetched from the Internet, we do not guarantee its authenticity, please judge for yourself. All content in this article belongs to the original author.",
|
||||||
"newsToday": "Today's News"
|
"newsToday": "Today's News",
|
||||||
|
"totpPostSetup": "One More Thing",
|
||||||
|
"totpPostSetupDescription": "Scan the QR Code below with Google Authenticator, Microsoft Authenticator, 1Password, Authy, Bitwarden or any of kind of authenticator app which supports TOTP.",
|
||||||
|
"totpNeverShare": "Never share this QR Code"
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,8 @@
|
|||||||
},
|
},
|
||||||
"loginEnterPassword": "验证代码",
|
"loginEnterPassword": "验证代码",
|
||||||
"loginSuccess": "登录为 {}",
|
"loginSuccess": "登录为 {}",
|
||||||
|
"authFactorDelete": "删除验证因子",
|
||||||
|
"authFactorDeleteDescription": "你确定要删除 {} 验证因子吗?",
|
||||||
"authFactorPassword": "密码",
|
"authFactorPassword": "密码",
|
||||||
"authFactorPasswordDescription": "注册时选择设置的密码。",
|
"authFactorPasswordDescription": "注册时选择设置的密码。",
|
||||||
"authFactorEmail": "电邮一次性验证码",
|
"authFactorEmail": "电邮一次性验证码",
|
||||||
@ -576,5 +578,8 @@
|
|||||||
"newsReadingFromReader": "你正在从 HyperNet.Reader 阅读文章",
|
"newsReadingFromReader": "你正在从 HyperNet.Reader 阅读文章",
|
||||||
"newsReadingFromOriginal": "你正在阅读原始文章",
|
"newsReadingFromOriginal": "你正在阅读原始文章",
|
||||||
"newsDisclaimer": "本文由 HyperNet.Reader 从互联网上获取,我们不担保其内容的真实性,请自行判断。本文章的所有内容版权归原作者所有。",
|
"newsDisclaimer": "本文由 HyperNet.Reader 从互联网上获取,我们不担保其内容的真实性,请自行判断。本文章的所有内容版权归原作者所有。",
|
||||||
"newsToday": "快讯"
|
"newsToday": "快讯",
|
||||||
|
"totpPostSetup": "还有一件事",
|
||||||
|
"totpPostSetupDescription": "使用 Google Authenticator, Microsoft Authenticator, 1Password, Authy, Bitwarden 或其他支持 TOTP 的验证器扫描本 QR Code 来添加。",
|
||||||
|
"totpNeverShare": "永远不要分享这个 QR Code"
|
||||||
}
|
}
|
||||||
|
@ -53,14 +53,14 @@ PODS:
|
|||||||
- Firebase/Messaging (11.6.0):
|
- Firebase/Messaging (11.6.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseMessaging (~> 11.6.0)
|
- FirebaseMessaging (~> 11.6.0)
|
||||||
- firebase_analytics (11.4.0):
|
- firebase_analytics (11.4.1):
|
||||||
- Firebase/Analytics (= 11.6.0)
|
- Firebase/Analytics (= 11.6.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_core (3.10.0):
|
- firebase_core (3.10.1):
|
||||||
- Firebase/CoreOnly (= 11.6.0)
|
- Firebase/CoreOnly (= 11.6.0)
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_messaging (15.2.0):
|
- firebase_messaging (15.2.1):
|
||||||
- Firebase/Messaging (= 11.6.0)
|
- Firebase/Messaging (= 11.6.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
@ -382,9 +382,9 @@ SPEC CHECKSUMS:
|
|||||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
||||||
Firebase: 374a441a91ead896215703a674d58cdb3e9d772b
|
Firebase: 374a441a91ead896215703a674d58cdb3e9d772b
|
||||||
firebase_analytics: 07bd7cfbac54bfcdccf2bb2530f9a65486f7ef3f
|
firebase_analytics: 13ea4ad8a42c5060bad7e6694304dabb8b02fe7e
|
||||||
firebase_core: feb37e79f775c2bd08dd35e02d83678291317e10
|
firebase_core: e2aa06dbd854d961f8ce46c2e20933bee1bf2d2b
|
||||||
firebase_messaging: e2f0ba891b1509668c07f5099761518a5af8fe3c
|
firebase_messaging: 96cf6d67121b3f39746b2a4f29a26c0eee4af70e
|
||||||
FirebaseAnalytics: 7114c698cac995602e3b1b96663473e50d54d6e7
|
FirebaseAnalytics: 7114c698cac995602e3b1b96663473e50d54d6e7
|
||||||
FirebaseCore: 48b0dd707581cf9c1a1220da68223fb0a562afaa
|
FirebaseCore: 48b0dd707581cf9c1a1220da68223fb0a562afaa
|
||||||
FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
|
FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
import 'package:dropdown_button2/dropdown_button2.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:gap/gap.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/auth.dart';
|
import 'package:surface/types/auth.dart';
|
||||||
@ -10,7 +12,7 @@ import 'package:surface/widgets/dialog.dart';
|
|||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
|
|
||||||
final Map<int, (String, String, IconData)> _kFactorTypes = {
|
final Map<int, (String, String, IconData)> kFactorTypes = {
|
||||||
0: ('authFactorPassword', 'authFactorPasswordDescription', Symbols.password),
|
0: ('authFactorPassword', 'authFactorPasswordDescription', Symbols.password),
|
||||||
1: ('authFactorEmail', 'authFactorEmailDescription', Symbols.email),
|
1: ('authFactorEmail', 'authFactorEmailDescription', Symbols.email),
|
||||||
2: ('authFactorTOTP', 'authFactorTOTPDescription', Symbols.timer),
|
2: ('authFactorTOTP', 'authFactorTOTPDescription', Symbols.timer),
|
||||||
@ -25,10 +27,12 @@ class FactorSettingsScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
|
class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
|
||||||
|
bool _isBusy = false;
|
||||||
List<SnAuthFactor>? _factors;
|
List<SnAuthFactor>? _factors;
|
||||||
|
|
||||||
Future<void> _fetchFactors() async {
|
Future<void> _fetchFactors() async {
|
||||||
try {
|
try {
|
||||||
|
setState(() => _isBusy = true);
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final resp = await sn.client.get('/cgi/id/users/me/factors');
|
final resp = await sn.client.get('/cgi/id/users/me/factors');
|
||||||
_factors = List<SnAuthFactor>.from(
|
_factors = List<SnAuthFactor>.from(
|
||||||
@ -38,7 +42,7 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
} finally {
|
} finally {
|
||||||
setState(() {});
|
setState(() => _isBusy = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +63,7 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
LoadingIndicator(
|
LoadingIndicator(
|
||||||
isActive: _factors == null,
|
isActive: _isBusy,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('authFactorAdd').tr(),
|
title: Text('authFactorAdd').tr(),
|
||||||
@ -90,10 +94,34 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
|
|||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
final ele = _factors![idx];
|
final ele = _factors![idx];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(_kFactorTypes[ele.type]!.$1).tr(),
|
title: Text(kFactorTypes[ele.type]!.$1).tr(),
|
||||||
subtitle: Text(_kFactorTypes[ele.type]!.$2).tr(),
|
subtitle: Text(kFactorTypes[ele.type]!.$2).tr(),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.only(left: 24, right: 12),
|
||||||
leading: Icon(_kFactorTypes[ele.type]!.$3),
|
leading: Icon(kFactorTypes[ele.type]!.$3),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Symbols.close),
|
||||||
|
onPressed: ele.type > 0
|
||||||
|
? () {
|
||||||
|
context
|
||||||
|
.showConfirmDialog(
|
||||||
|
'authFactorDelete'.tr(),
|
||||||
|
'authFactorDeleteDescription'.tr(args: [kFactorTypes[ele.type]!.$1.tr()]),
|
||||||
|
)
|
||||||
|
.then((val) async {
|
||||||
|
if (!val) return;
|
||||||
|
try {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
await sn.client.delete('/cgi/id/users/me/factors/${ele.id}');
|
||||||
|
_fetchFactors();
|
||||||
|
} catch (err) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -126,7 +154,14 @@ class _FactorNewDialogState extends State<_FactorNewDialog> {
|
|||||||
final resp = await sn.client.post('/cgi/id/users/me/factors', data: {
|
final resp = await sn.client.post('/cgi/id/users/me/factors', data: {
|
||||||
'type': _factorType,
|
'type': _factorType,
|
||||||
});
|
});
|
||||||
// TODO show qrcode when creating totp factor
|
final factor = SnAuthFactor.fromJson(resp.data);
|
||||||
|
if (!mounted) return;
|
||||||
|
if (factor.type == 2) {
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => _FactorTotpFactorDialog(factor: factor),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -154,7 +189,7 @@ class _FactorNewDialogState extends State<_FactorNewDialog> {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
value: _factorType,
|
value: _factorType,
|
||||||
items: _kFactorTypes.entries.map(
|
items: kFactorTypes.entries.map(
|
||||||
(ele) {
|
(ele) {
|
||||||
final contains = widget.currentlyHave.map((ele) => ele.type).contains(ele.key);
|
final contains = widget.currentlyHave.map((ele) => ele.type).contains(ele.key);
|
||||||
return DropdownMenuItem<int>(
|
return DropdownMenuItem<int>(
|
||||||
@ -199,3 +234,61 @@ class _FactorNewDialogState extends State<_FactorNewDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _FactorTotpFactorDialog extends StatelessWidget {
|
||||||
|
final SnAuthFactor factor;
|
||||||
|
|
||||||
|
const _FactorTotpFactorDialog({super.key, required this.factor});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
'totpPostSetup',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
).tr().width(280),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
'totpPostSetupDescription',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
).tr().width(280),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
QrImageView(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
data: factor.config!['url'],
|
||||||
|
errorCorrectionLevel: QrErrorCorrectLevel.H,
|
||||||
|
version: QrVersions.auto,
|
||||||
|
size: 160,
|
||||||
|
gapless: true,
|
||||||
|
eyeStyle: QrEyeStyle(
|
||||||
|
eyeShape: QrEyeShape.circle,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
dataModuleStyle: QrDataModuleStyle(
|
||||||
|
dataModuleShape: QrDataModuleShape.square,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
'totpNeverShare',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
).tr().bold().width(280),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
|
import 'package:surface/screens/account/factor_settings.dart';
|
||||||
import 'package:surface/types/auth.dart';
|
import 'package:surface/types/auth.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
@ -14,11 +15,6 @@ import 'package:url_launcher/url_launcher_string.dart';
|
|||||||
|
|
||||||
import '../../providers/websocket.dart';
|
import '../../providers/websocket.dart';
|
||||||
|
|
||||||
final Map<int, (String label, IconData icon, bool isOtp)> _factorLabelMap = {
|
|
||||||
0: ('authFactorPassword'.tr(), Symbols.password, false),
|
|
||||||
1: ('authFactorEmail'.tr(), Symbols.email, true),
|
|
||||||
};
|
|
||||||
|
|
||||||
class LoginScreen extends StatefulWidget {
|
class LoginScreen extends StatefulWidget {
|
||||||
const LoginScreen({super.key});
|
const LoginScreen({super.key});
|
||||||
|
|
||||||
@ -212,7 +208,9 @@ class _LoginCheckScreenState extends State<_LoginCheckScreen> {
|
|||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
autofillHints: [
|
autofillHints: [
|
||||||
(_factorLabelMap[widget.factor!.type]?.$3 ?? true) ? AutofillHints.password : AutofillHints.oneTimeCode
|
widget.factor!.type == 0
|
||||||
|
? AutofillHints.password
|
||||||
|
: AutofillHints.oneTimeCode
|
||||||
],
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
isDense: true,
|
isDense: true,
|
||||||
@ -267,7 +265,8 @@ class _LoginPickerScreenState extends State<_LoginPickerScreen> {
|
|||||||
bool _isBusy = false;
|
bool _isBusy = false;
|
||||||
int? _factorPicked;
|
int? _factorPicked;
|
||||||
|
|
||||||
Color get _unFocusColor => Theme.of(context).colorScheme.onSurface.withAlpha((255 * 0.75).round());
|
Color get _unFocusColor =>
|
||||||
|
Theme.of(context).colorScheme.onSurface.withAlpha((255 * 0.75).round());
|
||||||
|
|
||||||
void _performGetFactorCode() async {
|
void _performGetFactorCode() async {
|
||||||
if (_factorPicked == null) return;
|
if (_factorPicked == null) return;
|
||||||
@ -328,11 +327,11 @@ class _LoginPickerScreenState extends State<_LoginPickerScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
secondary: Icon(
|
secondary: Icon(
|
||||||
_factorLabelMap[x.type]?.$2 ?? Symbols.question_mark,
|
kFactorTypes[x.type]?.$3 ?? Symbols.question_mark,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
_factorLabelMap[x.type]?.$1 ?? 'unknown'.tr(),
|
kFactorTypes[x.type]?.$1 ?? 'unknown',
|
||||||
),
|
).tr(),
|
||||||
enabled: !widget.ticket!.factorTrail.contains(x.id),
|
enabled: !widget.ticket!.factorTrail.contains(x.id),
|
||||||
value: _factorPicked == x.id,
|
value: _factorPicked == x.id,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -408,11 +407,13 @@ class _LoginLookupScreenState extends State<_LoginLookupScreen> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final lookupResp = await sn.client.get('/cgi/id/users/lookup?probe=$username');
|
final lookupResp =
|
||||||
|
await sn.client.get('/cgi/id/users/lookup?probe=$username');
|
||||||
await sn.client.post('/cgi/id/users/me/password-reset', data: {
|
await sn.client.post('/cgi/id/users/me/password-reset', data: {
|
||||||
'user_id': lookupResp.data['id'],
|
'user_id': lookupResp.data['id'],
|
||||||
});
|
});
|
||||||
if (mounted) context.showModalDialog('done'.tr(), 'signinResetPasswordSent'.tr());
|
if (mounted)
|
||||||
|
context.showModalDialog('done'.tr(), 'signinResetPasswordSent'.tr());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (mounted) context.showErrorDialog(err);
|
if (mounted) context.showErrorDialog(err);
|
||||||
} finally {
|
} finally {
|
||||||
@ -437,7 +438,8 @@ class _LoginLookupScreenState extends State<_LoginLookupScreen> {
|
|||||||
widget.onTicket(result.ticket);
|
widget.onTicket(result.ticket);
|
||||||
|
|
||||||
// Pull factors
|
// Pull factors
|
||||||
final factorResp = await sn.client.get('/cgi/id/auth/factors', queryParameters: {
|
final factorResp =
|
||||||
|
await sn.client.get('/cgi/id/auth/factors', queryParameters: {
|
||||||
'ticketId': result.ticket!.id.toString(),
|
'ticketId': result.ticket!.id.toString(),
|
||||||
});
|
});
|
||||||
widget.onFactor(
|
widget.onFactor(
|
||||||
@ -531,7 +533,10 @@ class _LoginLookupScreenState extends State<_LoginLookupScreen> {
|
|||||||
'termAcceptNextWithAgree'.tr(),
|
'termAcceptNextWithAgree'.tr(),
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
color: Theme.of(context).colorScheme.onSurface.withAlpha((255 * 0.75).round()),
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSurface
|
||||||
|
.withAlpha((255 * 0.75).round()),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Material(
|
Material(
|
||||||
|
@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/post.dart';
|
import 'package:surface/providers/post.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/screens/post/post_detail.dart';
|
import 'package:surface/screens/post/post_detail.dart';
|
||||||
@ -96,6 +97,8 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final cfg = context.read<ConfigProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
floatingActionButtonLocation: ExpandableFab.location,
|
floatingActionButtonLocation: ExpandableFab.location,
|
||||||
floatingActionButton: ExpandableFab(
|
floatingActionButton: ExpandableFab(
|
||||||
@ -243,8 +246,10 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
),
|
),
|
||||||
openColor: Colors.transparent,
|
openColor: Colors.transparent,
|
||||||
openElevation: 0,
|
openElevation: 0,
|
||||||
closedColor: Theme.of(context).colorScheme.surfaceContainerLow.withOpacity(0.75),
|
|
||||||
transitionType: ContainerTransitionType.fade,
|
transitionType: ContainerTransitionType.fade,
|
||||||
|
closedColor: Theme.of(context).colorScheme.surfaceContainerLow.withOpacity(
|
||||||
|
cfg.prefs.getBool(kAppBackgroundStoreKey) == true ? 0.75 : 1,
|
||||||
|
),
|
||||||
closedShape: const RoundedRectangleBorder(
|
closedShape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
),
|
),
|
||||||
|
@ -51,7 +51,7 @@ class HomeScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _HomeScreenState extends State<HomeScreen> {
|
class _HomeScreenState extends State<HomeScreen> {
|
||||||
static const List<HomeScreenDashEntry> kCards = [
|
late final List<HomeScreenDashEntry> kCards = [
|
||||||
HomeScreenDashEntry(
|
HomeScreenDashEntry(
|
||||||
name: 'dashEntryRecommendation',
|
name: 'dashEntryRecommendation',
|
||||||
child: _HomeDashRecommendationPostWidget(),
|
child: _HomeDashRecommendationPostWidget(),
|
||||||
@ -69,7 +69,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
HomeScreenDashEntry(
|
HomeScreenDashEntry(
|
||||||
name: 'dashEntryTodayNews',
|
name: 'dashEntryTodayNews',
|
||||||
child: _HomeDashTodayNews(),
|
child: _HomeDashTodayNews(),
|
||||||
cols: 2,
|
cols: MediaQuery.of(context).size.width >= 640 ? 3 : 2,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ class _HomeDashTodayNewsState extends State<_HomeDashTodayNews> {
|
|||||||
Text(
|
Text(
|
||||||
_article!.title,
|
_article!.title,
|
||||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 18),
|
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 18),
|
||||||
maxLines: 2,
|
maxLines: MediaQuery.of(context).size.width >= 640 ? 2 : 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
|
@ -175,54 +175,57 @@ class _NewsDetailScreenState extends State<NewsDetailScreen> {
|
|||||||
),
|
),
|
||||||
if (_articleFragment != null && _isReadingFromReader)
|
if (_articleFragment != null && _isReadingFromReader)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: Container(
|
||||||
child: Column(
|
constraints: BoxConstraints(maxWidth: 640),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: SingleChildScrollView(
|
||||||
spacing: 8,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Text(_article!.title, style: Theme.of(context).textTheme.titleLarge),
|
spacing: 8,
|
||||||
Builder(builder: (context) {
|
children: [
|
||||||
final htmlDescription = parse(_article!.description);
|
Text(_article!.title, style: Theme.of(context).textTheme.titleLarge),
|
||||||
return Text(
|
Builder(builder: (context) {
|
||||||
htmlDescription.children.map((ele) => ele.text.trim()).join(),
|
final htmlDescription = parse(_article!.description);
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
return Text(
|
||||||
);
|
htmlDescription.children.map((ele) => ele.text.trim()).join(),
|
||||||
}),
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
Builder(builder: (context) {
|
);
|
||||||
final date = _article!.publishedAt ?? _article!.createdAt;
|
}),
|
||||||
return Row(
|
Builder(builder: (context) {
|
||||||
spacing: 2,
|
final date = _article!.publishedAt ?? _article!.createdAt;
|
||||||
children: [
|
return Row(
|
||||||
Text(DateFormat().format(date)).textStyle(Theme.of(context).textTheme.bodySmall!),
|
spacing: 2,
|
||||||
Text(' · ').textStyle(Theme.of(context).textTheme.bodySmall!).bold(),
|
children: [
|
||||||
Text(RelativeTime(context).format(date)).textStyle(Theme.of(context).textTheme.bodySmall!),
|
Text(DateFormat().format(date)).textStyle(Theme.of(context).textTheme.bodySmall!),
|
||||||
],
|
Text(' · ').textStyle(Theme.of(context).textTheme.bodySmall!).bold(),
|
||||||
).opacity(0.75);
|
Text(RelativeTime(context).format(date)).textStyle(Theme.of(context).textTheme.bodySmall!),
|
||||||
}),
|
],
|
||||||
Text('newsDisclaimer').tr().textStyle(Theme.of(context).textTheme.bodySmall!).opacity(0.75),
|
).opacity(0.75);
|
||||||
const Divider(),
|
}),
|
||||||
..._parseHtmlToWidgets(_articleFragment!.children),
|
Text('newsDisclaimer').tr().textStyle(Theme.of(context).textTheme.bodySmall!).opacity(0.75),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
InkWell(
|
..._parseHtmlToWidgets(_articleFragment!.children),
|
||||||
child: Row(
|
const Divider(),
|
||||||
mainAxisSize: MainAxisSize.min,
|
InkWell(
|
||||||
children: [
|
child: Row(
|
||||||
Text(
|
mainAxisSize: MainAxisSize.min,
|
||||||
'Reference from original website',
|
children: [
|
||||||
style: TextStyle(decoration: TextDecoration.underline),
|
Text(
|
||||||
),
|
'Reference from original website',
|
||||||
const Gap(4),
|
style: TextStyle(decoration: TextDecoration.underline),
|
||||||
Icon(Icons.launch, size: 16),
|
),
|
||||||
],
|
const Gap(4),
|
||||||
).opacity(0.85),
|
Icon(Icons.launch, size: 16),
|
||||||
onTap: () {
|
],
|
||||||
launchUrlString(_article!.url);
|
).opacity(0.85),
|
||||||
},
|
onTap: () {
|
||||||
),
|
launchUrlString(_article!.url);
|
||||||
Gap(MediaQuery.of(context).padding.bottom),
|
},
|
||||||
],
|
),
|
||||||
).padding(horizontal: 12, vertical: 16),
|
Gap(MediaQuery.of(context).padding.bottom),
|
||||||
),
|
],
|
||||||
|
).padding(horizontal: 12, vertical: 16),
|
||||||
|
),
|
||||||
|
).center(),
|
||||||
)
|
)
|
||||||
else if (_article != null)
|
else if (_article != null)
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -70,11 +70,16 @@ class _NewsScreenState extends State<NewsScreen> {
|
|||||||
sliver: SliverAppBar(
|
sliver: SliverAppBar(
|
||||||
leading: AutoAppBarLeading(),
|
leading: AutoAppBarLeading(),
|
||||||
title: Text('screenNews').tr(),
|
title: Text('screenNews').tr(),
|
||||||
|
floating: true,
|
||||||
|
snap: true,
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
tabs: [
|
tabs: [
|
||||||
Tab(child: Text('newsAllSources'.tr())),
|
Tab(child: Text('newsAllSources'.tr()).textColor(Theme.of(context).appBarTheme.foregroundColor)),
|
||||||
for (final source in _sources!) Tab(child: Text(source.label)),
|
for (final source in _sources!)
|
||||||
|
Tab(
|
||||||
|
child: Text(source.label).textColor(Theme.of(context).appBarTheme.foregroundColor),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -146,80 +151,87 @@ class _NewsArticleListWidgetState extends State<_NewsArticleListWidget> {
|
|||||||
return MediaQuery.removePadding(
|
return MediaQuery.removePadding(
|
||||||
context: context,
|
context: context,
|
||||||
removeTop: true,
|
removeTop: true,
|
||||||
child: RefreshIndicator(
|
child: Center(
|
||||||
onRefresh: _fetchArticles,
|
child: Container(
|
||||||
child: InfiniteList(
|
constraints: BoxConstraints(maxWidth: 640),
|
||||||
isLoading: _isBusy,
|
child: RefreshIndicator(
|
||||||
itemCount: _articles.length,
|
onRefresh: _fetchArticles,
|
||||||
hasReachedMax: _totalCount != null && _articles.length >= _totalCount!,
|
child: InfiniteList(
|
||||||
onFetchData: () {
|
isLoading: _isBusy,
|
||||||
_fetchArticles();
|
itemCount: _articles.length,
|
||||||
},
|
hasReachedMax: _totalCount != null && _articles.length >= _totalCount!,
|
||||||
itemBuilder: (context, index) {
|
onFetchData: () {
|
||||||
final article = _articles[index];
|
_fetchArticles();
|
||||||
|
},
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final article = _articles[index];
|
||||||
|
|
||||||
final baseUri = Uri.parse(article.url);
|
final baseUri = Uri.parse(article.url);
|
||||||
final baseUrl = '${baseUri.scheme}://${baseUri.host}';
|
final baseUrl = '${baseUri.scheme}://${baseUri.host}';
|
||||||
|
|
||||||
final htmlDescription = parse(article.description);
|
final htmlDescription = parse(article.description);
|
||||||
final date = article.publishedAt ?? article.createdAt;
|
final date = article.publishedAt ?? article.createdAt;
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
radius: 8,
|
radius: 8,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'newsDetail',
|
'newsDetail',
|
||||||
pathParameters: {'hash': article.hash},
|
pathParameters: {'hash': article.hash},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (article.thumbnail.isNotEmpty && !article.thumbnail.endsWith('.svg'))
|
if (article.thumbnail.isNotEmpty && !article.thumbnail.endsWith('.svg'))
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topRight: Radius.circular(8),
|
topRight: Radius.circular(8),
|
||||||
topLeft: Radius.circular(8),
|
topLeft: Radius.circular(8),
|
||||||
),
|
),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
child: AutoResizeUniversalImage(
|
child: AutoResizeUniversalImage(
|
||||||
article.thumbnail.startsWith('http') ? article.thumbnail : '$baseUrl/${article.thumbnail}',
|
article.thumbnail.startsWith('http')
|
||||||
|
? article.thumbnail
|
||||||
|
: '$baseUrl/${article.thumbnail}',
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const Gap(16),
|
||||||
),
|
Text(article.title).textStyle(Theme.of(context).textTheme.titleLarge!).padding(horizontal: 16),
|
||||||
const Gap(16),
|
const Gap(8),
|
||||||
Text(article.title).textStyle(Theme.of(context).textTheme.titleLarge!).padding(horizontal: 16),
|
Text(htmlDescription.children.map((ele) => ele.text.trim()).join())
|
||||||
const Gap(8),
|
.textStyle(Theme.of(context).textTheme.bodyMedium!)
|
||||||
Text(htmlDescription.children.map((ele) => ele.text.trim()).join())
|
.padding(horizontal: 16),
|
||||||
.textStyle(Theme.of(context).textTheme.bodyMedium!)
|
const Gap(8),
|
||||||
.padding(horizontal: 16),
|
Row(
|
||||||
const Gap(8),
|
spacing: 2,
|
||||||
Row(
|
children: [
|
||||||
spacing: 2,
|
Text(widget.allSources.where((x) => x.id == article.source).first.label)
|
||||||
children: [
|
.textStyle(Theme.of(context).textTheme.bodySmall!),
|
||||||
Text(widget.allSources.where((x) => x.id == article.source).first.label)
|
],
|
||||||
.textStyle(Theme.of(context).textTheme.bodySmall!),
|
).opacity(0.75).padding(horizontal: 16),
|
||||||
|
Row(
|
||||||
|
spacing: 2,
|
||||||
|
children: [
|
||||||
|
Text(DateFormat().format(date)).textStyle(Theme.of(context).textTheme.bodySmall!),
|
||||||
|
Text(' · ').textStyle(Theme.of(context).textTheme.bodySmall!).bold(),
|
||||||
|
Text(RelativeTime(context).format(date)).textStyle(Theme.of(context).textTheme.bodySmall!),
|
||||||
|
],
|
||||||
|
).opacity(0.75).padding(horizontal: 16),
|
||||||
|
const Gap(16),
|
||||||
],
|
],
|
||||||
).opacity(0.75).padding(horizontal: 16),
|
),
|
||||||
Row(
|
),
|
||||||
spacing: 2,
|
);
|
||||||
children: [
|
},
|
||||||
Text(DateFormat().format(date)).textStyle(Theme.of(context).textTheme.bodySmall!),
|
),
|
||||||
Text(' · ').textStyle(Theme.of(context).textTheme.bodySmall!).bold(),
|
),
|
||||||
Text(RelativeTime(context).format(date)).textStyle(Theme.of(context).textTheme.bodySmall!),
|
|
||||||
],
|
|
||||||
).opacity(0.75).padding(horizontal: 16),
|
|
||||||
const Gap(16),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -196,68 +196,71 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Container(
|
return AspectRatio(
|
||||||
constraints: BoxConstraints(maxHeight: constraints.maxHeight),
|
aspectRatio: widget.data[0]?.data['ratio']?.toDouble() ?? 1,
|
||||||
child: ScrollConfiguration(
|
child: Container(
|
||||||
behavior: _AttachmentListScrollBehavior(),
|
constraints: BoxConstraints(maxHeight: constraints.maxHeight),
|
||||||
child: ListView.separated(
|
child: ScrollConfiguration(
|
||||||
padding: widget.padding,
|
behavior: _AttachmentListScrollBehavior(),
|
||||||
shrinkWrap: true,
|
child: ListView.separated(
|
||||||
itemCount: widget.data.length,
|
padding: widget.padding,
|
||||||
itemBuilder: (context, idx) {
|
shrinkWrap: true,
|
||||||
return Container(
|
itemCount: widget.data.length,
|
||||||
constraints: constraints.copyWith(maxWidth: widget.maxWidth),
|
itemBuilder: (context, idx) {
|
||||||
child: AspectRatio(
|
return Container(
|
||||||
aspectRatio: (widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
|
constraints: constraints.copyWith(maxWidth: widget.maxWidth),
|
||||||
child: GestureDetector(
|
child: AspectRatio(
|
||||||
onTap: () {
|
aspectRatio: (widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
|
||||||
if (widget.data[idx]?.mediaType != SnMediaType.image) return;
|
child: GestureDetector(
|
||||||
context.pushTransparentRoute(
|
onTap: () {
|
||||||
AttachmentZoomView(
|
if (widget.data[idx]?.mediaType != SnMediaType.image) return;
|
||||||
data: widget.data.where((ele) => ele != null && ele.mediaType == SnMediaType.image).cast(),
|
context.pushTransparentRoute(
|
||||||
initialIndex: idx,
|
AttachmentZoomView(
|
||||||
heroTags: heroTags,
|
data: widget.data.where((ele) => ele != null && ele.mediaType == SnMediaType.image).cast(),
|
||||||
),
|
initialIndex: idx,
|
||||||
backgroundColor: Colors.black.withOpacity(0.7),
|
heroTags: heroTags,
|
||||||
rootNavigator: true,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Stack(
|
|
||||||
fit: StackFit.expand,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: backgroundColor,
|
|
||||||
border: Border(
|
|
||||||
top: borderSide,
|
|
||||||
bottom: borderSide,
|
|
||||||
),
|
|
||||||
borderRadius: AttachmentList.kDefaultRadius,
|
|
||||||
),
|
),
|
||||||
child: ClipRRect(
|
backgroundColor: Colors.black.withOpacity(0.7),
|
||||||
borderRadius: AttachmentList.kDefaultRadius,
|
rootNavigator: true,
|
||||||
child: AttachmentItem(
|
);
|
||||||
data: widget.data[idx],
|
},
|
||||||
heroTag: heroTags[idx],
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
|
border: Border(
|
||||||
|
top: borderSide,
|
||||||
|
bottom: borderSide,
|
||||||
|
),
|
||||||
|
borderRadius: AttachmentList.kDefaultRadius,
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: AttachmentList.kDefaultRadius,
|
||||||
|
child: AttachmentItem(
|
||||||
|
data: widget.data[idx],
|
||||||
|
heroTag: heroTags[idx],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Positioned(
|
||||||
Positioned(
|
right: 8,
|
||||||
right: 8,
|
bottom: 8,
|
||||||
bottom: 8,
|
child: Chip(
|
||||||
child: Chip(
|
label: Text('${idx + 1}/${widget.data.length}'),
|
||||||
label: Text('${idx + 1}/${widget.data.length}'),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
separatorBuilder: (context, index) => const Gap(8),
|
||||||
separatorBuilder: (context, index) => const Gap(8),
|
physics: const BouncingScrollPhysics(),
|
||||||
physics: const BouncingScrollPhysics(),
|
scrollDirection: Axis.horizontal,
|
||||||
scrollDirection: Axis.horizontal,
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -5,10 +5,9 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
// Keep this import to make the web image render work
|
// Keep this import to make the web image render work
|
||||||
import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart';
|
import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart';
|
||||||
import 'package:surface/providers/config.dart';
|
|
||||||
|
|
||||||
class UniversalImage extends StatelessWidget {
|
class UniversalImage extends StatelessWidget {
|
||||||
final String url;
|
final String url;
|
||||||
|
@ -22,14 +22,14 @@ PODS:
|
|||||||
- Firebase/Messaging (11.6.0):
|
- Firebase/Messaging (11.6.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseMessaging (~> 11.6.0)
|
- FirebaseMessaging (~> 11.6.0)
|
||||||
- firebase_analytics (11.4.0):
|
- firebase_analytics (11.4.1):
|
||||||
- Firebase/Analytics (= 11.6.0)
|
- Firebase/Analytics (= 11.6.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- firebase_core (3.10.0):
|
- firebase_core (3.10.1):
|
||||||
- Firebase/CoreOnly (~> 11.6.0)
|
- Firebase/CoreOnly (~> 11.6.0)
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- firebase_messaging (15.2.0):
|
- firebase_messaging (15.2.1):
|
||||||
- Firebase/CoreOnly (~> 11.6.0)
|
- Firebase/CoreOnly (~> 11.6.0)
|
||||||
- Firebase/Messaging (~> 11.6.0)
|
- Firebase/Messaging (~> 11.6.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
@ -296,9 +296,9 @@ SPEC CHECKSUMS:
|
|||||||
file_saver: 44e6fbf666677faf097302460e214e977fdd977b
|
file_saver: 44e6fbf666677faf097302460e214e977fdd977b
|
||||||
file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d
|
file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d
|
||||||
Firebase: 374a441a91ead896215703a674d58cdb3e9d772b
|
Firebase: 374a441a91ead896215703a674d58cdb3e9d772b
|
||||||
firebase_analytics: 5249f87da6fed852901581aab2602e0280ec2fdb
|
firebase_analytics: 91efc58e8e37964469fdd59ad11ba36bc97e75d6
|
||||||
firebase_core: 6d9bb8b0ea817e8fe0d928177d42275b45fdba6f
|
firebase_core: 75e003524565fb5bd80c9960bc5892e8475821cd
|
||||||
firebase_messaging: ae8e88b586e4d50abc7cac5bacf74d21967fd226
|
firebase_messaging: 082a385eb98b5bb843a566cb30404859c4bd6e25
|
||||||
FirebaseAnalytics: 7114c698cac995602e3b1b96663473e50d54d6e7
|
FirebaseAnalytics: 7114c698cac995602e3b1b96663473e50d54d6e7
|
||||||
FirebaseCore: 48b0dd707581cf9c1a1220da68223fb0a562afaa
|
FirebaseCore: 48b0dd707581cf9c1a1220da68223fb0a562afaa
|
||||||
FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
|
FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
|
||||||
|
76
pubspec.lock
76
pubspec.lock
@ -13,10 +13,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _flutterfire_internals
|
name: _flutterfire_internals
|
||||||
sha256: "27899c95f9e7ec06c8310e6e0eac967707714b9f1450c4a58fa00ca011a4a8ae"
|
sha256: e4f2a7ef31b0ab2c89d2bde35ef3e6e6aff1dce5e66069c6540b0e9cfe33ee6b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.49"
|
version: "1.3.50"
|
||||||
_macros:
|
_macros:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: dart
|
description: dart
|
||||||
@ -338,10 +338,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
|
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.7"
|
version: "2.3.8"
|
||||||
dart_webrtc:
|
dart_webrtc:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -418,10 +418,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: easy_localization
|
name: easy_localization
|
||||||
sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201
|
sha256: "0f5239c7b8ab06c66440cfb0e9aa4b4640429c6668d5a42fe389c5de42220b12"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.7"
|
version: "3.0.7+1"
|
||||||
easy_localization_loader:
|
easy_localization_loader:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -538,34 +538,34 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_analytics
|
name: firebase_analytics
|
||||||
sha256: "498c6cb8468e348a556709c745d92a52173ab3a9b906aa0593393f0787f201ea"
|
sha256: eac382bbcd5ae78c1d1ce5619d13f5a7424429f4bf55df9e3ad5110da34d1060
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.4.0"
|
version: "11.4.1"
|
||||||
firebase_analytics_platform_interface:
|
firebase_analytics_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_analytics_platform_interface
|
name: firebase_analytics_platform_interface
|
||||||
sha256: ccbb350554e98afdb4b59852689292d194d31232a2647b5012a66622b3711df9
|
sha256: a34db46c367265c4c961626e4b128bfb7b7e50958e7add4c27ba103f5f81b9b0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.0"
|
version: "4.3.1"
|
||||||
firebase_analytics_web:
|
firebase_analytics_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_analytics_web
|
name: firebase_analytics_web
|
||||||
sha256: "68e1f18fc16482c211c658e739c25f015b202a260d9ad8249c6d3d7963b8105f"
|
sha256: b6b4cef08e45e4c7d48476d9fc49fe9577081809a59026fe95b1a1b1eea165fa
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.10+6"
|
version: "0.5.10+7"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_core
|
name: firebase_core
|
||||||
sha256: "0307c1fde82e2b8b97e0be2dab93612aff9a72f31ebe9bfac66ed8b37ef7c568"
|
sha256: d851c1ca98fd5a4c07c747f8c65dacc2edd84a4d9ac055d32a5f0342529069f5
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.10.0"
|
version: "3.10.1"
|
||||||
firebase_core_platform_interface:
|
firebase_core_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -586,26 +586,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging
|
name: firebase_messaging
|
||||||
sha256: "48a8a59197c1c5174060ba9aa1e0036e9b5a0d28a0cc22d19c1fcabc67fafe3c"
|
sha256: e20ea2a0ecf9b0971575ab3ab42a6e285a94e50092c555b090c1a588a81b4d54
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.2.0"
|
version: "15.2.1"
|
||||||
firebase_messaging_platform_interface:
|
firebase_messaging_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging_platform_interface
|
name: firebase_messaging_platform_interface
|
||||||
sha256: "9770a8e91f54296829dcaa61ce9b7c2f9ae9abbf99976dd3103a60470d5264dd"
|
sha256: c57a92b5ae1857ef4fe4ae2e73452b44d32e984e15ab8b53415ea1bb514bdabd
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.6.0"
|
version: "4.6.1"
|
||||||
firebase_messaging_web:
|
firebase_messaging_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging_web
|
name: firebase_messaging_web
|
||||||
sha256: "329ca4ef45ec616abe6f1d5e58feed0934a50840a65aa327052354ad3c64ed77"
|
sha256: "83694a990d8525d6b01039240b97757298369622ca0253ad0ebcfed221bf8ee0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.10.0"
|
version: "3.10.1"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -830,10 +830,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_webrtc
|
name: flutter_webrtc
|
||||||
sha256: "188401cc3275bc4f1f965babdff6cac612a4b46572f1e49f49db8af5361d5712"
|
sha256: "4c76069f724f79dc6e8739c8f16c2ee00ca30a1f8e3aa75c0e830f8183278f03"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.6"
|
version: "0.12.7"
|
||||||
freezed:
|
freezed:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -878,18 +878,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: glob
|
name: glob
|
||||||
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
go_router:
|
go_router:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: "7c2d40b59890a929824f30d442e810116caf5088482629c894b9e4478c67472d"
|
sha256: daf3ff5570f55396b2d2c9bf8136d7db3a8acf208ac0cef92a3ae2beb9a81550
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.6.3"
|
version: "14.7.1"
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -950,10 +950,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.3.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1030,10 +1030,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_macos
|
name: image_picker_macos
|
||||||
sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62"
|
sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.1+1"
|
version: "0.2.1+2"
|
||||||
image_picker_platform_interface:
|
image_picker_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1710,18 +1710,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: a752ce92ea7540fc35a0d19722816e04d0e72828a4200e83a98cf1a1eb524c9a
|
sha256: c59819dacc6669a1165d54d2735a9543f136f9b3cec94ca65cea6ab8dffc422e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.5"
|
version: "2.4.0"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
sha256: "138b7bbbc7f59c56236e426c37afb8f78cbc57b094ac64c440e0bb90e380a4f5"
|
sha256: "650584dcc0a39856f369782874e562efd002a9c94aec032412c9eb81419cce1f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.4.4"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2171,10 +2171,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
|
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.2"
|
||||||
webrtc_interface:
|
webrtc_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2187,10 +2187,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "154360849a56b7b67331c21f09a386562d88903f90a1099c5987afc1912e1f29"
|
sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.10.0"
|
version: "5.10.1"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 2.2.2+57
|
version: 2.2.2+58
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
|
Loading…
x
Reference in New Issue
Block a user