mirror of
https://github.com/git-touch/git-touch
synced 2024-12-16 10:20:55 +01:00
feat: issue screen style
This commit is contained in:
parent
321ab0a60a
commit
1108346163
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:git_touch/models/theme.dart';
|
||||
import 'package:git_touch/utils/utils.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../widgets/loading.dart';
|
||||
import '../widgets/link.dart';
|
||||
@ -102,11 +103,7 @@ class _LongListScaffoldState<T, K> extends State<LongListScaffold<T, K>> {
|
||||
|
||||
Widget _buildItem(BuildContext context, int index) {
|
||||
if (index % 2 == 1) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: Colors.black12)),
|
||||
),
|
||||
);
|
||||
return BorderView();
|
||||
}
|
||||
|
||||
int realIndex = index ~/ 2;
|
||||
|
@ -360,40 +360,41 @@ mutation {
|
||||
);
|
||||
},
|
||||
headerBuilder: (payload) {
|
||||
return Column(children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: Colors.black12)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
payload['title'],
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
payload['title'],
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(padding: EdgeInsets.only(right: 8)),
|
||||
StateLabel(_getLabelStatus(payload))
|
||||
],
|
||||
),
|
||||
Padding(padding: EdgeInsets.only(bottom: 16)),
|
||||
CommentItem(
|
||||
payload,
|
||||
onReaction: _handleReaction(payload),
|
||||
),
|
||||
],
|
||||
Padding(padding: EdgeInsets.only(right: 8)),
|
||||
StateLabel(_getLabelStatus(payload))
|
||||
],
|
||||
),
|
||||
Padding(padding: EdgeInsets.only(bottom: 16)),
|
||||
CommentItem(
|
||||
payload,
|
||||
onReaction: _handleReaction(payload),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
]);
|
||||
BorderView(),
|
||||
],
|
||||
);
|
||||
},
|
||||
itemBuilder: (itemPayload) =>
|
||||
TimelineItem(itemPayload, onReaction: _handleReaction(itemPayload)),
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:git_touch/screens/user.dart';
|
||||
import 'package:primer/primer.dart';
|
||||
import '../providers/settings.dart';
|
||||
import '../screens/repo.dart';
|
||||
@ -41,6 +42,7 @@ TextSpan createLinkSpan(BuildContext context, String text, Function handle) {
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
Navigator.of(context).push(
|
||||
// FIXME: Material route
|
||||
CupertinoPageRoute(
|
||||
builder: (context) {
|
||||
return handle();
|
||||
@ -51,6 +53,10 @@ TextSpan createLinkSpan(BuildContext context, String text, Function handle) {
|
||||
);
|
||||
}
|
||||
|
||||
TextSpan createUserSpan(BuildContext context, String login) {
|
||||
return createLinkSpan(context, login, () => UserScreen(login));
|
||||
}
|
||||
|
||||
TextSpan createRepoLinkSpan(BuildContext context, String owner, String name) {
|
||||
return createLinkSpan(context, '$owner/$name', () => RepoScreen(owner, name));
|
||||
}
|
||||
|
@ -48,13 +48,13 @@ class CommentItem extends StatelessWidget {
|
||||
size: 16,
|
||||
login: payload['author']['login'],
|
||||
),
|
||||
Padding(padding: EdgeInsets.only(left: 6)),
|
||||
SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
UserName(payload['author']['login']),
|
||||
Padding(padding: EdgeInsets.only(bottom: 2)),
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
timeago.format(DateTime.parse(payload['createdAt'])),
|
||||
style: TextStyle(color: Colors.black54, fontSize: 13),
|
||||
@ -63,72 +63,72 @@ class CommentItem extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
]),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: MarkdownBody(
|
||||
data: payload['body'],
|
||||
// styleSheet: MarkdownStyleSheet(code: TextStyle(fontSize: 14)),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
MarkdownBody(
|
||||
data: payload['body'] as String,
|
||||
// styleSheet: MarkdownStyleSheet(code: TextStyle(fontSize: 14)),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Wrap(
|
||||
children: emojiMap.entries
|
||||
.where((entry) => payload[entry.key]['totalCount'] as int != 0)
|
||||
.map<Widget>((entry) {
|
||||
var emojiKey = entry.key;
|
||||
var emoji = entry.value;
|
||||
var count = payload[entry.key]['totalCount'] as int;
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
...emojiMap.entries
|
||||
.where((entry) => payload[entry.key]['totalCount'] as int != 0)
|
||||
.map<Widget>((entry) {
|
||||
var emojiKey = entry.key;
|
||||
var emoji = entry.value;
|
||||
var count = payload[entry.key]['totalCount'] as int;
|
||||
|
||||
return Link(
|
||||
onTap: () {
|
||||
onReaction(emojiKey, _hasReacted(emojiKey));
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(6),
|
||||
decoration: _getDecorationByKey(emojiKey),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
style: TextStyle(fontSize: 16),
|
||||
children: [
|
||||
TextSpan(text: emoji),
|
||||
TextSpan(text: ' '),
|
||||
TextSpan(
|
||||
text: count.toString(),
|
||||
style: TextStyle(color: PrimerColors.blue500),
|
||||
),
|
||||
return Link(
|
||||
onTap: () {
|
||||
onReaction(emojiKey, _hasReacted(emojiKey));
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(4),
|
||||
decoration: _getDecorationByKey(emojiKey),
|
||||
child: Wrap(
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(emoji, style: TextStyle(fontSize: 18)),
|
||||
SizedBox(width: 4),
|
||||
Text(count.toString(),
|
||||
style: TextStyle(
|
||||
color: PrimerColors.blue500, fontSize: 14))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList()
|
||||
..add(
|
||||
Link(
|
||||
onTap: () async {
|
||||
var result = await Provider.of<ThemeModel>(context)
|
||||
.showDialogOptions(
|
||||
context,
|
||||
emojiMap.entries.map((entry) {
|
||||
var emojiKey = entry.key;
|
||||
return DialogOption(
|
||||
value: emojiKey,
|
||||
widget: Container(
|
||||
decoration: _getDecorationByKey(emojiKey),
|
||||
child: Text(emojiKey + ' ' + entry.value),
|
||||
),
|
||||
);
|
||||
}).toList());
|
||||
onReaction(result, _hasReacted(result));
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Icon(
|
||||
Octicons.smiley,
|
||||
color: PrimerColors.blue500,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
Link(
|
||||
onTap: () async {
|
||||
var result =
|
||||
await Provider.of<ThemeModel>(context).showDialogOptions(
|
||||
context,
|
||||
emojiMap.entries.map((entry) {
|
||||
var emojiKey = entry.key;
|
||||
return DialogOption(
|
||||
value: emojiKey,
|
||||
widget: Container(
|
||||
decoration: _getDecorationByKey(emojiKey),
|
||||
child: Text(emojiKey + ' ' + entry.value),
|
||||
),
|
||||
);
|
||||
}).toList());
|
||||
onReaction(result, _hasReacted(result));
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(4),
|
||||
child: Wrap(
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: <Widget>[
|
||||
Text('+', style: TextStyle(color: PrimerColors.blue500)),
|
||||
Icon(Octicons.smiley,
|
||||
color: PrimerColors.blue500, size: 18),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -6,6 +6,43 @@ import '../utils/utils.dart';
|
||||
import 'comment_item.dart';
|
||||
import 'user_name.dart';
|
||||
|
||||
class TimelineEventItem extends StatelessWidget {
|
||||
final String actor;
|
||||
final IconData iconData;
|
||||
final Color iconColor;
|
||||
final TextSpan textSpan;
|
||||
final item;
|
||||
|
||||
TimelineEventItem({
|
||||
this.actor,
|
||||
this.iconData = Octicons.octoface,
|
||||
this.iconColor = PrimerColors.gray400,
|
||||
this.textSpan,
|
||||
this.item,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
SizedBox(width: 6),
|
||||
Icon(iconData, color: iconColor, size: 20),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: RichText(
|
||||
text: TextSpan(style: TextStyle(color: Colors.black), children: [
|
||||
// TODO: actor is null
|
||||
createUserSpan(context, actor),
|
||||
textSpan,
|
||||
// TextSpan(text: ' ' + TimeAgo.formatFromString(item['createdAt']))
|
||||
]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TimelineItem extends StatelessWidget {
|
||||
final Map<String, dynamic> payload;
|
||||
final Function(String emojiKey, bool isRemove) onReaction;
|
||||
@ -23,32 +60,6 @@ class TimelineItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildItem({
|
||||
String actor,
|
||||
IconData iconData = Octicons.octoface,
|
||||
Color iconColor = PrimerColors.gray400,
|
||||
TextSpan textSpan,
|
||||
item,
|
||||
}) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Padding(padding: EdgeInsets.only(left: 6)),
|
||||
Icon(iconData, color: iconColor, size: 20),
|
||||
Padding(padding: EdgeInsets.only(left: 12)),
|
||||
Expanded(
|
||||
child: RichText(
|
||||
text: TextSpan(style: TextStyle(color: Colors.black), children: [
|
||||
// TODO: actor is null
|
||||
createUserSpan(actor),
|
||||
textSpan,
|
||||
// TextSpan(text: ' ' + TimeAgo.formatFromString(item['createdAt']))
|
||||
]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
TextSpan _buildLabel(item) {
|
||||
var color = convertColor(item['label']['color']);
|
||||
var grayscale = color.red * 0.3 + color.green * 0.59 + color.blue * 0.11;
|
||||
@ -73,7 +84,7 @@ class TimelineItem extends StatelessWidget {
|
||||
Widget _buildByType(BuildContext context) {
|
||||
String type = payload['__typename'];
|
||||
|
||||
var defaultItem = _buildItem(
|
||||
var defaultItem = TimelineEventItem(
|
||||
actor: '',
|
||||
iconData: Octicons.octoface,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -85,7 +96,7 @@ class TimelineItem extends StatelessWidget {
|
||||
switch (type) {
|
||||
// common types
|
||||
case 'Commit':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['author']['user'] == null
|
||||
? null
|
||||
: payload['author']['user']['login'],
|
||||
@ -99,7 +110,7 @@ class TimelineItem extends StatelessWidget {
|
||||
case 'IssueComment':
|
||||
return CommentItem(payload, onReaction: onReaction);
|
||||
case 'CrossReferencedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.primitive_dot,
|
||||
iconColor: Palette.green,
|
||||
@ -109,7 +120,7 @@ class TimelineItem extends StatelessWidget {
|
||||
item: payload,
|
||||
);
|
||||
case 'ClosedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.circle_slash,
|
||||
iconColor: PrimerColors.red600,
|
||||
@ -118,7 +129,7 @@ class TimelineItem extends StatelessWidget {
|
||||
);
|
||||
|
||||
case 'ReopenedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.primitive_dot,
|
||||
iconColor: Palette.green,
|
||||
@ -134,7 +145,7 @@ class TimelineItem extends StatelessWidget {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.bookmark,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -144,7 +155,7 @@ class TimelineItem extends StatelessWidget {
|
||||
item: payload,
|
||||
);
|
||||
case 'AssignedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.key,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -156,7 +167,7 @@ class TimelineItem extends StatelessWidget {
|
||||
case 'UnassignedEvent':
|
||||
return defaultItem; // TODO:
|
||||
case 'LabeledEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.tag,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -167,7 +178,7 @@ class TimelineItem extends StatelessWidget {
|
||||
item: payload,
|
||||
);
|
||||
case 'UnlabeledEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.tag,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -179,7 +190,7 @@ class TimelineItem extends StatelessWidget {
|
||||
);
|
||||
|
||||
case 'MilestonedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.milestone,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -192,7 +203,7 @@ class TimelineItem extends StatelessWidget {
|
||||
case 'DemilestonedEvent':
|
||||
return defaultItem; // TODO:
|
||||
case 'RenamedTitleEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.pencil,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -207,7 +218,7 @@ class TimelineItem extends StatelessWidget {
|
||||
item: payload,
|
||||
);
|
||||
case 'LockedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.lock,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -216,7 +227,7 @@ class TimelineItem extends StatelessWidget {
|
||||
item: payload,
|
||||
);
|
||||
case 'UnlockedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.key,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -233,7 +244,7 @@ class TimelineItem extends StatelessWidget {
|
||||
case 'CommitCommentThread':
|
||||
return defaultItem; // TODO:
|
||||
case 'PullRequestReview':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['author']['login'],
|
||||
iconColor: Color(0xff28a745),
|
||||
iconData: Octicons.check,
|
||||
@ -244,7 +255,7 @@ class TimelineItem extends StatelessWidget {
|
||||
case 'PullRequestReviewComment':
|
||||
return defaultItem; // TODO:
|
||||
case 'MergedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.git_merge,
|
||||
iconColor: Color(0xff6f42c1),
|
||||
@ -260,7 +271,7 @@ class TimelineItem extends StatelessWidget {
|
||||
case 'DeploymentEnvironmentChangedEvent':
|
||||
return defaultItem; // TODO:
|
||||
case 'HeadRefDeletedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
actor: payload['actor']['login'],
|
||||
iconData: Octicons.git_branch,
|
||||
textSpan: TextSpan(children: [
|
||||
@ -275,14 +286,14 @@ class TimelineItem extends StatelessWidget {
|
||||
case 'BaseRefForcePushedEvent':
|
||||
return defaultItem; // TODO:
|
||||
case 'ReviewRequestedEvent':
|
||||
return _buildItem(
|
||||
return TimelineEventItem(
|
||||
iconData: Octicons.eye,
|
||||
// actor: payload['author']['login'],
|
||||
// TODO:
|
||||
actor: 'test',
|
||||
textSpan: TextSpan(children: [
|
||||
TextSpan(text: ' requested a review from '),
|
||||
createUserSpan(payload['requestedReviewer']['login']),
|
||||
createUserSpan(context, payload['requestedReviewer']['login']),
|
||||
]),
|
||||
item: payload,
|
||||
);
|
||||
|
@ -5,10 +5,6 @@ import 'link.dart';
|
||||
|
||||
final style = TextStyle(fontWeight: FontWeight.w600);
|
||||
|
||||
TextSpan createUserSpan(String login) {
|
||||
return TextSpan(text: login, style: style);
|
||||
}
|
||||
|
||||
class UserName extends StatelessWidget {
|
||||
final String login;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user