M assets/article/article.html => assets/article/article.html +1 -0
@@ 3,6 3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="referrer" content="no-referrer">
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'self'; img-src http: https: data:; style-src 'self' 'unsafe-inline'; frame-src http: https:; media-src http: https:; connect-src https: http:">
<title>Article</title>
M lib/l10n/intl_en.arb => lib/l10n/intl_en.arb +2 -1
@@ 87,5 87,6 @@
"wentWrong": "Something went wrong.",
"retry": "Retry",
"copy": "Copy",
- "errorLog": "Error log"
+ "errorLog": "Error log",
+ "unreadSourceTip": "You can long press on the title of this page to toggle between all and unread subscriptions."
}=
\ No newline at end of file
M lib/l10n/intl_zh.arb => lib/l10n/intl_zh.arb +2 -1
@@ 87,5 87,6 @@
"wentWrong": "发生错误",
"retry": "重试",
"copy": "复制",
- "errorLog": "错误日志"
+ "errorLog": "错误日志",
+ "unreadSourceTip": "您可以长按此页面的标题来切换全部订阅源或仅未读订阅源。"
}=
\ No newline at end of file
M lib/models/sources_model.dart => lib/models/sources_model.dart +12 -0
@@ 1,5 1,8 @@
+import 'dart:collection';
+
import 'package:fluent_reader_lite/models/source.dart';
import 'package:fluent_reader_lite/utils/global.dart';
+import 'package:fluent_reader_lite/utils/store.dart';
import 'package:fluent_reader_lite/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:html/parser.dart';
@@ 11,6 14,15 @@ import 'item.dart';
class SourcesModel with ChangeNotifier {
Map<String, RSSSource> _sources = Map();
Map<String, RSSSource> _deleted = Map();
+ bool _showUnreadTip = Store.sp.getBool(StoreKeys.UNREAD_SOURCE_TIP) ?? true;
+
+ bool get showUnreadTip => _showUnreadTip;
+ set showUnreadTip(bool value) {
+ if (_showUnreadTip != value) {
+ _showUnreadTip = value;
+ Store.sp.setBool(StoreKeys.UNREAD_SOURCE_TIP, value);
+ }
+ }
bool has(String id) => _sources.containsKey(id);
M lib/pages/subscription_list_page.dart => lib/pages/subscription_list_page.dart +88 -2
@@ 33,6 33,7 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
List<String> sids;
String title;
bool transitioning = false;
+ bool unreadOnly = false;
void _onScrollTop() {
if (widget.scrollTopNotifier.index == 1 && !Navigator.of(context).canPop()) {
@@ 106,15 107,93 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
}
}
+ void _toggleUnreadOnly() {
+ HapticFeedback.mediumImpact();
+ setState(() { unreadOnly = !unreadOnly; });
+ _onScrollTop();
+ }
+
+ void _dismissTip() {
+ if (Global.sourcesModel.showUnreadTip) {
+ Global.sourcesModel.showUnreadTip = false;
+ setState(() {});
+ }
+ }
+
+ Widget _buildUnreadTip() {
+ return SliverToBoxAdapter(child: Container(
+ padding: EdgeInsets.all(16),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(16),
+ child: Container(
+ padding: EdgeInsets.all(12),
+ color: CupertinoColors.secondarySystemBackground.resolveFrom(context),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: EdgeInsets.only(right: 12),
+ child: Icon(Icons.radio_button_checked),
+ ),
+ Flexible(child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ S.of(context).unreadSourceTip,
+ style: TextStyle(
+ color: CupertinoColors.label.resolveFrom(context),
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ Padding(padding: EdgeInsets.only(bottom: 6)),
+ CupertinoButton(
+ minSize: 28,
+ padding: EdgeInsets.zero,
+ child: Text(S.of(context).confirm),
+ onPressed: _dismissTip,
+ ),
+ ],
+ )),
+ ],
+ ),
+ ),
+ ),
+ ));
+ }
+
@override
Widget build(BuildContext context) {
+ final titleWidget = GestureDetector(
+ onLongPress: _toggleUnreadOnly,
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Container(
+ constraints: BoxConstraints(
+ maxWidth: Global.isTablet
+ ? 260
+ : MediaQuery.of(context).size.width - 60,
+ ),
+ child: Text(
+ title ?? S.of(context).subscriptions,
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ if (unreadOnly) Padding(
+ padding: EdgeInsets.only(left: 4),
+ child: Icon(Icons.radio_button_checked, size: 18),
+ ),
+ ],
+ ),
+ );
final navigationBar = CupertinoSliverNavigationBar(
stretch: false,
- largeTitle: Text(title ?? S.of(context).subscriptions),
+ largeTitle: titleWidget,
heroTag: "subscriptions",
transitionBetweenRoutes: true,
backgroundColor: transitioning ? MyColors.tileBackground : CupertinoColors.systemBackground,
leading: CupertinoButton(
+ minSize: 36,
padding: EdgeInsets.zero,
child: Text(S.of(context).groups),
onPressed: _openGroups,
@@ 149,10 228,16 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
List<RSSSource> sources;
if (sids == null) {
sources = Global.sourcesModel.getSources().toList();
+ if (unreadOnly) {
+ sources = sources.where((s) => s.unreadCount > 0).toList();
+ }
} else {
sources = [];
for (var sid in sids) {
- sources.add(Global.sourcesModel.getSource(sid));
+ final source = Global.sourcesModel.getSource(sid);
+ if (!unreadOnly || source.unreadCount > 0) {
+ sources.add(source);
+ }
}
}
// Latest sources first
@@ 203,6 288,7 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
slivers: [
navigationBar,
SyncControl(),
+ if (Global.sourcesModel.showUnreadTip) _buildUnreadTip(),
if (sids != null) Consumer<SourcesModel>(
builder: (context, sourcesModel, child) {
var count = sids
M lib/utils/store.dart => lib/utils/store.dart +1 -0
@@ 24,6 24,7 @@ abstract class StoreKeys {
static const DIM_READ = "dimRead";
static const FEED_SWIPE_R = "feedSwipeR";
static const FEED_SWIPE_L = "feedSwipeL";
+ static const UNREAD_SOURCE_TIP = "unreadSourceTip";
// Reading preferences
static const ARTICLE_FONT_SIZE = "articleFontSize";
M pubspec.lock => pubspec.lock +7 -0
@@ 238,6 238,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.4"
+ lpinyin:
+ dependency: "direct main"
+ description:
+ name: lpinyin
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.0"
matcher:
dependency: transitive
description:
M pubspec.yaml => pubspec.yaml +2 -1
@@ 15,7 15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 1.0.0+4
+version: 1.0.1+5
environment:
sdk: ">=2.7.0 <3.0.0"
@@ 43,6 43,7 @@ dependencies:
responsive_builder: ^0.3.0
cached_network_image: ^2.5.0
flutter_cache_manager: ^2.1.0
+ lpinyin: ^1.1.0
modal_bottom_sheet: ^1.0.0+1
overlay_dialog: ^0.0.3