~cytrogen/fluent-reader-mobile

71532ac0a757be531f849737793bf01d343cc12c — Bruce Liu 5 years ago 206160b
add error messages
M lib/l10n/intl_en.arb => lib/l10n/intl_en.arb +5 -1
@@ 83,5 83,9 @@
    "getApiKey": "Get API ID & Key",
    "getApiKeyHint": "In \"Preferences\" > \"Developer\"",
    "prev": "Previous",
    "next": "Next"
    "next": "Next",
    "wentWrong": "Something went wrong.",
    "retry": "Retry",
    "copy": "Copy",
    "errorLog": "Error log"
  }
\ No newline at end of file

M lib/l10n/intl_zh.arb => lib/l10n/intl_zh.arb +5 -1
@@ 83,5 83,9 @@
    "getApiKey": "获取 API ID & KEY",
    "getApiKeyHint": "在 “偏好设置” > “开发者” 下",
    "prev": "前一项",
    "next": "后一项"
    "next": "后一项",
    "wentWrong": "发生错误",
    "retry": "重试",
    "copy": "复制",
    "errorLog": "错误日志"
  }
\ No newline at end of file

M lib/main.dart => lib/main.dart +2 -0
@@ 2,6 2,7 @@ import 'dart:io';

import 'package:fluent_reader_lite/models/service.dart';
import 'package:fluent_reader_lite/pages/article_page.dart';
import 'package:fluent_reader_lite/pages/error_log_page.dart';
import 'package:fluent_reader_lite/pages/settings/about_page.dart';
import 'package:fluent_reader_lite/pages/home_page.dart';
import 'package:fluent_reader_lite/pages/settings/feed_page.dart';


@@ 53,6 54,7 @@ void main() async {
class MyApp extends StatelessWidget {
  static final Map<String, Widget Function(BuildContext)> baseRoutes = {
    "/article": (context) => ArticlePage(),
    "/error-log": (context) => ErrorLogPage(),
    "/settings": (context) => SettingsPage(),
    "/settings/sources": (context) => SourcesPage(),
    "/settings/sources/edit": (context) => SourceEditPage(),

M lib/models/sync_model.dart => lib/models/sync_model.dart +1 -0
@@ 56,6 56,7 @@ class SyncModel with ChangeNotifier {
      lastSyncSuccess = true;
    } catch(exp) {
      lastSyncSuccess = false;
      Store.setErrorLog(exp.toString());
      print(exp);
    }
    lastSynced = DateTime.now();

M lib/pages/article_page.dart => lib/pages/article_page.dart +45 -9
@@ 11,6 11,7 @@ import 'package:fluent_reader_lite/utils/global.dart';
import 'package:fluent_reader_lite/utils/store.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';


@@ 29,10 30,14 @@ class ArticlePage extends StatefulWidget {
  ArticlePageState createState() => ArticlePageState();
}

enum _ArticleLoadState {
  Loading, Success, Failure
}

class ArticlePageState extends State<ArticlePage> {
  WebViewController _controller;
  int requestId = 0;
  bool loaded = false;
  _ArticleLoadState loaded = _ArticleLoadState.Loading;
  bool navigated = false;
  SourceOpenTarget _target;
  String iid;


@@ 44,7 49,7 @@ class ArticlePageState extends State<ArticlePage> {
    }
    setState(() {
      iid = id;
      loaded = false;
      loaded = _ArticleLoadState.Loading;
      navigated = false;
      _target = null;
      if (isSource != null) isSourceFeed = isSource;


@@ 70,12 75,15 @@ class ArticlePageState extends State<ArticlePage> {
        var html = (await http.get(item.link)).body;
        a = Uri.encodeComponent(html);
      } catch(exp) {
        setState(() { loaded = true; });
        if (mounted && currId == requestId) {
          setState(() { loaded = _ArticleLoadState.Failure; });
        }
        return;
      }
    } else {
      a = Uri.encodeComponent(item.content);
    }
    if (!mounted || currId != requestId) return;
    var h = '<p id="source">${source.name}${(item.creator!=null&&item.creator.length>0)?' / '+item.creator:''}</p>';
    h += '<p id="title">${item.title}</p>';
    h += '<p id="date">${DateFormat.yMd(Localizations.localeOf(context).toString()).add_Hm().format(item.date)}</p>';


@@ 87,20 95,20 @@ class ArticlePageState extends State<ArticlePage> {
      var brightness = Global.currentBrightness(context);
      localUrl += "&t=${brightness.index}";
    }
    if (currId == requestId) _controller.loadUrl(localUrl);
    _controller.loadUrl(localUrl);
  }

  void _onPageReady(_) async {
    if (Platform.isAndroid || Global.globalModel.getBrightness() != null) {
      await Future.delayed(Duration(milliseconds: 300));
    }
    setState(() { loaded = true; });
    setState(() { loaded = _ArticleLoadState.Success; });
    if (_target == SourceOpenTarget.Local || _target == SourceOpenTarget.FullContent) {
      navigated = true;
    }
  }
  void _onWebpageReady(_) {
    if (loaded) navigated = true;
    if (loaded == _ArticleLoadState.Success) navigated = true;
  }

  void _setOpenTarget(RSSSource source, {SourceOpenTarget target}) {


@@ 112,7 120,7 @@ class ArticlePageState extends State<ArticlePage> {
  void _loadOpenTarget(RSSItem item, RSSSource source) {
    setState(() {
      requestId += 1;
      loaded = false;
      loaded = _ArticleLoadState.Loading;
      navigated = false;
    });
    switch (_target) {


@@ 166,7 174,7 @@ class ArticlePageState extends State<ArticlePage> {
        var source = tuple.item2;
        if (_target == null) _target = source.openTarget;
        final body = SafeArea(child: IndexedStack(
          index: !loaded ? 0 : 1,
          index: loaded.index,
          children: [
            Center(
              child: CupertinoActivityIndicator()


@@ 182,6 190,21 @@ class ArticlePageState extends State<ArticlePage> {
              onPageFinished: _onWebpageReady,
              navigationDelegate: _onNavigate,
            ),
            Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    S.of(context).wentWrong,
                    style: TextStyle(color: CupertinoColors.label.resolveFrom(context)),
                  ),
                  CupertinoButton(
                    child: Text(S.of(context).retry),
                    onPressed: () { _loadOpenTarget(item, source); },
                  ),
                ],
              ),
            )
          ],
        ), bottom: false,);
        return CupertinoPageScaffold(


@@ 208,6 231,7 @@ class ArticlePageState extends State<ArticlePage> {
                      ? S.of(context).markUnread
                      : S.of(context).markRead,
                    onPressed: () {
                      HapticFeedback.mediumImpact();
                      Global.itemsModel.updateItem(item.id, read: !item.hasRead);
                    },
                  ),


@@ 219,13 243,25 @@ class ArticlePageState extends State<ArticlePage> {
                      ? S.of(context).star
                      : S.of(context).unstar,
                    onPressed: () {
                      HapticFeedback.mediumImpact();
                      Global.itemsModel.updateItem(item.id, starred: !item.starred);
                    },
                  ),
                  CupertinoToolbarItem(
                    icon: CupertinoIcons.share,
                    semanticLabel: S.of(context).share,
                    onPressed: () { Share.share(item.link); },
                    onPressed: () {
                      final media = MediaQuery.of(context);
                      Share.share(
                        item.link,
                        sharePositionOrigin: Rect.fromLTWH(
                          media.size.width - ArticlePage.state.currentContext.size.width / 2,
                          media.size.height - media.padding.bottom - 54,
                          0,
                          0
                        )
                      );
                    },
                  ),
                  CupertinoToolbarItem(
                    icon: CupertinoIcons.chevron_up,

A lib/pages/error_log_page.dart => lib/pages/error_log_page.dart +35 -0
@@ 0,0 1,35 @@
import 'package:fluent_reader_lite/components/list_tile_group.dart';
import 'package:fluent_reader_lite/generated/l10n.dart';
import 'package:fluent_reader_lite/utils/colors.dart';
import 'package:fluent_reader_lite/utils/store.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class ErrorLogPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final errorLog = Store.getErrorLog();
    return CupertinoPageScaffold(
      backgroundColor: MyColors.background,
      navigationBar: CupertinoNavigationBar(
        middle: Text(S.of(context).errorLog),
        trailing: CupertinoButton(
          padding: EdgeInsets.zero,
          child: Text(S.of(context).copy),
          onPressed: () {
            Clipboard.setData(ClipboardData(text: errorLog));
          },
        ),
      ),
      child: ListView(children: [
        ListTileGroup([
          SelectableText(
            errorLog,
            style: TextStyle(color: CupertinoColors.label.resolveFrom(context)),
          ),
        ]),
      ]),
    );
  }
}
\ No newline at end of file

M lib/pages/item_list_page.dart => lib/pages/item_list_page.dart +7 -1
@@ 241,7 241,13 @@ class _ItemListPageState extends State<ItemListPage> {
              ], mainAxisAlignment: MainAxisAlignment.spaceBetween),
              onPressed: () { 
                Navigator.of(context, rootNavigator: true).pop();
                Share.share(item.link);
                final media = MediaQuery.of(context);
                Share.share(
                  item.link,
                  sharePositionOrigin: Rect.fromLTWH(
                    160, media.size.height - media.padding.bottom, 0, 0
                  ),
                );
              },
            ),
          ],

M lib/pages/settings/source_edit_page.dart => lib/pages/settings/source_edit_page.dart +4 -1
@@ 58,7 58,10 @@ class SourceEditPage extends StatelessWidget {
        final urlTile = ListTileGroup([
          MyListTile(
            title: Flexible(child: Text(source.url, style: urlStyle, overflow: TextOverflow.ellipsis)),
            trailing: Icon(CupertinoIcons.doc_on_clipboard),
            trailing: Icon(
              CupertinoIcons.doc_on_clipboard,
              semanticLabel: S.of(context).copy,
            ),
            onTap: () { Clipboard.setData(ClipboardData(text: source.url)); },
            trailingChevron: false,
            withDivider: false,

M lib/pages/subscription_list_page.dart => lib/pages/subscription_list_page.dart +28 -17
@@ 13,6 13,7 @@ import 'package:fluent_reader_lite/utils/colors.dart';
import 'package:fluent_reader_lite/utils/global.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:provider/provider.dart';


@@ 98,6 99,13 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
    Navigator.of(context, rootNavigator: true).pushNamed("/settings");
  }

  void _openErrorLog() {
    if (!Global.syncModel.lastSyncSuccess) {
      HapticFeedback.mediumImpact();
      Navigator.of(context, rootNavigator: true).pushNamed("/error-log");
    }
  }

  @override
  Widget build(BuildContext context) {
    final navigationBar = CupertinoSliverNavigationBar(


@@ 166,23 174,26 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
    final syncInfo = Consumer<SyncModel>(
      builder: (context, syncModel, child) {
        return SliverToBoxAdapter(
          child: Container(
            padding: EdgeInsets.all(12),
            child: Column(
              children: [
                Text(
                  syncModel.lastSyncSuccess
                    ? S.of(context).lastSyncSuccess
                    : S.of(context).lastSyncFailure,
                  style: syncStyle,
                ),
                Text(
                  DateFormat
                    .Md(Localizations.localeOf(context).toString())
                    .add_Hm().format(syncModel.lastSynced),
                  style: syncStyle,
                ),
              ],
          child: GestureDetector(
            onLongPress: _openErrorLog,
            child: Container(
              padding: EdgeInsets.all(12),
              child: Column(
                children: [
                  Text(
                    syncModel.lastSyncSuccess
                      ? S.of(context).lastSyncSuccess
                      : S.of(context).lastSyncFailure,
                    style: syncStyle,
                  ),
                  Text(
                    DateFormat
                      .Md(Localizations.localeOf(context).toString())
                      .add_Hm().format(syncModel.lastSynced),
                    style: syncStyle,
                  ),
                ],
              ),
            ),
          ),
        );

M lib/utils/store.dart => lib/utils/store.dart +9 -0
@@ 6,6 6,7 @@ import 'package:shared_preferences/shared_preferences.dart';

abstract class StoreKeys {
  static const GROUPS = "groups";
  static const ERROR_LOG = "errorLog";
 
  // General
  static const THEME = "theme";


@@ 97,4 98,12 @@ class Store {
  static void setArticleFontSize(int value) {
    sp.setInt(StoreKeys.ARTICLE_FONT_SIZE, value);
  }

  static String getErrorLog() {
    return sp.getString(StoreKeys.ERROR_LOG) ?? "";
  }

  static void setErrorLog(String value) {
    sp.setString(StoreKeys.ERROR_LOG, value);
  }
}
\ No newline at end of file

M pubspec.yaml => pubspec.yaml +1 -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+3
version: 1.0.0+4

environment:
  sdk: ">=2.7.0 <3.0.0"