Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
18fba0c9e7 | |||
c68138e516 | |||
cedd0b083a | |||
1a0721ba3a | |||
a75f42e440 | |||
e4a6ff2da4 | |||
baa6b401d3 |
@ -2,14 +2,12 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
import 'package:solian/providers/content/posts.dart';
|
import 'package:solian/providers/content/posts.dart';
|
||||||
|
import 'package:solian/providers/last_read.dart';
|
||||||
|
|
||||||
class PostListController extends GetxController {
|
class PostListController extends GetxController {
|
||||||
late final SharedPreferences _prefs;
|
|
||||||
|
|
||||||
String? author;
|
String? author;
|
||||||
|
|
||||||
/// The polling source modifier.
|
/// The polling source modifier.
|
||||||
@ -19,21 +17,14 @@ class PostListController extends GetxController {
|
|||||||
RxInt mode = 0.obs;
|
RxInt mode = 0.obs;
|
||||||
|
|
||||||
/// The paging controller for infinite loading.
|
/// The paging controller for infinite loading.
|
||||||
/// Only available when mode is `0` or `1`.
|
/// Only available when mode is `0`, `1` or `2`.
|
||||||
PagingController<int, Post> pagingController =
|
PagingController<int, Post> pagingController =
|
||||||
PagingController(firstPageKey: 0);
|
PagingController(firstPageKey: 0);
|
||||||
|
|
||||||
PostListController({this.author}) {
|
PostListController({this.author}) {
|
||||||
_initPreferences();
|
|
||||||
_initPagingController();
|
_initPagingController();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initPreferences() {
|
|
||||||
SharedPreferences.getInstance().then((prefs) {
|
|
||||||
_prefs = prefs;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize a compatibility layer to paging controller
|
/// Initialize a compatibility layer to paging controller
|
||||||
void _initPagingController() {
|
void _initPagingController() {
|
||||||
pagingController.addPageRequestListener(_onPagingControllerRequest);
|
pagingController.addPageRequestListener(_onPagingControllerRequest);
|
||||||
@ -109,11 +100,7 @@ class PostListController extends GetxController {
|
|||||||
postList.retainWhere((x) => idx.add(x.id));
|
postList.retainWhere((x) => idx.add(x.id));
|
||||||
|
|
||||||
var lastId = postList.map((x) => x.id).reduce(max);
|
var lastId = postList.map((x) => x.id).reduce(max);
|
||||||
if (_prefs.containsKey('feed_last_read_at')) {
|
Get.find<LastReadProvider>().feedLastReadAt = lastId;
|
||||||
final storedId = _prefs.getInt('feed_last_read_at') ?? 0;
|
|
||||||
lastId = max(storedId, lastId);
|
|
||||||
}
|
|
||||||
_prefs.setInt('feed_last_read_at', lastId);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import 'package:solian/firebase_options.dart';
|
|||||||
import 'package:solian/platform.dart';
|
import 'package:solian/platform.dart';
|
||||||
import 'package:solian/providers/attachment_uploader.dart';
|
import 'package:solian/providers/attachment_uploader.dart';
|
||||||
import 'package:solian/providers/daily_sign.dart';
|
import 'package:solian/providers/daily_sign.dart';
|
||||||
|
import 'package:solian/providers/last_read.dart';
|
||||||
import 'package:solian/providers/link_expander.dart';
|
import 'package:solian/providers/link_expander.dart';
|
||||||
import 'package:solian/providers/stickers.dart';
|
import 'package:solian/providers/stickers.dart';
|
||||||
import 'package:solian/providers/theme_switcher.dart';
|
import 'package:solian/providers/theme_switcher.dart';
|
||||||
@ -132,5 +133,6 @@ class SolianApp extends StatelessWidget {
|
|||||||
Get.lazyPut(() => AttachmentUploaderController());
|
Get.lazyPut(() => AttachmentUploaderController());
|
||||||
Get.lazyPut(() => LinkExpandProvider());
|
Get.lazyPut(() => LinkExpandProvider());
|
||||||
Get.lazyPut(() => DailySignProvider());
|
Get.lazyPut(() => DailySignProvider());
|
||||||
|
Get.lazyPut(() => LastReadProvider());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,15 @@ class Channel {
|
|||||||
'realm_id': realmId,
|
'realm_id': realmId,
|
||||||
'is_encrypted': isEncrypted,
|
'is_encrypted': isEncrypted,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (other is! Channel) return false;
|
||||||
|
return id == other.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => id;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChannelMember {
|
class ChannelMember {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
|
|
||||||
part 'daily_sign.g.dart';
|
part 'daily_sign.g.dart';
|
||||||
@ -39,10 +40,10 @@ class DailySignRecord {
|
|||||||
};
|
};
|
||||||
|
|
||||||
String get overviewSuggestion => switch (resultTier) {
|
String get overviewSuggestion => switch (resultTier) {
|
||||||
0 => '诸事不宜',
|
0 => 'dailySignTier0'.tr,
|
||||||
1 => '有些不宜',
|
1 => 'dailySignTier1'.tr,
|
||||||
2 => '平平淡淡',
|
2 => 'dailySignTier2'.tr,
|
||||||
3 => '有些事宜',
|
3 => 'dailySignTier3'.tr,
|
||||||
_ => '诸事皆宜',
|
_ => 'dailySignTier4'.tr,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,22 @@ class PostProvider extends GetConnect {
|
|||||||
httpClient.baseUrl = ServiceFinder.buildUrl('interactive', null);
|
httpClient.baseUrl = ServiceFinder.buildUrl('interactive', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Response> seeWhatsNew(int pivot) async {
|
||||||
|
GetConnect client;
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.value) {
|
||||||
|
client = auth.configureClient('co');
|
||||||
|
} else {
|
||||||
|
client = ServiceFinder.configureClient('co');
|
||||||
|
}
|
||||||
|
final resp = await client.get('/whats-new?pivot=$pivot');
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw RequestException(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Response> listRecommendations(int page,
|
Future<Response> listRecommendations(int page,
|
||||||
{String? realm, String? channel}) async {
|
{String? realm, String? channel}) async {
|
||||||
GetConnect client;
|
GetConnect client;
|
||||||
|
54
lib/providers/last_read.dart
Normal file
54
lib/providers/last_read.dart
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class LastReadProvider extends GetxController {
|
||||||
|
int? _feedLastReadAt;
|
||||||
|
int? _messagesLastReadAt;
|
||||||
|
|
||||||
|
int? get feedLastReadAt => _feedLastReadAt;
|
||||||
|
int? get messagesLastReadAt => _messagesLastReadAt;
|
||||||
|
|
||||||
|
set feedLastReadAt(int? value) {
|
||||||
|
if (value == _feedLastReadAt) return;
|
||||||
|
final newValue = max(_feedLastReadAt ?? 0, value ?? 0);
|
||||||
|
if (newValue != _feedLastReadAt) {
|
||||||
|
_feedLastReadAt = newValue;
|
||||||
|
_saveToStorage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set messagesLastReadAt(int? value) {
|
||||||
|
if (value == _messagesLastReadAt) return;
|
||||||
|
final newValue = max(_messagesLastReadAt ?? 0, value ?? 0);
|
||||||
|
if (newValue != _messagesLastReadAt) {
|
||||||
|
_messagesLastReadAt = newValue;
|
||||||
|
_saveToStorage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LastReadProvider() {
|
||||||
|
_revertFromStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _revertFromStorage() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
if (prefs.containsKey('feed_last_read_at')) {
|
||||||
|
_feedLastReadAt = prefs.getInt('feed_last_read_at')!;
|
||||||
|
}
|
||||||
|
if (prefs.containsKey('messages_last_read_at')) {
|
||||||
|
_messagesLastReadAt = prefs.getInt('messages_last_read_at');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _saveToStorage() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
if (_feedLastReadAt != null) {
|
||||||
|
prefs.setInt('feed_last_read_at', _feedLastReadAt!);
|
||||||
|
}
|
||||||
|
if (_messagesLastReadAt != null) {
|
||||||
|
prefs.setInt('messages_last_read_at', _messagesLastReadAt!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,27 @@ Future<MessageHistoryDb> createHistoryDb() async {
|
|||||||
.addMigrations([migration1to2]).build();
|
.addMigrations([migration1to2]).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<(List<Event>, int)?> getWhatsNewEvents(int pivot, {take = 10}) async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.isFalse) return null;
|
||||||
|
|
||||||
|
final client = auth.configureClient('messaging');
|
||||||
|
|
||||||
|
final resp = await client.get(
|
||||||
|
'/whats-new?pivot=$pivot&take=$take',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw RequestException(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
final PaginationResult response = PaginationResult.fromJson(resp.body);
|
||||||
|
final result =
|
||||||
|
response.data?.map((e) => Event.fromJson(e)).toList() ?? List.empty();
|
||||||
|
|
||||||
|
return (result, response.count);
|
||||||
|
}
|
||||||
|
|
||||||
Future<Event?> getRemoteEvent(int id, Channel channel, String scope) async {
|
Future<Event?> getRemoteEvent(int id, Channel channel, String scope) async {
|
||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
if (auth.isAuthorized.isFalse) return null;
|
if (auth.isAuthorized.isFalse) return null;
|
||||||
|
@ -1,19 +1,29 @@
|
|||||||
import 'dart:math';
|
import 'dart:developer';
|
||||||
|
import 'dart:math' hide log;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter/material.dart' hide Notification;
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
|
import 'package:solian/models/channel.dart';
|
||||||
import 'package:solian/models/daily_sign.dart';
|
import 'package:solian/models/daily_sign.dart';
|
||||||
|
import 'package:solian/models/event.dart';
|
||||||
|
import 'package:solian/models/notification.dart';
|
||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/providers/content/posts.dart';
|
import 'package:solian/providers/content/posts.dart';
|
||||||
import 'package:solian/providers/daily_sign.dart';
|
import 'package:solian/providers/daily_sign.dart';
|
||||||
|
import 'package:solian/providers/last_read.dart';
|
||||||
|
import 'package:solian/providers/message/adaptor.dart';
|
||||||
import 'package:solian/providers/websocket.dart';
|
import 'package:solian/providers/websocket.dart';
|
||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
import 'package:solian/screens/account/notification.dart';
|
import 'package:solian/screens/account/notification.dart';
|
||||||
|
import 'package:solian/widgets/chat/chat_event.dart';
|
||||||
import 'package:solian/widgets/posts/post_list.dart';
|
import 'package:solian/widgets/posts/post_list.dart';
|
||||||
|
|
||||||
class DashboardScreen extends StatefulWidget {
|
class DashboardScreen extends StatefulWidget {
|
||||||
@ -24,6 +34,8 @@ class DashboardScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DashboardScreenState extends State<DashboardScreen> {
|
class _DashboardScreenState extends State<DashboardScreen> {
|
||||||
|
late final AuthProvider _auth = Get.find();
|
||||||
|
late final LastReadProvider _lastRead = Get.find();
|
||||||
late final WebSocketProvider _ws = Get.find();
|
late final WebSocketProvider _ws = Get.find();
|
||||||
late final PostProvider _posts = Get.find();
|
late final PostProvider _posts = Get.find();
|
||||||
late final DailySignProvider _dailySign = Get.find();
|
late final DailySignProvider _dailySign = Get.find();
|
||||||
@ -31,21 +43,40 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
|||||||
Color get _unFocusColor =>
|
Color get _unFocusColor =>
|
||||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
|
||||||
|
|
||||||
|
List<Notification> get _pendingNotifications =>
|
||||||
|
List<Notification>.from(_ws.notifications)
|
||||||
|
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
|
||||||
|
|
||||||
List<Post>? _currentPosts;
|
List<Post>? _currentPosts;
|
||||||
|
int? _currentPostsCount;
|
||||||
|
|
||||||
Future<void> _pullPosts() async {
|
Future<void> _pullPosts() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
print(_lastRead.feedLastReadAt);
|
||||||
final resp = await _posts.listRecommendations(0);
|
if (_lastRead.feedLastReadAt == null) return;
|
||||||
|
log('[Dashboard] Pulling posts with pivot: ${_lastRead.feedLastReadAt}');
|
||||||
|
final resp = await _posts.seeWhatsNew(_lastRead.feedLastReadAt!);
|
||||||
final result = PaginationResult.fromJson(resp.body);
|
final result = PaginationResult.fromJson(resp.body);
|
||||||
if (prefs.containsKey('feed_last_read_at')) {
|
setState(() {
|
||||||
final id = prefs.getInt('feed_last_read_at')!;
|
_currentPostsCount = result.count;
|
||||||
setState(() {
|
_currentPosts = result.data?.map((e) => Post.fromJson(e)).toList();
|
||||||
_currentPosts = result.data
|
});
|
||||||
?.map((e) => Post.fromJson(e))
|
}
|
||||||
.where((x) => x.id > id)
|
|
||||||
.toList();
|
List<Event>? _currentMessages;
|
||||||
});
|
int? _currentMessagesCount;
|
||||||
}
|
|
||||||
|
Map<Channel, List<Event>>? get _currentGroupedMessages =>
|
||||||
|
_currentMessages?.groupListsBy((x) => x.channel!);
|
||||||
|
|
||||||
|
Future<void> _pullMessages() async {
|
||||||
|
if (_lastRead.messagesLastReadAt == null) return;
|
||||||
|
log('[Dashboard] Pulling messages with pivot: ${_lastRead.messagesLastReadAt}');
|
||||||
|
final out = await getWhatsNewEvents(_lastRead.messagesLastReadAt!);
|
||||||
|
if (out == null) return;
|
||||||
|
setState(() {
|
||||||
|
_currentMessages = out.$1;
|
||||||
|
_currentMessagesCount = out.$2;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _signingDaily = true;
|
bool _signingDaily = true;
|
||||||
@ -73,146 +104,252 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
|||||||
setState(() => _signingDaily = false);
|
setState(() => _signingDaily = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _pullData() async {
|
||||||
|
if (!_auth.isAuthorized.value) return;
|
||||||
|
await Future.wait([
|
||||||
|
_pullPosts(),
|
||||||
|
_pullMessages(),
|
||||||
|
_pullDaily(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_pullPosts();
|
_pullData();
|
||||||
_pullDaily();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final width = MediaQuery.of(context).size.width;
|
final width = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
return ListView(
|
return RefreshIndicator(
|
||||||
children: [
|
onRefresh: _pullData,
|
||||||
Column(
|
child: ListView(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
Column(
|
||||||
Text('today'.tr, style: Theme.of(context).textTheme.headlineSmall),
|
|
||||||
Text(DateFormat('yyyy/MM/dd').format(DateTime.now())),
|
|
||||||
],
|
|
||||||
).paddingOnly(top: 8, left: 18, right: 18, bottom: 12),
|
|
||||||
Card(
|
|
||||||
child: ListTile(
|
|
||||||
leading: AnimatedSwitcher(
|
|
||||||
switchInCurve: Curves.fastOutSlowIn,
|
|
||||||
switchOutCurve: Curves.fastOutSlowIn,
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
transitionBuilder: (child, animation) {
|
|
||||||
return ScaleTransition(
|
|
||||||
scale: animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: _signRecord == null
|
|
||||||
? Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
DateFormat('dd').format(DateTime.now()),
|
|
||||||
style:
|
|
||||||
GoogleFonts.robotoMono(fontSize: 22, height: 1.2),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
DateFormat('yy/MM').format(DateTime.now()),
|
|
||||||
style: GoogleFonts.robotoMono(fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Text(
|
|
||||||
_signRecord!.symbol,
|
|
||||||
style: GoogleFonts.notoSerifHk(fontSize: 20, height: 1),
|
|
||||||
).paddingSymmetric(horizontal: 9),
|
|
||||||
).paddingOnly(left: 4),
|
|
||||||
title: _signRecord == null
|
|
||||||
? const Text('诸事不宜')
|
|
||||||
: Text(_signRecord!.overviewSuggestion),
|
|
||||||
subtitle: _signRecord == null
|
|
||||||
? const Text('今日未拜访佛祖')
|
|
||||||
: Text('+${_signRecord!.resultExperience} EXP'),
|
|
||||||
trailing: AnimatedSwitcher(
|
|
||||||
switchInCurve: Curves.fastOutSlowIn,
|
|
||||||
switchOutCurve: Curves.fastOutSlowIn,
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
transitionBuilder: (child, animation) {
|
|
||||||
return ScaleTransition(
|
|
||||||
scale: animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: _signRecord == null
|
|
||||||
? IconButton(
|
|
||||||
tooltip: '上香求签',
|
|
||||||
icon: const Icon(Icons.local_fire_department),
|
|
||||||
onPressed: _signingDaily ? null : _signDaily,
|
|
||||||
)
|
|
||||||
: const SizedBox(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).paddingSymmetric(horizontal: 8),
|
|
||||||
const Divider(thickness: 0.3).paddingSymmetric(vertical: 8),
|
|
||||||
Obx(
|
|
||||||
() => Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Text(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
'today'.tr,
|
||||||
children: [
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
Column(
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Text(DateFormat('yyyy/MM/dd').format(DateTime.now())),
|
||||||
children: [
|
],
|
||||||
Text(
|
).paddingOnly(top: 8, left: 18, right: 18, bottom: 12),
|
||||||
'notification'.tr,
|
Card(
|
||||||
style: Theme.of(context)
|
child: ListTile(
|
||||||
.textTheme
|
leading: AnimatedSwitcher(
|
||||||
.titleMedium!
|
switchInCurve: Curves.fastOutSlowIn,
|
||||||
.copyWith(fontSize: 18),
|
switchOutCurve: Curves.fastOutSlowIn,
|
||||||
),
|
duration: const Duration(milliseconds: 300),
|
||||||
Text(
|
transitionBuilder: (child, animation) {
|
||||||
'notificationUnreadCount'.trParams({
|
return ScaleTransition(
|
||||||
'count': _ws.notifications.length.toString(),
|
scale: animation,
|
||||||
}),
|
child: child,
|
||||||
),
|
);
|
||||||
],
|
},
|
||||||
),
|
child: _signRecord == null
|
||||||
IconButton(
|
? Column(
|
||||||
icon: const Icon(Icons.more_horiz),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
onPressed: () {
|
children: [
|
||||||
showModalBottomSheet(
|
Text(
|
||||||
useRootNavigator: true,
|
DateFormat('dd').format(DateTime.now()),
|
||||||
isScrollControlled: true,
|
style: GoogleFonts.robotoMono(
|
||||||
context: context,
|
fontSize: 22, height: 1.2),
|
||||||
builder: (context) => const NotificationScreen(),
|
),
|
||||||
).then((_) => _ws.notificationUnread.value = 0);
|
Text(
|
||||||
},
|
DateFormat('yy/MM').format(DateTime.now()),
|
||||||
),
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
],
|
),
|
||||||
).paddingOnly(left: 18, right: 18, bottom: 8),
|
],
|
||||||
if (_ws.notifications.isNotEmpty)
|
)
|
||||||
|
: Text(
|
||||||
|
_signRecord!.symbol,
|
||||||
|
style: GoogleFonts.notoSerifHk(fontSize: 20, height: 1),
|
||||||
|
).paddingSymmetric(horizontal: 9),
|
||||||
|
).paddingOnly(left: 4),
|
||||||
|
title: _signRecord == null
|
||||||
|
? Text('dailySign'.tr)
|
||||||
|
: Text(_signRecord!.overviewSuggestion),
|
||||||
|
subtitle: _signRecord == null
|
||||||
|
? Text('dailySignNone'.tr)
|
||||||
|
: Text('+${_signRecord!.resultExperience} EXP'),
|
||||||
|
trailing: AnimatedSwitcher(
|
||||||
|
switchInCurve: Curves.fastOutSlowIn,
|
||||||
|
switchOutCurve: Curves.fastOutSlowIn,
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
transitionBuilder: (child, animation) {
|
||||||
|
return ScaleTransition(
|
||||||
|
scale: animation,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: _signRecord == null
|
||||||
|
? IconButton(
|
||||||
|
tooltip: '上香求签',
|
||||||
|
icon: const Icon(Icons.local_fire_department),
|
||||||
|
onPressed: _signingDaily ? null : _signDaily,
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).paddingSymmetric(horizontal: 8),
|
||||||
|
const Divider(thickness: 0.3).paddingSymmetric(vertical: 8),
|
||||||
|
|
||||||
|
/// Unread notifications
|
||||||
|
Obx(
|
||||||
|
() => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'notification'.tr,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium!
|
||||||
|
.copyWith(fontSize: 18),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'notificationUnreadCount'.trParams({
|
||||||
|
'count': _ws.notifications.length.toString(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.more_horiz),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const NotificationScreen(),
|
||||||
|
).then((_) => _ws.notificationUnread.value = 0);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingOnly(left: 18, right: 18, bottom: 8),
|
||||||
|
if (_ws.notifications.isNotEmpty)
|
||||||
|
SizedBox(
|
||||||
|
height: 76,
|
||||||
|
child: ListView.separated(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
itemCount: min(_pendingNotifications.length, 10),
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
final x = _pendingNotifications[idx];
|
||||||
|
return SizedBox(
|
||||||
|
width: min(360, width),
|
||||||
|
child: Card(
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
x.title,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (x.subtitle != null)
|
||||||
|
Text(
|
||||||
|
x.subtitle!,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Text(
|
||||||
|
x.body,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (_, __) => const Gap(4),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
trailing: const Icon(Icons.inbox_outlined),
|
||||||
|
title: Text('notifyEmpty'.tr),
|
||||||
|
subtitle: Text('notifyEmptyCaption'.tr),
|
||||||
|
),
|
||||||
|
).paddingSymmetric(horizontal: 8),
|
||||||
|
],
|
||||||
|
).paddingOnly(bottom: 12),
|
||||||
|
),
|
||||||
|
|
||||||
|
/// Unread friends / followed people posts
|
||||||
|
if (_currentPosts?.isNotEmpty ?? false)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'feed'.tr,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium!
|
||||||
|
.copyWith(fontSize: 18),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'feedUnreadCount'.trParams({
|
||||||
|
'count': (_currentPostsCount ?? 0).toString(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_forward),
|
||||||
|
onPressed: () {
|
||||||
|
AppRouter.instance.goNamed('feed');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingOnly(left: 18, right: 18, bottom: 8),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 76,
|
height: 360,
|
||||||
width: width,
|
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: min(_ws.notifications.length, 3),
|
itemCount: _currentPosts!.length,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
final x = _ws.notifications[idx];
|
final item = _currentPosts![idx];
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: width,
|
width: min(480, width),
|
||||||
child: Card(
|
child: Card(
|
||||||
child: ListTile(
|
child: SingleChildScrollView(
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
child: PostListEntryWidget(
|
||||||
horizontal: 24,
|
item: item,
|
||||||
vertical: 4,
|
isClickable: true,
|
||||||
),
|
isShowEmbed: true,
|
||||||
title: Text(x.title),
|
isNestedClickable: true,
|
||||||
subtitle: Column(
|
onUpdate: (_) {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
_pullPosts();
|
||||||
children: [
|
},
|
||||||
if (x.subtitle != null) Text(x.subtitle!),
|
backgroundColor: Theme.of(context)
|
||||||
Text(x.body),
|
.colorScheme
|
||||||
],
|
.surfaceContainerLow,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).paddingSymmetric(horizontal: 8),
|
).paddingSymmetric(horizontal: 8),
|
||||||
@ -220,97 +357,141 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
],
|
||||||
Card(
|
).paddingOnly(bottom: 12),
|
||||||
child: ListTile(
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
/// Unread messages part
|
||||||
trailing: const Icon(Icons.inbox_outlined),
|
if (_currentMessages?.isNotEmpty ?? false)
|
||||||
title: Text('notifyEmpty'.tr),
|
Column(
|
||||||
subtitle: Text('notifyEmptyCaption'.tr),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
),
|
children: [
|
||||||
).paddingSymmetric(horizontal: 8),
|
Row(
|
||||||
],
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
).paddingOnly(bottom: 12),
|
children: [
|
||||||
),
|
Column(
|
||||||
if (_currentPosts?.isNotEmpty ?? false)
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Text(
|
||||||
children: [
|
'messages'.tr,
|
||||||
Row(
|
style: Theme.of(context)
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
.textTheme
|
||||||
children: [
|
.titleMedium!
|
||||||
Column(
|
.copyWith(fontSize: 18),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
),
|
||||||
children: [
|
Text(
|
||||||
Text(
|
'messagesUnreadCount'.trParams({
|
||||||
'feed'.tr,
|
'count': (_currentMessagesCount ?? 0).toString(),
|
||||||
style: Theme.of(context)
|
}),
|
||||||
.textTheme
|
),
|
||||||
.titleMedium!
|
],
|
||||||
.copyWith(fontSize: 18),
|
),
|
||||||
),
|
IconButton(
|
||||||
Text(
|
icon: const Icon(Icons.arrow_forward),
|
||||||
'notificationUnreadCount'.trParams({
|
onPressed: () {
|
||||||
'count': (_currentPosts?.length ?? 0).toString(),
|
AppRouter.instance.goNamed('chat');
|
||||||
}),
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
).paddingOnly(left: 18, right: 18, bottom: 8),
|
||||||
IconButton(
|
SizedBox(
|
||||||
icon: const Icon(Icons.arrow_forward),
|
height: 360,
|
||||||
onPressed: () {
|
child: ListView.builder(
|
||||||
AppRouter.instance.goNamed('feed');
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: _currentGroupedMessages!.length,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
final channel =
|
||||||
|
_currentGroupedMessages!.keys.elementAt(idx);
|
||||||
|
final itemList =
|
||||||
|
_currentGroupedMessages!.values.elementAt(idx);
|
||||||
|
return SizedBox(
|
||||||
|
width: min(520, width),
|
||||||
|
child: Card(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
tileColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainerHigh,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(8),
|
||||||
|
topRight: Radius.circular(8),
|
||||||
|
)),
|
||||||
|
leading: CircleAvatar(
|
||||||
|
backgroundColor: channel.realmId == null
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.transparent,
|
||||||
|
radius: 20,
|
||||||
|
child: FaIcon(
|
||||||
|
FontAwesomeIcons.hashtag,
|
||||||
|
color: channel.realmId == null
|
||||||
|
? Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onPrimary
|
||||||
|
: Theme.of(context).colorScheme.primary,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
title: Text(channel.name),
|
||||||
|
subtitle: Text(channel.description),
|
||||||
|
onTap: () {
|
||||||
|
AppRouter.instance.pushNamed(
|
||||||
|
'channelChat',
|
||||||
|
pathParameters: {'alias': channel.alias},
|
||||||
|
queryParameters: {
|
||||||
|
if (channel.realmId != null)
|
||||||
|
'realm': channel.realm!.alias,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: itemList.length,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
final item = itemList[idx];
|
||||||
|
return ChatEvent(item: item).paddingOnly(
|
||||||
|
bottom: 8, top: 16, left: 8, right: 8);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).paddingSymmetric(horizontal: 8),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
)
|
||||||
).paddingOnly(left: 18, right: 18, bottom: 8),
|
],
|
||||||
SizedBox(
|
).paddingOnly(bottom: 12),
|
||||||
height: 360,
|
|
||||||
width: width,
|
/// Footer
|
||||||
child: ListView.builder(
|
Column(
|
||||||
scrollDirection: Axis.horizontal,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
itemCount: _currentPosts!.length,
|
children: [
|
||||||
itemBuilder: (context, idx) {
|
Text(
|
||||||
final item = _currentPosts![idx];
|
'Powered by Solar Network',
|
||||||
return SizedBox(
|
style: TextStyle(color: _unFocusColor, fontSize: 12),
|
||||||
width: width,
|
),
|
||||||
child: Card(
|
Text(
|
||||||
child: Card(
|
'dashboardFooter'.tr,
|
||||||
child: PostListEntryWidget(
|
style: [const Locale('zh', 'CN'), const Locale('zh', 'HK')]
|
||||||
item: item,
|
.contains(Get.deviceLocale)
|
||||||
isClickable: true,
|
? GoogleFonts.notoSerifHk(
|
||||||
isShowEmbed: true,
|
color: _unFocusColor,
|
||||||
isNestedClickable: true,
|
fontSize: 12,
|
||||||
onUpdate: (_) {
|
)
|
||||||
_pullPosts();
|
: TextStyle(
|
||||||
},
|
color: _unFocusColor,
|
||||||
backgroundColor: Theme.of(context)
|
fontSize: 12,
|
||||||
.colorScheme
|
),
|
||||||
.surfaceContainerLow,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).paddingSymmetric(horizontal: 8),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
).paddingAll(8),
|
||||||
Column(
|
],
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
),
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Powered by Solar Network',
|
|
||||||
style: TextStyle(color: _unFocusColor, fontSize: 12),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'占卜多少都是玩,人生还得靠自己',
|
|
||||||
style:
|
|
||||||
GoogleFonts.notoSerifHk(color: _unFocusColor, fontSize: 12),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
).paddingAll(8),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,17 @@ const i18nEnglish = {
|
|||||||
'feedSearch': 'Search Feed',
|
'feedSearch': 'Search Feed',
|
||||||
'feedSearchWithTag': 'Searching with tag #@key',
|
'feedSearchWithTag': 'Searching with tag #@key',
|
||||||
'feedSearchWithCategory': 'Searching in category @category',
|
'feedSearchWithCategory': 'Searching in category @category',
|
||||||
|
'feedUnreadCount': '@count posts you may missed',
|
||||||
|
'messages': 'Messages',
|
||||||
|
'messagesUnreadCount': '@count messages unread',
|
||||||
|
'dailySign': 'Daily Sign',
|
||||||
|
'dailySignNone': 'You haven\'t sign today',
|
||||||
|
'dailySignTier0': 'Everything may not be good',
|
||||||
|
'dailySignTier1': 'Something may be wrong',
|
||||||
|
'dailySignTier2': 'Just so so',
|
||||||
|
'dailySignTier3': 'Something may goes well',
|
||||||
|
'dailySignTier4': 'Everything will be awesome',
|
||||||
|
'dashboardFooter': 'Don\'t be serious, just for fun.',
|
||||||
'visitProfilePage': 'Visit Profile Page',
|
'visitProfilePage': 'Visit Profile Page',
|
||||||
'profilePosts': 'Posts',
|
'profilePosts': 'Posts',
|
||||||
'profileAlbum': 'Album',
|
'profileAlbum': 'Album',
|
||||||
|
@ -29,6 +29,17 @@ const i18nSimplifiedChinese = {
|
|||||||
'feedSearch': '搜索资讯',
|
'feedSearch': '搜索资讯',
|
||||||
'feedSearchWithTag': '检索带有 #@key 标签的资讯',
|
'feedSearchWithTag': '检索带有 #@key 标签的资讯',
|
||||||
'feedSearchWithCategory': '检索位于分类 @category 的资讯',
|
'feedSearchWithCategory': '检索位于分类 @category 的资讯',
|
||||||
|
'feedUnreadCount': '@count 条你可能错过的帖子',
|
||||||
|
'messages': '消息',
|
||||||
|
'messagesUnreadCount': '@count 条未读的消息',
|
||||||
|
'dailySign': '签到',
|
||||||
|
'dailySignNone': '今日未拜访佛祖',
|
||||||
|
'dailySignTier0': '诸事不宜',
|
||||||
|
'dailySignTier1': '有些不宜',
|
||||||
|
'dailySignTier2': '平平淡淡',
|
||||||
|
'dailySignTier3': '有些事宜',
|
||||||
|
'dailySignTier4': '诸事皆宜',
|
||||||
|
'dashboardFooter': '占卜多少沾点玩,人生还得靠实力',
|
||||||
'visitProfilePage': '造访个人主页',
|
'visitProfilePage': '造访个人主页',
|
||||||
'profilePosts': '帖子',
|
'profilePosts': '帖子',
|
||||||
'profileAlbum': '相簿',
|
'profileAlbum': '相簿',
|
||||||
|
@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:solian/controllers/chat_events_controller.dart';
|
import 'package:solian/controllers/chat_events_controller.dart';
|
||||||
import 'package:solian/models/channel.dart';
|
import 'package:solian/models/channel.dart';
|
||||||
import 'package:solian/models/event.dart';
|
import 'package:solian/models/event.dart';
|
||||||
|
import 'package:solian/providers/last_read.dart';
|
||||||
import 'package:solian/widgets/chat/chat_event.dart';
|
import 'package:solian/widgets/chat/chat_event.dart';
|
||||||
import 'package:solian/widgets/chat/chat_event_action.dart';
|
import 'package:solian/widgets/chat/chat_event_action.dart';
|
||||||
|
|
||||||
@ -39,6 +40,9 @@ class ChatEventList extends StatelessWidget {
|
|||||||
key: Key('chat-history#${channel.id}'),
|
key: Key('chat-history#${channel.id}'),
|
||||||
itemCount: chatController.currentEvents.length,
|
itemCount: chatController.currentEvents.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
Get.find<LastReadProvider>().messagesLastReadAt =
|
||||||
|
chatController.currentEvents[index].id;
|
||||||
|
|
||||||
bool isMerged = false, hasMerged = false;
|
bool isMerged = false, hasMerged = false;
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
hasMerged = _checkMessageMergeable(
|
hasMerged = _checkMessageMergeable(
|
||||||
|
@ -26,7 +26,7 @@ class AppNavigationDrawer extends StatefulWidget {
|
|||||||
|
|
||||||
class _AppNavigationDrawerState extends State<AppNavigationDrawer>
|
class _AppNavigationDrawerState extends State<AppNavigationDrawer>
|
||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
bool _isCollapsed = false;
|
bool _isCollapsed = true;
|
||||||
|
|
||||||
late final AnimationController _drawerAnimationController =
|
late final AnimationController _drawerAnimationController =
|
||||||
AnimationController(
|
AnimationController(
|
||||||
|
@ -885,6 +885,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
gap:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: gap
|
||||||
|
sha256: f19387d4e32f849394758b91377f9153a1b41d79513ef7668c088c77dbc6955d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
get:
|
get:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -2,7 +2,7 @@ name: solian
|
|||||||
description: "The Solar Network App"
|
description: "The Solar Network App"
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
version: 1.2.1+25
|
version: 1.2.1+27
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.3.4 <4.0.0"
|
sdk: ">=3.3.4 <4.0.0"
|
||||||
@ -76,6 +76,7 @@ dependencies:
|
|||||||
google_fonts: ^6.2.1
|
google_fonts: ^6.2.1
|
||||||
freezed_annotation: ^2.4.4
|
freezed_annotation: ^2.4.4
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
|
gap: ^3.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user