2019-09-13 10:27:33 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
2019-09-30 14:49:22 +02:00
|
|
|
import 'package:git_touch/models/theme.dart';
|
2019-09-14 11:19:33 +02:00
|
|
|
import 'package:git_touch/utils/utils.dart';
|
2019-09-13 10:27:33 +02:00
|
|
|
import 'package:primer/primer.dart';
|
2019-09-30 14:49:22 +02:00
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:uri/uri.dart';
|
2019-11-06 14:56:52 +01:00
|
|
|
import 'package:path/path.dart' as path;
|
2019-09-13 10:27:33 +02:00
|
|
|
|
|
|
|
class MarkdownView extends StatelessWidget {
|
|
|
|
final String text;
|
2019-11-06 14:56:52 +01:00
|
|
|
final List<String> basePaths;
|
2019-09-13 10:27:33 +02:00
|
|
|
|
2019-11-06 14:56:52 +01:00
|
|
|
MarkdownView(this.text, {this.basePaths});
|
2019-09-13 10:27:33 +02:00
|
|
|
|
2019-09-30 14:49:22 +02:00
|
|
|
Map<String, String> matchPattern(String url, String pattern) {
|
|
|
|
var uri = Uri.parse(url);
|
|
|
|
return UriParser(UriTemplate(pattern)).match(uri)?.parameters;
|
|
|
|
}
|
|
|
|
|
2019-09-13 10:27:33 +02:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2019-11-08 12:56:49 +01:00
|
|
|
final theme = Provider.of<ThemeModel>(context);
|
|
|
|
final _basicStyle =
|
|
|
|
TextStyle(fontSize: 16, color: theme.palette.text, height: 1.5);
|
|
|
|
final _hStyle =
|
|
|
|
_basicStyle.copyWith(fontWeight: FontWeight.w600, height: 1.25);
|
|
|
|
|
2019-09-13 10:27:33 +02:00
|
|
|
return MarkdownBody(
|
2019-09-30 14:49:22 +02:00
|
|
|
onTapLink: (url) {
|
2019-11-06 14:56:52 +01:00
|
|
|
if (basePaths != null &&
|
|
|
|
!url.startsWith('https://') &&
|
|
|
|
!url.startsWith('http://')) {
|
|
|
|
// Treat as relative path
|
|
|
|
|
|
|
|
final x = basePaths.sublist(3).join('/');
|
|
|
|
var y = path.join(x, url);
|
|
|
|
if (y.startsWith('/')) y = y.substring(1);
|
|
|
|
|
2019-12-13 04:09:04 +01:00
|
|
|
return Provider.of<ThemeModel>(context).push(context,
|
|
|
|
'/${basePaths[0]}/${basePaths[1]}/${basePaths[2]}?path=${y.urlencode}');
|
2019-11-06 14:56:52 +01:00
|
|
|
}
|
|
|
|
|
2019-09-30 14:49:22 +02:00
|
|
|
// TODO: Relative paths
|
|
|
|
if (url.startsWith('https://github.com')) {
|
|
|
|
Map<String, String> m;
|
|
|
|
|
|
|
|
m = matchPattern(url, '/{owner}/{name}/pull/{number}');
|
|
|
|
if (m != null) {
|
2019-12-13 04:09:04 +01:00
|
|
|
return Provider.of<ThemeModel>(context).push(context, url);
|
2019-09-30 14:49:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m = matchPattern(url, '/{owner}/{name}/issues/{number}');
|
|
|
|
if (m != null) {
|
2019-12-13 04:09:04 +01:00
|
|
|
return Provider.of<ThemeModel>(context).push(context, url);
|
2019-09-30 14:49:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m = matchPattern(url, '/{owner}/{name}');
|
|
|
|
if (m != null) {
|
2019-12-13 04:09:04 +01:00
|
|
|
return Provider.of<ThemeModel>(context).push(context, url);
|
2019-09-30 14:49:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m = matchPattern(url, '/{login}');
|
|
|
|
if (m != null) {
|
2019-12-13 04:09:04 +01:00
|
|
|
return Provider.of<ThemeModel>(context).push(context, url);
|
2019-09-30 14:49:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
launchUrl(url);
|
|
|
|
},
|
2019-09-13 10:27:33 +02:00
|
|
|
data: text,
|
|
|
|
styleSheet: MarkdownStyleSheet(
|
2019-11-08 12:56:49 +01:00
|
|
|
a: _basicStyle.copyWith(color: theme.palette.primary),
|
2019-09-13 18:31:33 +02:00
|
|
|
p: _basicStyle,
|
|
|
|
code: _basicStyle.copyWith(
|
|
|
|
fontSize: 16 * 0.85,
|
|
|
|
height: 1.45,
|
2019-10-02 10:09:54 +02:00
|
|
|
fontFamily: CommonStyle.monospace,
|
2019-09-13 18:31:33 +02:00
|
|
|
),
|
|
|
|
h1: _hStyle.copyWith(fontSize: 32),
|
|
|
|
h2: _hStyle.copyWith(fontSize: 24),
|
|
|
|
h3: _hStyle.copyWith(fontSize: 20),
|
|
|
|
h4: _hStyle,
|
|
|
|
h5: _hStyle.copyWith(fontSize: 14),
|
2019-11-08 12:56:49 +01:00
|
|
|
h6: _hStyle.copyWith(
|
|
|
|
fontSize: 16 * 0.85, color: theme.palette.tertiaryText),
|
2019-09-13 18:31:33 +02:00
|
|
|
em: _basicStyle.copyWith(fontStyle: FontStyle.italic),
|
|
|
|
strong: _basicStyle.copyWith(fontWeight: FontWeight.w600),
|
2019-11-30 15:44:55 +01:00
|
|
|
del: const TextStyle(decoration: TextDecoration.lineThrough),
|
2019-11-08 12:56:49 +01:00
|
|
|
blockquote: _basicStyle.copyWith(color: theme.palette.tertiaryText),
|
2019-09-13 18:31:33 +02:00
|
|
|
img: _basicStyle,
|
2019-11-30 15:44:55 +01:00
|
|
|
checkbox: _basicStyle,
|
2019-09-13 18:31:33 +02:00
|
|
|
blockSpacing: 16,
|
|
|
|
listIndent: 32,
|
2019-11-30 15:44:55 +01:00
|
|
|
listBullet: _basicStyle,
|
|
|
|
tableHead: _basicStyle,
|
|
|
|
tableBody: _basicStyle,
|
|
|
|
tableHeadAlign: TextAlign.center,
|
|
|
|
tableBorder: TableBorder.all(color: Colors.grey.shade300, width: 0),
|
|
|
|
tableColumnWidth: const FlexColumnWidth(),
|
|
|
|
tableCellsPadding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
|
2019-11-07 14:46:05 +01:00
|
|
|
blockquotePadding: EdgeInsets.symmetric(horizontal: 16),
|
2019-09-13 10:27:33 +02:00
|
|
|
blockquoteDecoration: BoxDecoration(
|
2019-09-13 18:31:33 +02:00
|
|
|
border: Border(left: BorderSide(color: Color(0xffdfe2e5), width: 4)),
|
|
|
|
),
|
2019-11-07 14:46:05 +01:00
|
|
|
codeblockPadding: EdgeInsets.all(16),
|
2019-09-13 10:27:33 +02:00
|
|
|
codeblockDecoration: BoxDecoration(
|
2019-09-13 18:31:33 +02:00
|
|
|
color: PrimerColors.gray100,
|
|
|
|
borderRadius: BorderRadius.circular(3),
|
|
|
|
),
|
2019-09-13 10:27:33 +02:00
|
|
|
horizontalRuleDecoration: BoxDecoration(
|
2019-09-13 18:31:33 +02:00
|
|
|
border: Border(
|
|
|
|
top: BorderSide(width: 4, color: PrimerColors.gray200),
|
|
|
|
),
|
2019-09-13 10:27:33 +02:00
|
|
|
),
|
|
|
|
),
|
|
|
|
// syntaxHighlighter: , // TODO:
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|