mirror of
https://github.com/pd4d10/git-touch.git
synced 2026-03-12 02:06:17 -05:00
feat: gists screen (#80)
closes: #66 Co-authored-by: Rongjian Zhang <pd4d10@gmail.com>
This commit is contained in:
committed by
GitHub
parent
7a11dca621
commit
094ad4b1bb
@@ -326,13 +326,47 @@ class GithubUserOrganizationItem {
|
||||
_$GithubUserOrganizationItemFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class GistFiles {
|
||||
GistFiles({
|
||||
this.filename,
|
||||
this.size,
|
||||
this.rawUrl,
|
||||
this.type,
|
||||
this.language,
|
||||
this.truncated,
|
||||
this.content,
|
||||
});
|
||||
String filename;
|
||||
int size;
|
||||
String rawUrl;
|
||||
String type;
|
||||
String language;
|
||||
bool truncated;
|
||||
String content;
|
||||
|
||||
factory GistFiles.fromJson(Map<String, dynamic> json) =>
|
||||
_$GistFilesFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class GithubGistsItem {
|
||||
int id;
|
||||
bool isFork;
|
||||
bool isPublic;
|
||||
String name;
|
||||
String id;
|
||||
String description;
|
||||
bool public;
|
||||
Map<String, GistFiles> files;
|
||||
GithubEventUser owner;
|
||||
List<GistFiles> get fileNames {
|
||||
List<GistFiles> filenames = [];
|
||||
files.forEach((String key, GistFiles value) {
|
||||
filenames.add(value);
|
||||
});
|
||||
return filenames;
|
||||
}
|
||||
|
||||
DateTime createdAt;
|
||||
DateTime updatedAt;
|
||||
|
||||
GithubGistsItem();
|
||||
factory GithubGistsItem.fromJson(Map<String, dynamic> json) =>
|
||||
_$GithubGistsItemFromJson(json);
|
||||
|
||||
@@ -461,12 +461,43 @@ Map<String, dynamic> _$GithubUserOrganizationItemToJson(
|
||||
'url': instance.url,
|
||||
};
|
||||
|
||||
GistFiles _$GistFilesFromJson(Map<String, dynamic> json) {
|
||||
return GistFiles(
|
||||
filename: json['filename'] as String,
|
||||
size: json['size'] as int,
|
||||
rawUrl: json['raw_url'] as String,
|
||||
type: json['type'] as String,
|
||||
language: json['language'] as String,
|
||||
truncated: json['truncated'] as bool,
|
||||
content: json['content'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$GistFilesToJson(GistFiles instance) => <String, dynamic>{
|
||||
'filename': instance.filename,
|
||||
'size': instance.size,
|
||||
'raw_url': instance.rawUrl,
|
||||
'type': instance.type,
|
||||
'language': instance.language,
|
||||
'truncated': instance.truncated,
|
||||
'content': instance.content,
|
||||
};
|
||||
|
||||
GithubGistsItem _$GithubGistsItemFromJson(Map<String, dynamic> json) {
|
||||
return GithubGistsItem()
|
||||
..id = json['id'] as int
|
||||
..isFork = json['is_fork'] as bool
|
||||
..isPublic = json['is_public'] as bool
|
||||
..name = json['name'] as String
|
||||
..id = json['id'] as String
|
||||
..description = json['description'] as String
|
||||
..public = json['public'] as bool
|
||||
..files = (json['files'] as Map<String, dynamic>)?.map(
|
||||
(k, e) => MapEntry(
|
||||
k, e == null ? null : GistFiles.fromJson(e as Map<String, dynamic>)),
|
||||
)
|
||||
..owner = json['owner'] == null
|
||||
? null
|
||||
: GithubEventUser.fromJson(json['owner'] as Map<String, dynamic>)
|
||||
..createdAt = json['created_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['created_at'] as String)
|
||||
..updatedAt = json['updated_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['updated_at'] as String);
|
||||
@@ -475,9 +506,11 @@ GithubGistsItem _$GithubGistsItemFromJson(Map<String, dynamic> json) {
|
||||
Map<String, dynamic> _$GithubGistsItemToJson(GithubGistsItem instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'is_fork': instance.isFork,
|
||||
'is_public': instance.isPublic,
|
||||
'name': instance.name,
|
||||
'description': instance.description,
|
||||
'public': instance.public,
|
||||
'files': instance.files,
|
||||
'owner': instance.owner,
|
||||
'created_at': instance.createdAt?.toIso8601String(),
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:git_touch/screens/code_theme.dart';
|
||||
import 'package:git_touch/screens/gh_commits.dart';
|
||||
import 'package:git_touch/screens/gh_contributors.dart';
|
||||
import 'package:git_touch/screens/gh_files.dart';
|
||||
import 'package:git_touch/screens/gh_gists_files.dart';
|
||||
import 'package:git_touch/screens/gh_org_repos.dart';
|
||||
import 'package:git_touch/screens/gl_commit.dart';
|
||||
import 'package:git_touch/screens/gl_starrers.dart';
|
||||
@@ -38,8 +39,9 @@ import 'package:git_touch/screens/settings.dart';
|
||||
import 'package:git_touch/screens/gh_user.dart';
|
||||
import 'package:git_touch/screens/gh_users.dart';
|
||||
import 'package:git_touch/screens/gh_user_organization.dart';
|
||||
import 'package:git_touch/screens/gh_gists.dart';
|
||||
import 'package:git_touch/screens/gh_gist_object.dart';
|
||||
import 'package:git_touch/screens/gh_compare.dart';
|
||||
// import 'package:git_touch/screens/gh_gists.dart';
|
||||
|
||||
class RouterScreen {
|
||||
String path;
|
||||
@@ -78,6 +80,8 @@ class GithubRouter {
|
||||
GithubRouter.watchers,
|
||||
GithubRouter.contributors,
|
||||
GithubRouter.files,
|
||||
GithubRouter.gistFiles,
|
||||
GithubRouter.gistObject,
|
||||
GithubRouter.compare,
|
||||
];
|
||||
static final user = RouterScreen('/:login', (_, p) {
|
||||
@@ -99,7 +103,7 @@ class GithubRouter {
|
||||
case 'organizations':
|
||||
return GhUserOrganizationScreen(login);
|
||||
case 'gists':
|
||||
// return GhGistsScreen(login);
|
||||
return GhGistsScreen(login);
|
||||
default:
|
||||
return GhUserScreen(login);
|
||||
}
|
||||
@@ -112,6 +116,18 @@ class GithubRouter {
|
||||
branch: p['ref'].first);
|
||||
}
|
||||
});
|
||||
static final gistObject = RouterScreen('/:login/gists/:id/:file', (_, p) {
|
||||
return GistObjectScreen(
|
||||
p['login'].first,
|
||||
p['id'].first,
|
||||
p['file'].first,
|
||||
raw: p['raw']?.first,
|
||||
content: p['content'].first,
|
||||
);
|
||||
});
|
||||
static final gistFiles = RouterScreen('/:login/gists/:id', (_, p) {
|
||||
return GhGistsFilesScreen(p['login'].first, p['id'].first);
|
||||
});
|
||||
static final issueAdd = RouterScreen('/:owner/:name/issues/new', (_, p) {
|
||||
return GhIssueFormScreen(p['owner'].first, p['name'].first);
|
||||
});
|
||||
|
||||
31
lib/screens/gh_gist_object.dart
Normal file
31
lib/screens/gh_gist_object.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:git_touch/scaffolds/common.dart';
|
||||
import 'package:git_touch/widgets/app_bar_title.dart';
|
||||
import 'package:git_touch/widgets/blob_view.dart';
|
||||
import 'package:git_touch/widgets/action_entry.dart';
|
||||
|
||||
class GistObjectScreen extends StatelessWidget {
|
||||
final String login;
|
||||
final String id;
|
||||
final String file;
|
||||
final String raw;
|
||||
final String content;
|
||||
|
||||
GistObjectScreen(this.login, this.id, this.file, {this.raw, this.content});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonScaffold(
|
||||
title: AppBarTitle(file),
|
||||
action: ActionEntry(
|
||||
iconData: Icons.settings,
|
||||
url: '/choose-code-theme',
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: BlobView(
|
||||
file,
|
||||
text: content,
|
||||
)));
|
||||
}
|
||||
}
|
||||
48
lib/screens/gh_gists.dart
Normal file
48
lib/screens/gh_gists.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:git_touch/models/github.dart';
|
||||
import 'package:git_touch/scaffolds/list_stateful.dart';
|
||||
import 'package:git_touch/widgets/app_bar_title.dart';
|
||||
import 'package:git_touch/widgets/gists_item.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:git_touch/models/auth.dart';
|
||||
|
||||
class GhGistsScreen extends StatelessWidget {
|
||||
final String login;
|
||||
GhGistsScreen(this.login);
|
||||
|
||||
Future<ListPayload<GithubGistsItem, int>> _query(BuildContext context,
|
||||
[int page = 1]) async {
|
||||
final auth = Provider.of<AuthModel>(context);
|
||||
final res = await auth.ghClient.getJSON<List, List<GithubGistsItem>>(
|
||||
'/users/$login/gists?page=$page',
|
||||
convert: (vs) => [for (var v in vs) GithubGistsItem.fromJson(v)],
|
||||
);
|
||||
return ListPayload(
|
||||
cursor: page + 1,
|
||||
items: res,
|
||||
hasMore: res.isNotEmpty,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListStatefulScaffold<GithubGistsItem, int>(
|
||||
title: AppBarTitle('Gists'),
|
||||
onRefresh: () => _query(context),
|
||||
onLoadMore: (cursor) => _query(context, cursor),
|
||||
itemBuilder: (v) {
|
||||
return GistsItem(
|
||||
description: v.description,
|
||||
login: login,
|
||||
files: v.files,
|
||||
filenames: v.fileNames,
|
||||
language: v.fileNames[0].language,
|
||||
avatarUrl: v.owner.avatarUrl,
|
||||
updatedAt: v.updatedAt,
|
||||
id: v.id,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
49
lib/screens/gh_gists_files.dart
Normal file
49
lib/screens/gh_gists_files.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:git_touch/models/github.dart';
|
||||
import 'package:git_touch/scaffolds/refresh_stateful.dart';
|
||||
import 'package:git_touch/widgets/app_bar_title.dart';
|
||||
import 'package:git_touch/widgets/object_tree.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:git_touch/models/auth.dart';
|
||||
|
||||
class GhGistsFilesScreen extends StatelessWidget {
|
||||
final String id;
|
||||
final String login;
|
||||
GhGistsFilesScreen(this.login, this.id);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final auth = Provider.of<AuthModel>(context);
|
||||
return RefreshStatefulScaffold<GithubGistsItem>(
|
||||
title: AppBarTitle('Files'),
|
||||
fetchData: () async {
|
||||
final data = await auth.ghClient.getJSON(
|
||||
'/gists/$id',
|
||||
convert: (vs) => GithubGistsItem.fromJson(vs),
|
||||
);
|
||||
return data;
|
||||
},
|
||||
bodyBuilder: (payload, _) {
|
||||
return ObjectTree(
|
||||
items: payload.fileNames.map((v) {
|
||||
final uri = Uri(
|
||||
path: '/github/$login/gists/$id/${v.filename}',
|
||||
queryParameters: {
|
||||
'content': v.content,
|
||||
...(v.rawUrl == null ? {} : {'raw': v.rawUrl}),
|
||||
},
|
||||
).toString();
|
||||
return ObjectTreeItem(
|
||||
url: uri,
|
||||
type: 'file',
|
||||
name: v.filename,
|
||||
downloadUrl: v.rawUrl,
|
||||
size: v.size,
|
||||
);
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:git_touch/graphql/gh.dart';
|
||||
import 'package:git_touch/models/github.dart';
|
||||
import 'package:git_touch/models/theme.dart';
|
||||
import 'package:git_touch/scaffolds/refresh_stateful.dart';
|
||||
import 'package:git_touch/utils/utils.dart';
|
||||
@@ -152,6 +153,10 @@ class GhUserScreen extends StatelessWidget {
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.book,
|
||||
text: Text('Gists'),
|
||||
url: '/github/$login?tab=gists'),
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.home,
|
||||
text: Text('Organizations'),
|
||||
|
||||
130
lib/widgets/gists_item.dart
Normal file
130
lib/widgets/gists_item.dart
Normal file
@@ -0,0 +1,130 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:git_touch/models/github.dart';
|
||||
import 'package:git_touch/models/theme.dart';
|
||||
import 'package:git_touch/utils/utils.dart';
|
||||
import 'package:git_touch/widgets/avatar.dart';
|
||||
import 'package:git_touch/widgets/link.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:github/src/const/language_color.dart';
|
||||
import 'package:timeago/timeago.dart' as timeago;
|
||||
|
||||
class GistsItem extends StatelessWidget {
|
||||
final String description;
|
||||
final String login;
|
||||
final Map<String, GistFiles> files;
|
||||
final List<GistFiles> filenames;
|
||||
final String language;
|
||||
final String avatarUrl;
|
||||
final DateTime updatedAt;
|
||||
final String id;
|
||||
|
||||
GistsItem({
|
||||
@required this.description,
|
||||
@required this.login,
|
||||
@required this.files,
|
||||
@required this.filenames,
|
||||
@required this.language,
|
||||
@required this.avatarUrl,
|
||||
@required this.updatedAt,
|
||||
@required this.id,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Provider.of<ThemeModel>(context);
|
||||
return Link(
|
||||
url: '/github/$login/gists/$id',
|
||||
child: Container(
|
||||
padding: CommonStyle.padding,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Avatar(
|
||||
url: avatarUrl,
|
||||
size: AvatarSize.small,
|
||||
linkUrl: '/github/$login',
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(children: [
|
||||
TextSpan(
|
||||
text: '$login / ',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: theme.palette.primary,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: filenames[0].filename,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: theme.palette.primary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
]),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
if (description != null && description.isNotEmpty) ...[
|
||||
Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
color: theme.palette.secondaryText,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
],
|
||||
if (updatedAt != null) ...[
|
||||
Text(
|
||||
'Updated ${timeago.format(updatedAt)}',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.palette.tertiaryText,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
],
|
||||
DefaultTextStyle(
|
||||
style: TextStyle(color: theme.palette.text, fontSize: 14),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
if (language != null)
|
||||
Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
color: convertColor(languagesColor[language]),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
if (language != null)
|
||||
Text(
|
||||
language,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
SizedBox(width: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user