1
0
mirror of https://github.com/git-touch/git-touch synced 2025-02-22 06:17:40 +01:00

feat: code settings

This commit is contained in:
Rongjian Zhang 2019-09-15 17:36:09 +08:00
parent 9bc5c2b96a
commit a47d84a6cc
18 changed files with 275 additions and 44 deletions

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/code.dart';
import 'package:git_touch/models/settings.dart'; import 'package:git_touch/models/settings.dart';
import 'package:git_touch/models/theme.dart'; import 'package:git_touch/models/theme.dart';
import 'package:git_touch/screens/issues.dart'; import 'package:git_touch/screens/issues.dart';
@ -34,6 +35,7 @@ class _HomeState extends State<Home> {
// FIXME: // FIXME:
Provider.of<ThemeModel>(context).init(); Provider.of<ThemeModel>(context).init();
Provider.of<SettingsModel>(context).init(); Provider.of<SettingsModel>(context).init();
Provider.of<CodeModel>(context).init();
}); });
} }
@ -134,7 +136,7 @@ class _HomeState extends State<Home> {
} }
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
return CupertinoApp( return CupertinoApp(
home: CupertinoTheme( home: CupertinoTheme(
data: CupertinoThemeData( data: CupertinoThemeData(
@ -181,6 +183,7 @@ class App extends StatelessWidget {
ChangeNotifierProvider(builder: (context) => NotificationModel()), ChangeNotifierProvider(builder: (context) => NotificationModel()),
ChangeNotifierProvider(builder: (context) => ThemeModel()), ChangeNotifierProvider(builder: (context) => ThemeModel()),
ChangeNotifierProvider(builder: (context) => SettingsModel()), ChangeNotifierProvider(builder: (context) => SettingsModel()),
ChangeNotifierProvider(builder: (context) => CodeModel()),
], ],
child: Home(), child: Home(),
); );

71
lib/models/code.dart Normal file
View File

@ -0,0 +1,71 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_highlight/theme_map.dart';
import 'package:shared_preferences/shared_preferences.dart';
class CodeModel with ChangeNotifier {
static const _kTheme = 'code-theme';
static const _kFontSize = 'code-font-size';
static const _kFontFamily = 'code-font-family';
static var themes = themeMap.keys.toList();
static const fontSizes = [12, 13, 14, 15, 16, 17, 18, 19, 20];
static const fontFamilies = ['System'];
String _theme = 'github';
int _fontSize = 14;
String _fontFamily = 'System';
String get theme => _theme;
int get fontSize => _fontSize;
String get fontFamily => _fontFamily;
init() async {
var prefs = await SharedPreferences.getInstance();
var vh = prefs.getString(_kTheme);
var vs = prefs.getInt(_kFontSize);
var vf = prefs.getString(_kFontFamily);
print('read code: $vh, $vs, $vf');
if (themeMap.keys.contains(vh)) {
_theme = vh;
}
if (fontSizes.contains(vs)) {
_fontSize = vs;
}
if (fontFamilies.contains(vf)) {
_fontFamily = vf;
}
notifyListeners();
}
setTheme(String v) async {
var prefs = await SharedPreferences.getInstance();
await prefs.setString(_kTheme, v);
print('write code theme: $v');
_theme = v;
notifyListeners();
}
setFontSize(int v) async {
var prefs = await SharedPreferences.getInstance();
await prefs.setInt(_kFontSize, v);
print('write code font size: $v');
_fontSize = v;
notifyListeners();
}
setFontFamily(String v) async {
var prefs = await SharedPreferences.getInstance();
await prefs.setString(_kFontFamily, v);
print('write code font family: $v');
_fontFamily = v;
notifyListeners();
}
}

View File

@ -10,10 +10,10 @@ class DialogOption<T> {
DialogOption({this.value, this.widget}); DialogOption({this.value, this.widget});
} }
class ThemeMap { class AppThemeMap {
static const material = 0; static const material = 0;
static const cupertino = 1; static const cupertino = 1;
static const values = [ThemeMap.material, ThemeMap.cupertino]; static const values = [AppThemeMap.material, AppThemeMap.cupertino];
} }
class ThemeModel with ChangeNotifier { class ThemeModel with ChangeNotifier {
@ -28,12 +28,12 @@ class ThemeModel with ChangeNotifier {
int v = prefs.getInt(storageKey); int v = prefs.getInt(storageKey);
print('read theme: $v'); print('read theme: $v');
if (ThemeMap.values.contains(v)) { if (AppThemeMap.values.contains(v)) {
_theme = v; _theme = v;
} else if (Platform.isIOS) { } else if (Platform.isIOS) {
_theme = ThemeMap.cupertino; _theme = AppThemeMap.cupertino;
} else { } else {
_theme = ThemeMap.material; _theme = AppThemeMap.material;
} }
notifyListeners(); notifyListeners();
@ -55,7 +55,7 @@ class ThemeModel with ChangeNotifier {
bool fullscreenDialog = false, bool fullscreenDialog = false,
}) { }) {
switch (theme) { switch (theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
Navigator.of(context).push(CupertinoPageRoute( Navigator.of(context).push(CupertinoPageRoute(
builder: builder, builder: builder,
fullscreenDialog: fullscreenDialog, fullscreenDialog: fullscreenDialog,
@ -71,7 +71,7 @@ class ThemeModel with ChangeNotifier {
Future<bool> showConfirm(BuildContext context, String text) { Future<bool> showConfirm(BuildContext context, String text) {
switch (theme) { switch (theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
return showCupertinoDialog( return showCupertinoDialog(
context: context, context: context,
builder: (context) { builder: (context) {
@ -130,7 +130,7 @@ class ThemeModel with ChangeNotifier {
var cancelWidget = Text('Cancel'); var cancelWidget = Text('Cancel');
switch (theme) { switch (theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
return showCupertinoDialog<T>( return showCupertinoDialog<T>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -181,4 +181,38 @@ class ThemeModel with ChangeNotifier {
); );
} }
} }
Future<T> showPicker<T>(
BuildContext context, {
@required int initialItem,
@required List<Widget> children,
@required Function(int) onSelectedItemChanged,
}) {
switch (theme) {
case AppThemeMap.cupertino:
return showCupertinoModalPopup<T>(
context: context,
builder: (context) {
return Container(
height: 300,
child: CupertinoPicker(
backgroundColor: CupertinoColors.white,
children: children,
itemExtent: 40,
scrollController:
FixedExtentScrollController(initialItem: initialItem),
onSelectedItemChanged: onSelectedItemChanged,
),
);
},
);
default:
return showModalBottomSheet<T>(
context: context,
builder: (context) {
return null; // TODO:
},
);
}
}
} }

View File

@ -184,7 +184,7 @@ class _ListScaffoldState<T, K> extends State<ListScaffold<T, K>> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
List<Widget> slivers = [ List<Widget> slivers = [
CupertinoSliverRefreshControl(onRefresh: _refresh) CupertinoSliverRefreshControl(onRefresh: _refresh)
]; ];

View File

@ -181,7 +181,7 @@ class _LongListScaffoldState<T, K> extends State<LongListScaffold<T, K>> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
List<Widget> slivers = [ List<Widget> slivers = [
CupertinoSliverRefreshControl(onRefresh: _refresh) CupertinoSliverRefreshControl(onRefresh: _refresh)
]; ];

View File

@ -80,7 +80,7 @@ class _RefreshScaffoldState<T> extends State<RefreshScaffold<T>> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
return CupertinoPageScaffold( return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar( navigationBar: CupertinoNavigationBar(
middle: widget.title, middle: widget.title,

View File

@ -41,7 +41,7 @@ class RefreshStatelessScaffold extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
return CupertinoPageScaffold( return CupertinoPageScaffold(
navigationBar: navigationBar:
CupertinoNavigationBar(middle: title, trailing: trailing), CupertinoNavigationBar(middle: title, trailing: trailing),

View File

@ -22,7 +22,7 @@ class SimpleScaffold extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
return CupertinoPageScaffold( return CupertinoPageScaffold(
navigationBar: navigationBar:
CupertinoNavigationBar(middle: title, trailing: trailing), CupertinoNavigationBar(middle: title, trailing: trailing),
@ -32,11 +32,7 @@ class SimpleScaffold extends StatelessWidget {
); );
default: default:
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(title: title, actions: actions, bottom: bottom),
title: title,
actions: actions,
bottom: bottom,
),
body: SingleChildScrollView(child: bodyBuilder()), body: SingleChildScrollView(child: bodyBuilder()),
); );
} }

View File

@ -0,0 +1,105 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_highlight/flutter_highlight.dart';
import 'package:flutter_highlight/theme_map.dart';
import 'package:git_touch/models/code.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/simple.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/table_view.dart';
import 'package:provider/provider.dart';
class CodeSettingsScreen extends StatelessWidget {
final String code;
final String language;
CodeSettingsScreen(this.code, this.language);
static Timer _themeDebounce;
@override
Widget build(BuildContext context) {
var codeProvider = Provider.of<CodeModel>(context);
return SimpleScaffold(
title: AppBarTitle('Code theme'),
bodyBuilder: () {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TableView(
items: [
TableViewItem(
text: Text('Syntax Highlighting'),
rightWidget: Text(codeProvider.theme),
onTap: () {
Provider.of<ThemeModel>(context).showPicker(
context,
children: CodeModel.themes.map((k) => Text(k)).toList(),
initialItem:
CodeModel.themes.indexOf(codeProvider.theme),
onSelectedItemChanged: (int value) {
if (_themeDebounce?.isActive ?? false)
_themeDebounce.cancel();
_themeDebounce =
Timer(const Duration(milliseconds: 500), () {
Provider.of<CodeModel>(context)
.setTheme(CodeModel.themes[value]);
});
},
);
}),
TableViewItem(
text: Text('Font Size'),
rightWidget: Text(codeProvider.fontSize.toString()),
onTap: () {
Provider.of<ThemeModel>(context).showPicker(
context,
children: CodeModel.fontSizes
.map((k) => Text(k.toString()))
.toList(),
initialItem:
CodeModel.fontSizes.indexOf(codeProvider.fontSize),
onSelectedItemChanged: (int value) {
if (_themeDebounce?.isActive ?? false)
_themeDebounce.cancel();
_themeDebounce =
Timer(const Duration(milliseconds: 500), () {
Provider.of<CodeModel>(context)
.setFontSize(CodeModel.fontSizes[value]);
});
},
);
},
),
TableViewItem(
text: Text('Font Family'),
rightWidget: Text(codeProvider.fontFamily.toString()),
onTap: () {
// TODO:
},
),
],
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: HighlightView(
code,
language: language,
theme: themeMap[codeProvider.theme],
textStyle: TextStyle(
fontSize: codeProvider.fontSize.toDouble(),
fontFamily: monospaceFont,
),
padding: const EdgeInsets.all(10),
),
)
],
);
},
);
}
}

View File

@ -184,7 +184,7 @@ $key: pullRequest(number: ${item.number}) {
Widget _buildTitle() { Widget _buildTitle() {
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
// var textStyle = DefaultTextStyle.of(context).style; // var textStyle = DefaultTextStyle.of(context).style;
return DefaultTextStyle( return DefaultTextStyle(
style: TextStyle(fontSize: 16), style: TextStyle(fontSize: 16),

View File

@ -1,6 +1,10 @@
import 'package:flutter_highlight/themes/github.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter_highlight/theme_map.dart';
import 'package:git_touch/models/code.dart';
import 'package:git_touch/screens/code_settings.dart';
import 'package:git_touch/screens/image_view.dart'; import 'package:git_touch/screens/image_view.dart';
import 'package:git_touch/widgets/app_bar_title.dart'; import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:git_touch/widgets/markdown_view.dart'; import 'package:git_touch/widgets/markdown_view.dart';
import 'package:git_touch/widgets/table_view.dart'; import 'package:git_touch/widgets/table_view.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
@ -10,7 +14,6 @@ import 'package:git_touch/models/settings.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:git_touch/scaffolds/refresh.dart'; import 'package:git_touch/scaffolds/refresh.dart';
import 'package:git_touch/utils/utils.dart'; import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:primer/primer.dart'; import 'package:primer/primer.dart';
import 'package:seti/seti.dart'; import 'package:seti/seti.dart';
@ -29,14 +32,16 @@ class ObjectScreen extends StatelessWidget {
this.type = 'tree', this.type = 'tree',
}); });
String get expression => '$branch:' + paths.join('/'); String get _expression => '$branch:' + paths.join('/');
String get extname { String get _extname {
if (paths.isEmpty) return ''; if (paths.isEmpty) return '';
var dotext = path.extension(paths.last); var dotext = path.extension(paths.last);
if (dotext.isEmpty) return ''; if (dotext.isEmpty) return '';
return dotext.substring(1); return dotext.substring(1);
} }
String get _language => _extname.isEmpty ? 'plaintext' : _extname;
String get rawUrl => String get rawUrl =>
'https://raw.githubusercontent.com/$owner/$name/$branch/' + 'https://raw.githubusercontent.com/$owner/$name/$branch/' +
paths.join('/'); // TODO: paths.join('/'); // TODO:
@ -111,8 +116,9 @@ class ObjectScreen extends StatelessWidget {
); );
} }
Widget _buildBlob(payload) { Widget _buildBlob(BuildContext context, payload) {
switch (extname) { var codeProvider = Provider.of<CodeModel>(context);
switch (_extname) {
case 'md': case 'md':
case 'markdown': case 'markdown':
return Padding( return Padding(
@ -124,10 +130,12 @@ class ObjectScreen extends StatelessWidget {
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: HighlightView( child: HighlightView(
payload['text'], payload['text'],
language: extname.isEmpty ? 'plaintext' : extname, language: _language,
theme: githubTheme, theme: themeMap[codeProvider.theme],
padding: EdgeInsets.all(10), padding: EdgeInsets.all(10),
textStyle: TextStyle(fontFamily: monospaceFont), textStyle: TextStyle(
fontSize: codeProvider.fontSize.toDouble(),
fontFamily: monospaceFont),
), ),
); );
} }
@ -140,7 +148,7 @@ class ObjectScreen extends StatelessWidget {
onRefresh: () async { onRefresh: () async {
var data = await Provider.of<SettingsModel>(context).query('''{ var data = await Provider.of<SettingsModel>(context).query('''{
repository(owner: "$owner", name: "$name") { repository(owner: "$owner", name: "$name") {
object(expression: "$expression") { object(expression: "$_expression") {
$_subQuery $_subQuery
} }
} }
@ -161,12 +169,25 @@ class ObjectScreen extends StatelessWidget {
return data['repository']['object']; return data['repository']['object'];
}, },
trailingBuilder: (payload) {
switch (type) {
case 'blob':
return Link(
child: Icon(Octicons.settings, size: 20),
material: false,
screenBuilder: (_) =>
CodeSettingsScreen(payload['text'], _language),
);
default:
return null;
}
},
bodyBuilder: (payload) { bodyBuilder: (payload) {
switch (type) { switch (type) {
case 'tree': case 'tree':
return _buildTree(payload); return _buildTree(payload);
case 'blob': case 'blob':
return _buildBlob(payload); return _buildBlob(context, payload);
default: default:
return null; return null;
} }

View File

@ -46,7 +46,7 @@ class _SearchScreenState extends State<SearchScreen> {
Widget _buildInput() { Widget _buildInput() {
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
return CupertinoTextField( return CupertinoTextField(
// padding: EdgeInsets.all(10), // padding: EdgeInsets.all(10),
placeholder: 'Type to search', placeholder: 'Type to search',

View File

@ -37,11 +37,11 @@ class SettingsScreen extends StatelessWidget {
TableView(headerText: 'THEME', items: [ TableView(headerText: 'THEME', items: [
TableViewItem( TableViewItem(
text: Text('Material'), text: Text('Material'),
rightWidget: rightWidget: _buildRightWidget(
_buildRightWidget(themeProvider.theme == ThemeMap.material), themeProvider.theme == AppThemeMap.material),
onTap: () { onTap: () {
if (themeProvider.theme != ThemeMap.material) { if (themeProvider.theme != AppThemeMap.material) {
themeProvider.setTheme(ThemeMap.material); themeProvider.setTheme(AppThemeMap.material);
} }
}, },
hideRightChevron: true, hideRightChevron: true,
@ -49,10 +49,10 @@ class SettingsScreen extends StatelessWidget {
TableViewItem( TableViewItem(
text: Text('Cupertino'), text: Text('Cupertino'),
rightWidget: _buildRightWidget( rightWidget: _buildRightWidget(
themeProvider.theme == ThemeMap.cupertino), themeProvider.theme == AppThemeMap.cupertino),
onTap: () { onTap: () {
if (themeProvider.theme != ThemeMap.cupertino) { if (themeProvider.theme != AppThemeMap.cupertino) {
themeProvider.setTheme(ThemeMap.cupertino); themeProvider.setTheme(AppThemeMap.cupertino);
} }
}, },
hideRightChevron: true, hideRightChevron: true,

View File

@ -148,7 +148,7 @@ class UserScreen extends StatelessWidget {
var payload = data[0]; var payload = data[0];
if (isMe) { if (isMe) {
return Link( return Link(
child: Icon(Icons.settings, size: 24), child: Icon(Icons.settings),
screenBuilder: (_) => SettingsScreen(), screenBuilder: (_) => SettingsScreen(),
material: false, material: false,
fullscreenDialog: true, fullscreenDialog: true,

View File

@ -30,7 +30,7 @@ class ActionButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
return GestureDetector( return GestureDetector(
child: Icon(iconData, size: 24), child: Icon(iconData, size: 24),
onTap: () async { onTap: () async {

View File

@ -66,7 +66,8 @@ class Link extends StatelessWidget {
color: bgColor ?? Colors.white, color: bgColor ?? Colors.white,
child: InkWell( child: InkWell(
child: child, child: child,
splashColor: theme == ThemeMap.cupertino ? Colors.transparent : null, splashColor:
theme == AppThemeMap.cupertino ? Colors.transparent : null,
onTap: () => _onTap(context, theme), onTap: () => _onTap(context, theme),
), ),
), ),

View File

@ -12,7 +12,7 @@ class Loading extends StatelessWidget {
// return Image.asset('images/loading.webp'); // return Image.asset('images/loading.webp');
switch (Provider.of<ThemeModel>(context).theme) { switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino: case AppThemeMap.cupertino:
return CupertinoActivityIndicator(radius: 12); return CupertinoActivityIndicator(radius: 12);
default: default:
return Center( return Center(

View File

@ -16,7 +16,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
http: ^0.12.0 http: ^0.12.0
rxdart: ^0.20.0 rxdart: ^0.22.2
uri: ^0.11.3 uri: ^0.11.3
intl: ^0.15.7 intl: ^0.15.7
url_launcher: ^5.0.2 url_launcher: ^5.0.2