mirror of
https://github.com/git-touch/git-touch
synced 2025-02-02 08:56:54 +01:00
parent
70e19e9b72
commit
242dd1200d
@ -289,7 +289,7 @@ class AuthModel with ChangeNotifier {
|
||||
Future<DataWithPage> fetchGiteaWithPage(String path,
|
||||
{int? page, int? limit}) async {
|
||||
page = page ?? 1;
|
||||
limit = limit ?? pageSize;
|
||||
limit = limit ?? PAGE_SIZE;
|
||||
|
||||
var uri = Uri.parse('${activeAccount!.domain}/api/v1$path');
|
||||
uri = uri.replace(
|
||||
@ -393,7 +393,7 @@ class AuthModel with ChangeNotifier {
|
||||
Future<DataWithPage> fetchGogsWithPage(String path,
|
||||
{int? page, int? limit}) async {
|
||||
page = page ?? 1;
|
||||
limit = limit ?? pageSize;
|
||||
limit = limit ?? PAGE_SIZE;
|
||||
|
||||
var uri = Uri.parse('${activeAccount!.domain}/api/v1$path');
|
||||
uri = uri.replace(
|
||||
@ -480,7 +480,7 @@ class AuthModel with ChangeNotifier {
|
||||
Future<DataWithPage> fetchGiteeWithPage(String path,
|
||||
{int? page, int? limit}) async {
|
||||
page = page ?? 1;
|
||||
limit = limit ?? pageSize;
|
||||
limit = limit ?? PAGE_SIZE;
|
||||
|
||||
var uri = Uri.parse('${activeAccount!.domain}/api/v5$path');
|
||||
uri = uri.replace(
|
||||
@ -545,7 +545,10 @@ class AuthModel with ChangeNotifier {
|
||||
final uri = Uri.parse(activeAccount!.domain).replace(
|
||||
userInfo: '${activeAccount!.login}:${activeAccount!.appPassword}',
|
||||
path: input.path,
|
||||
query: input.query,
|
||||
queryParameters: {
|
||||
'pagelen': PAGE_SIZE.toString(),
|
||||
...input.queryParameters
|
||||
},
|
||||
);
|
||||
if (isPost) {
|
||||
return http.post(
|
||||
|
@ -65,11 +65,11 @@ class BbRepoMainbranch {
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class BbTree {
|
||||
String? type;
|
||||
String? path;
|
||||
String type;
|
||||
String path;
|
||||
int? size;
|
||||
Map<String, dynamic>? links;
|
||||
BbTree();
|
||||
BbTree({required this.type, required this.path});
|
||||
factory BbTree.fromJson(Map<String, dynamic> json) => _$BbTreeFromJson(json);
|
||||
}
|
||||
|
||||
|
@ -115,9 +115,10 @@ Map<String, dynamic> _$BbRepoMainbranchToJson(BbRepoMainbranch instance) =>
|
||||
};
|
||||
|
||||
BbTree _$BbTreeFromJson(Map<String, dynamic> json) {
|
||||
return BbTree()
|
||||
..type = json['type'] as String?
|
||||
..path = json['path'] as String?
|
||||
return BbTree(
|
||||
type: json['type'] as String,
|
||||
path: json['path'] as String,
|
||||
)
|
||||
..size = json['size'] as int?
|
||||
..links = json['links'] as Map<String, dynamic>?;
|
||||
}
|
||||
|
@ -48,12 +48,12 @@ class GiteaRepository {
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class GiteaTree {
|
||||
String? type;
|
||||
String? name;
|
||||
String type;
|
||||
String name;
|
||||
String? path;
|
||||
int? size;
|
||||
String? downloadUrl;
|
||||
GiteaTree();
|
||||
GiteaTree({required this.type, required this.name});
|
||||
factory GiteaTree.fromJson(Map<String, dynamic> json) =>
|
||||
_$GiteaTreeFromJson(json);
|
||||
}
|
||||
@ -61,7 +61,8 @@ class GiteaTree {
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class GiteaBlob extends GiteaTree {
|
||||
String? content;
|
||||
GiteaBlob();
|
||||
GiteaBlob({required String type, required String name})
|
||||
: super(name: name, type: type);
|
||||
factory GiteaBlob.fromJson(Map<String, dynamic> json) =>
|
||||
_$GiteaBlobFromJson(json);
|
||||
}
|
||||
|
@ -81,9 +81,10 @@ Map<String, dynamic> _$GiteaRepositoryToJson(GiteaRepository instance) =>
|
||||
};
|
||||
|
||||
GiteaTree _$GiteaTreeFromJson(Map<String, dynamic> json) {
|
||||
return GiteaTree()
|
||||
..type = json['type'] as String?
|
||||
..name = json['name'] as String?
|
||||
return GiteaTree(
|
||||
type: json['type'] as String,
|
||||
name: json['name'] as String,
|
||||
)
|
||||
..path = json['path'] as String?
|
||||
..size = json['size'] as int?
|
||||
..downloadUrl = json['download_url'] as String?;
|
||||
@ -98,9 +99,10 @@ Map<String, dynamic> _$GiteaTreeToJson(GiteaTree instance) => <String, dynamic>{
|
||||
};
|
||||
|
||||
GiteaBlob _$GiteaBlobFromJson(Map<String, dynamic> json) {
|
||||
return GiteaBlob()
|
||||
..type = json['type'] as String?
|
||||
..name = json['name'] as String?
|
||||
return GiteaBlob(
|
||||
type: json['type'] as String,
|
||||
name: json['name'] as String,
|
||||
)
|
||||
..path = json['path'] as String?
|
||||
..size = json['size'] as int?
|
||||
..downloadUrl = json['download_url'] as String?
|
||||
|
@ -107,11 +107,11 @@ class GiteeCommitAuthor {
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class GiteeTreeItem {
|
||||
String? path;
|
||||
String? type;
|
||||
String path;
|
||||
String type;
|
||||
String? sha;
|
||||
int? size;
|
||||
GiteeTreeItem();
|
||||
GiteeTreeItem({required this.path, required this.type});
|
||||
factory GiteeTreeItem.fromJson(Map<String, dynamic> json) =>
|
||||
_$GiteeTreeItemFromJson(json);
|
||||
}
|
||||
|
@ -181,9 +181,10 @@ Map<String, dynamic> _$GiteeCommitAuthorToJson(GiteeCommitAuthor instance) =>
|
||||
};
|
||||
|
||||
GiteeTreeItem _$GiteeTreeItemFromJson(Map<String, dynamic> json) {
|
||||
return GiteeTreeItem()
|
||||
..path = json['path'] as String?
|
||||
..type = json['type'] as String?
|
||||
return GiteeTreeItem(
|
||||
path: json['path'] as String,
|
||||
type: json['type'] as String,
|
||||
)
|
||||
..sha = json['sha'] as String?
|
||||
..size = json['size'] as int?;
|
||||
}
|
||||
|
@ -128,10 +128,10 @@ class GitlabProjectNamespace {
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class GitlabTreeItem {
|
||||
String? type;
|
||||
String? path;
|
||||
String? name;
|
||||
GitlabTreeItem();
|
||||
String type;
|
||||
String path;
|
||||
String name;
|
||||
GitlabTreeItem({required this.type, required this.path, required this.name});
|
||||
factory GitlabTreeItem.fromJson(Map<String, dynamic> json) =>
|
||||
_$GitlabTreeItemFromJson(json);
|
||||
}
|
||||
|
@ -229,10 +229,11 @@ Map<String, dynamic> _$GitlabProjectNamespaceToJson(
|
||||
};
|
||||
|
||||
GitlabTreeItem _$GitlabTreeItemFromJson(Map<String, dynamic> json) {
|
||||
return GitlabTreeItem()
|
||||
..type = json['type'] as String?
|
||||
..path = json['path'] as String?
|
||||
..name = json['name'] as String?;
|
||||
return GitlabTreeItem(
|
||||
type: json['type'] as String,
|
||||
path: json['path'] as String,
|
||||
name: json['name'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$GitlabTreeItemToJson(GitlabTreeItem instance) =>
|
||||
|
@ -50,12 +50,12 @@ class GogsOrg {
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class GogsTree {
|
||||
String? type;
|
||||
String? name;
|
||||
String type;
|
||||
String name;
|
||||
String? path;
|
||||
int? size;
|
||||
String? downloadUrl;
|
||||
GogsTree();
|
||||
GogsTree({required this.type, required this.name});
|
||||
factory GogsTree.fromJson(Map<String, dynamic> json) =>
|
||||
_$GogsTreeFromJson(json);
|
||||
}
|
||||
@ -63,7 +63,8 @@ class GogsTree {
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class GogsBlob extends GogsTree {
|
||||
String? content;
|
||||
GogsBlob();
|
||||
GogsBlob({required String type, required String name})
|
||||
: super(name: name, type: type);
|
||||
factory GogsBlob.fromJson(Map<String, dynamic> json) =>
|
||||
_$GogsBlobFromJson(json);
|
||||
}
|
||||
|
@ -85,9 +85,10 @@ Map<String, dynamic> _$GogsOrgToJson(GogsOrg instance) => <String, dynamic>{
|
||||
};
|
||||
|
||||
GogsTree _$GogsTreeFromJson(Map<String, dynamic> json) {
|
||||
return GogsTree()
|
||||
..type = json['type'] as String?
|
||||
..name = json['name'] as String?
|
||||
return GogsTree(
|
||||
type: json['type'] as String,
|
||||
name: json['name'] as String,
|
||||
)
|
||||
..path = json['path'] as String?
|
||||
..size = json['size'] as int?
|
||||
..downloadUrl = json['download_url'] as String?;
|
||||
@ -102,9 +103,10 @@ Map<String, dynamic> _$GogsTreeToJson(GogsTree instance) => <String, dynamic>{
|
||||
};
|
||||
|
||||
GogsBlob _$GogsBlobFromJson(Map<String, dynamic> json) {
|
||||
return GogsBlob()
|
||||
..type = json['type'] as String?
|
||||
..name = json['name'] as String?
|
||||
return GogsBlob(
|
||||
type: json['type'] as String,
|
||||
name: json['name'] as String,
|
||||
)
|
||||
..path = json['path'] as String?
|
||||
..size = json['size'] as int?
|
||||
..downloadUrl = json['download_url'] as String?
|
||||
|
@ -19,8 +19,8 @@ class ListStatefulScaffold<T, K> extends StatefulWidget {
|
||||
|
||||
ListStatefulScaffold({
|
||||
required this.title,
|
||||
required this.itemBuilder,
|
||||
required this.fetch,
|
||||
required this.itemBuilder,
|
||||
this.actionBuilder,
|
||||
});
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'dart:convert';
|
||||
import 'package:git_touch/scaffolds/list_stateful.dart';
|
||||
import 'package:universal_io/io.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:git_touch/models/auth.dart';
|
||||
import 'package:git_touch/models/bitbucket.dart';
|
||||
import 'package:git_touch/scaffolds/refresh_stateful.dart';
|
||||
import 'package:git_touch/utils/utils.dart';
|
||||
import 'package:git_touch/widgets/action_entry.dart';
|
||||
import 'package:git_touch/widgets/app_bar_title.dart';
|
||||
@ -22,51 +22,55 @@ class BbObjectScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final auth = Provider.of<AuthModel>(context);
|
||||
return RefreshStatefulScaffold(
|
||||
|
||||
return ListStatefulScaffold<dynamic, String?>(
|
||||
title: AppBarTitle(path ?? 'Files'),
|
||||
fetch: () async {
|
||||
final res = await auth
|
||||
.fetchBb('/repositories/$owner/$name/src/$ref/${path ?? ''}');
|
||||
fetch: (next) async {
|
||||
final res = await auth.fetchBb(
|
||||
next ?? '/repositories/$owner/$name/src/$ref/${path ?? ''}');
|
||||
if (res.headers[HttpHeaders.contentTypeHeader] == 'text/plain') {
|
||||
return utf8.decode(res.bodyBytes);
|
||||
} else {
|
||||
return BbPagination.fromJson(json.decode(utf8.decode(res.bodyBytes)))
|
||||
.values;
|
||||
}
|
||||
},
|
||||
actionBuilder: (dynamic p, _) {
|
||||
if (p is String) {
|
||||
return ActionEntry(
|
||||
iconData: Ionicons.cog,
|
||||
url: '/choose-code-theme',
|
||||
return ListPayload(
|
||||
cursor: '',
|
||||
hasMore: false,
|
||||
items: [utf8.decode(res.bodyBytes)],
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
bodyBuilder: (dynamic pl, _) {
|
||||
if (pl is String) {
|
||||
return BlobView(path, text: pl);
|
||||
} else if (pl is List) {
|
||||
final items = pl.map((t) => BbTree.fromJson(t)).toList();
|
||||
final v =
|
||||
BbPagination.fromJson(json.decode(utf8.decode(res.bodyBytes)));
|
||||
final items = [for (var t in v.values) BbTree.fromJson(t)];
|
||||
items.sort((a, b) {
|
||||
return sortByKey('dir', a.type, b.type);
|
||||
});
|
||||
return ObjectTree(items: [
|
||||
for (var v in items)
|
||||
ObjectTreeItem(
|
||||
name: p.basename(v.path!),
|
||||
type: v.type,
|
||||
size: v.type == 'commit_file' ? v.size : null,
|
||||
url:
|
||||
'/bitbucket/$owner/$name/src/$ref?path=${v.path!.urlencode}',
|
||||
downloadUrl: v.links!['self']['href'] as String?,
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
return null;
|
||||
|
||||
return ListPayload(
|
||||
cursor: v.next,
|
||||
hasMore: v.next != null,
|
||||
items: items,
|
||||
);
|
||||
}
|
||||
},
|
||||
itemBuilder: (pl) {
|
||||
if (pl is String) {
|
||||
return BlobView(path, text: pl);
|
||||
} else if (pl is BbTree) {
|
||||
return ObjectTreeItem(
|
||||
name: p.basename(pl.path),
|
||||
type: pl.type,
|
||||
// size: v.type == 'commit_file' ? v.size : null,
|
||||
size: pl.size,
|
||||
url: '/bitbucket/$owner/$name/src/$ref?path=${pl.path.urlencode}',
|
||||
downloadUrl: pl.links!['self']['href'] as String?,
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
actionBuilder: () {
|
||||
return ActionEntry(
|
||||
iconData: Ionicons.cog,
|
||||
url: '/choose-code-theme',
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ class BbRepoScreen extends StatelessWidget {
|
||||
),
|
||||
CommonStyle.border,
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.code,
|
||||
|
@ -46,6 +46,7 @@ class MyApp extends StatelessWidget {
|
||||
CommonStyle.verticalGap,
|
||||
TableView(
|
||||
headerText: AppLocalizations.of(context)!.fontStyle,
|
||||
hasIcon: false,
|
||||
items: [
|
||||
TableViewItem(
|
||||
text: Text(AppLocalizations.of(context)!.fontSize),
|
||||
|
@ -133,7 +133,6 @@ class GeRepoScreen extends StatelessWidget {
|
||||
),
|
||||
CommonStyle.border,
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.code,
|
||||
|
@ -6,6 +6,7 @@ import 'package:git_touch/widgets/app_bar_title.dart';
|
||||
import 'package:git_touch/widgets/object_tree.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:git_touch/models/auth.dart';
|
||||
import 'package:git_touch/widgets/table_view.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/S.dart';
|
||||
|
||||
@ -30,7 +31,7 @@ class GeTreeScreen extends StatelessWidget {
|
||||
return items;
|
||||
},
|
||||
bodyBuilder: (data, _) {
|
||||
return ObjectTree(
|
||||
return TableView(
|
||||
items: [
|
||||
for (var item in data)
|
||||
ObjectTreeItem(
|
||||
@ -41,9 +42,9 @@ class GeTreeScreen extends StatelessWidget {
|
||||
url: (() {
|
||||
switch (item.type) {
|
||||
case 'tree':
|
||||
return '/gitee/$owner/$name/tree/${item.sha}?path=${item.path!.urlencode}';
|
||||
return '/gitee/$owner/$name/tree/${item.sha}?path=${item.path.urlencode}';
|
||||
case 'blob':
|
||||
return '/gitee/$owner/$name/blob/${item.sha}?path=${item.path!.urlencode}';
|
||||
return '/gitee/$owner/$name/blob/${item.sha}?path=${item.path.urlencode}';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -21,12 +21,12 @@ class GhEventsScreen extends StatelessWidget {
|
||||
fetch: (page) async {
|
||||
page = page ?? 1;
|
||||
final events = await context.read<AuthModel>().ghClient!.getJSON(
|
||||
'/users/$login/events?page=$page&per_page=$pageSize',
|
||||
'/users/$login/events?page=$page&per_page=$PAGE_SIZE',
|
||||
convert: (dynamic vs) =>
|
||||
[for (var v in vs) GithubEvent.fromJson(v)]);
|
||||
return ListPayload(
|
||||
cursor: page + 1,
|
||||
hasMore: events.length == pageSize,
|
||||
hasMore: events.length == PAGE_SIZE,
|
||||
items: events,
|
||||
);
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ import 'package:git_touch/graphql/github.var.gql.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:git_touch/widgets/table_view.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:git_touch/models/auth.dart';
|
||||
import 'package:flutter_gen/gen_l10n/S.dart';
|
||||
@ -30,7 +31,7 @@ class GhGistsFilesScreen extends StatelessWidget {
|
||||
return gist;
|
||||
},
|
||||
bodyBuilder: (payload, _) {
|
||||
return ObjectTree(
|
||||
return TableView(
|
||||
items: payload!.files!.map((v) {
|
||||
final uri = Uri(
|
||||
path: '/github/$login/gists/$id/${v.name}',
|
||||
@ -41,7 +42,7 @@ class GhGistsFilesScreen extends StatelessWidget {
|
||||
return ObjectTreeItem(
|
||||
url: uri,
|
||||
type: 'file',
|
||||
name: v.name,
|
||||
name: v.name ?? '',
|
||||
downloadUrl: null,
|
||||
size: v.size,
|
||||
);
|
||||
|
@ -44,12 +44,12 @@ class GhNewsScreenState extends State<GhNewsScreen> {
|
||||
final login = auth.activeAccount!.login;
|
||||
|
||||
final events = await auth.ghClient!.getJSON(
|
||||
'/users/$login/received_events?page=$page&per_page=$pageSize',
|
||||
'/users/$login/received_events?page=$page&per_page=$PAGE_SIZE',
|
||||
convert: (dynamic vs) => [for (var v in vs) GithubEvent.fromJson(v)],
|
||||
);
|
||||
return ListPayload(
|
||||
cursor: page + 1,
|
||||
hasMore: events.length == pageSize,
|
||||
hasMore: events.length == PAGE_SIZE,
|
||||
items: events,
|
||||
);
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ import 'package:git_touch/widgets/blob_view.dart';
|
||||
import 'package:git_touch/widgets/object_tree.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:git_touch/models/auth.dart';
|
||||
import 'package:git_touch/widgets/table_view.dart';
|
||||
import 'package:github/github.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@ -58,7 +59,7 @@ class GhObjectScreen extends StatelessWidget {
|
||||
},
|
||||
bodyBuilder: (data, _) {
|
||||
if (data.isDirectory) {
|
||||
return ObjectTree(
|
||||
return TableView(
|
||||
items: data.tree!.map((v) {
|
||||
// if (item.type == 'commit') return null;
|
||||
final uri = Uri(
|
||||
@ -69,8 +70,8 @@ class GhObjectScreen extends StatelessWidget {
|
||||
},
|
||||
).toString();
|
||||
return ObjectTreeItem(
|
||||
name: v.name,
|
||||
type: v.type,
|
||||
name: v.name ?? '',
|
||||
type: v.type ?? '',
|
||||
url: uri.toString(),
|
||||
downloadUrl: v.downloadUrl,
|
||||
size: v.type == 'file' ? v.size : null,
|
||||
|
@ -238,7 +238,6 @@ class GhRepoScreen extends StatelessWidget {
|
||||
]),
|
||||
],
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
if (ref != null)
|
||||
TableViewItem(
|
||||
|
@ -50,7 +50,7 @@ class _GhSearchScreenState extends State<GhSearchScreen> {
|
||||
final auth = context.read<AuthModel>();
|
||||
final data = await auth.query('''
|
||||
{
|
||||
repository: search(first: $pageSize, type: REPOSITORY, query: "$keyword") {
|
||||
repository: search(first: $PAGE_SIZE, type: REPOSITORY, query: "$keyword") {
|
||||
nodes {
|
||||
... on Repository {
|
||||
owner {
|
||||
@ -76,7 +76,7 @@ class _GhSearchScreenState extends State<GhSearchScreen> {
|
||||
}
|
||||
}
|
||||
}
|
||||
user: search(first: $pageSize, type: USER, query: "$keyword") {
|
||||
user: search(first: $PAGE_SIZE, type: USER, query: "$keyword") {
|
||||
nodes {
|
||||
... on Organization {
|
||||
__typename
|
||||
@ -90,7 +90,7 @@ class _GhSearchScreenState extends State<GhSearchScreen> {
|
||||
}
|
||||
}
|
||||
}
|
||||
issue: search(first: $pageSize, type: ISSUE, query: "$keyword") {
|
||||
issue: search(first: $PAGE_SIZE, type: ISSUE, query: "$keyword") {
|
||||
nodes {
|
||||
... on PullRequest {
|
||||
__typename
|
||||
|
@ -118,7 +118,6 @@ class _User extends StatelessWidget {
|
||||
),
|
||||
CommonStyle.border,
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.rss,
|
||||
@ -217,7 +216,6 @@ class _Org extends StatelessWidget {
|
||||
),
|
||||
]),
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.rss,
|
||||
|
@ -156,7 +156,6 @@ class GlProjectScreen extends StatelessWidget {
|
||||
),
|
||||
CommonStyle.border,
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.code,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:git_touch/models/gitlab.dart';
|
||||
import 'package:git_touch/scaffolds/refresh_stateful.dart';
|
||||
import 'package:git_touch/scaffolds/list_stateful.dart';
|
||||
import 'package:git_touch/widgets/app_bar_title.dart';
|
||||
import 'package:git_touch/widgets/object_tree.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -18,39 +18,41 @@ class GlTreeScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final auth = Provider.of<AuthModel>(context);
|
||||
return RefreshStatefulScaffold<Iterable<GitlabTreeItem>>(
|
||||
|
||||
return ListStatefulScaffold<GitlabTreeItem, int>(
|
||||
title: AppBarTitle(path ?? AppLocalizations.of(context)!.files),
|
||||
fetch: () async {
|
||||
fetch: (page) async {
|
||||
final uri = Uri(
|
||||
path: '/projects/$id/repository/tree',
|
||||
queryParameters: {
|
||||
'ref': ref,
|
||||
'page': page?.toString(),
|
||||
...(path == null ? {} : {'path': path})
|
||||
},
|
||||
);
|
||||
final res = await auth.fetchGitlab(uri.toString());
|
||||
return (res as List).map((v) => GitlabTreeItem.fromJson(v));
|
||||
final res = await auth.fetchGitlabWithPage(uri.toString());
|
||||
return ListPayload(
|
||||
cursor: res.cursor,
|
||||
hasMore: res.hasMore,
|
||||
items: [for (var v in res.data) GitlabTreeItem.fromJson(v)],
|
||||
);
|
||||
},
|
||||
bodyBuilder: (data, _) {
|
||||
return ObjectTree(
|
||||
items: data.map((item) {
|
||||
return ObjectTreeItem(
|
||||
type: item.type,
|
||||
name: item.name,
|
||||
downloadUrl:
|
||||
'${auth.activeAccount!.domain}/api/v4/projects/$id/repository/files/${item.path!.urlencode}/raw?ref=master', // TODO:
|
||||
url: (() {
|
||||
switch (item.type) {
|
||||
case 'tree':
|
||||
return '/gitlab/projects/$id/tree/$ref?path=${item.path!.urlencode}';
|
||||
case 'blob':
|
||||
return '/gitlab/projects/$id/blob/$ref?path=${item.path!.urlencode}';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})(),
|
||||
);
|
||||
}),
|
||||
itemBuilder: (item) {
|
||||
return ObjectTreeItem(
|
||||
type: item.type,
|
||||
name: item.name,
|
||||
downloadUrl:
|
||||
'${auth.activeAccount!.domain}/api/v4/projects/$id/repository/files/${item.path.urlencode}/raw?ref=master', // TODO:
|
||||
url: (() {
|
||||
switch (item.type) {
|
||||
case 'tree':
|
||||
return '/gitlab/projects/$id/tree/$ref?path=${item.path.urlencode}';
|
||||
case 'blob':
|
||||
return '/gitlab/projects/$id/blob/$ref?path=${item.path.urlencode}';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -8,6 +8,7 @@ import 'package:git_touch/widgets/action_entry.dart';
|
||||
import 'package:git_touch/widgets/app_bar_title.dart';
|
||||
import 'package:git_touch/widgets/blob_view.dart';
|
||||
import 'package:git_touch/widgets/object_tree.dart';
|
||||
import 'package:git_touch/widgets/table_view.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class GoObjectScreen extends StatelessWidget {
|
||||
@ -44,7 +45,7 @@ class GoObjectScreen extends StatelessWidget {
|
||||
items.sort((a, b) {
|
||||
return sortByKey('dir', a.type, b.type);
|
||||
});
|
||||
return ObjectTree(items: [
|
||||
return TableView(items: [
|
||||
for (var v in items)
|
||||
ObjectTreeItem(
|
||||
name: v.name,
|
||||
|
@ -89,7 +89,6 @@ class GoRepoScreen extends StatelessWidget {
|
||||
),
|
||||
CommonStyle.border,
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.code,
|
||||
|
@ -71,7 +71,6 @@ class GoUserScreen extends StatelessWidget {
|
||||
]),
|
||||
CommonStyle.border,
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.home,
|
||||
|
@ -8,6 +8,7 @@ import 'package:git_touch/widgets/action_entry.dart';
|
||||
import 'package:git_touch/widgets/app_bar_title.dart';
|
||||
import 'package:git_touch/widgets/blob_view.dart';
|
||||
import 'package:git_touch/widgets/object_tree.dart';
|
||||
import 'package:git_touch/widgets/table_view.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class GtObjectScreen extends StatelessWidget {
|
||||
@ -43,7 +44,7 @@ class GtObjectScreen extends StatelessWidget {
|
||||
items.sort((a, b) {
|
||||
return sortByKey('dir', a.type, b.type);
|
||||
});
|
||||
return ObjectTree(items: [
|
||||
return TableView(items: [
|
||||
for (var v in items)
|
||||
ObjectTreeItem(
|
||||
name: v.name,
|
||||
|
@ -81,7 +81,6 @@ class GtRepoScreen extends StatelessWidget {
|
||||
),
|
||||
CommonStyle.border,
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.code,
|
||||
|
@ -131,7 +131,6 @@ class GtUserScreen extends StatelessWidget {
|
||||
ContributionWidget(weeks: p.userHeatmap),
|
||||
CommonStyle.border,
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.home,
|
||||
|
@ -26,81 +26,86 @@ class SettingsScreen extends StatelessWidget {
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
CommonStyle.verticalGap,
|
||||
TableView(headerText: AppLocalizations.of(context)!.system, items: [
|
||||
if (auth.activeAccount!.platform == PlatformType.github) ...[
|
||||
TableView(
|
||||
hasIcon: false,
|
||||
headerText: AppLocalizations.of(context)!.system,
|
||||
items: [
|
||||
if (auth.activeAccount!.platform == PlatformType.github) ...[
|
||||
TableViewItem(
|
||||
text: Text(AppLocalizations.of(context)!.githubStatus),
|
||||
url: 'https://www.githubstatus.com/',
|
||||
),
|
||||
TableViewItem(
|
||||
text: Text(AppLocalizations.of(context)!.reviewPermissions),
|
||||
url:
|
||||
'https://github.com/settings/connections/applications/$clientId',
|
||||
rightWidget: Text(auth.activeAccount!.login),
|
||||
),
|
||||
],
|
||||
if (auth.activeAccount!.platform == PlatformType.gitlab)
|
||||
TableViewItem(
|
||||
text: Text(AppLocalizations.of(context)!.gitlabStatus),
|
||||
url: '${auth.activeAccount!.domain}/help',
|
||||
rightWidget: FutureBuilder<String>(
|
||||
future:
|
||||
auth.fetchGitlab('/version').then((v) => v['version']),
|
||||
builder: (context, snapshot) {
|
||||
return Text(snapshot.data ?? '');
|
||||
},
|
||||
),
|
||||
),
|
||||
if (auth.activeAccount!.platform == PlatformType.gitea)
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.info,
|
||||
text: Text(AppLocalizations.of(context)!.giteaStatus),
|
||||
url: '/gitea/status',
|
||||
rightWidget: FutureBuilder<String>(
|
||||
future:
|
||||
auth.fetchGitea('/version').then((v) => v['version']),
|
||||
builder: (context, snapshot) {
|
||||
return Text(snapshot.data ?? '');
|
||||
},
|
||||
),
|
||||
),
|
||||
TableViewItem(
|
||||
text: Text(AppLocalizations.of(context)!.githubStatus),
|
||||
url: 'https://www.githubstatus.com/',
|
||||
),
|
||||
TableViewItem(
|
||||
text: Text(AppLocalizations.of(context)!.reviewPermissions),
|
||||
url:
|
||||
'https://github.com/settings/connections/applications/$clientId',
|
||||
text: Text(AppLocalizations.of(context)!.switchAccounts),
|
||||
url: '/login',
|
||||
rightWidget: Text(auth.activeAccount!.login),
|
||||
),
|
||||
TableViewItem(
|
||||
text: Text('App Language'),
|
||||
rightWidget: Text(theme.locale == null
|
||||
? AppLocalizations.of(context)!.followSystem
|
||||
: localeNameMap[theme.locale!] ?? theme.locale!),
|
||||
onTap: () {
|
||||
theme.showActions(context, [
|
||||
for (final key in [
|
||||
null,
|
||||
...AppLocalizations.supportedLocales
|
||||
.map((l) => l.toString())
|
||||
.where((key) => localeNameMap[key] != null)
|
||||
])
|
||||
ActionItem(
|
||||
text: key == null
|
||||
? AppLocalizations.of(context)!.followSystem
|
||||
: localeNameMap[key],
|
||||
onTap: (_) async {
|
||||
final res = await theme.showConfirm(
|
||||
context,
|
||||
Text(
|
||||
'The app will reload to make the language setting take effect'),
|
||||
);
|
||||
if (res == true && theme.locale != key) {
|
||||
await theme.setLocale(key);
|
||||
auth.reloadApp();
|
||||
}
|
||||
},
|
||||
)
|
||||
]);
|
||||
},
|
||||
)
|
||||
],
|
||||
if (auth.activeAccount!.platform == PlatformType.gitlab)
|
||||
TableViewItem(
|
||||
text: Text(AppLocalizations.of(context)!.gitlabStatus),
|
||||
url: '${auth.activeAccount!.domain}/help',
|
||||
rightWidget: FutureBuilder<String>(
|
||||
future:
|
||||
auth.fetchGitlab('/version').then((v) => v['version']),
|
||||
builder: (context, snapshot) {
|
||||
return Text(snapshot.data ?? '');
|
||||
},
|
||||
),
|
||||
),
|
||||
if (auth.activeAccount!.platform == PlatformType.gitea)
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.info,
|
||||
text: Text(AppLocalizations.of(context)!.giteaStatus),
|
||||
url: '/gitea/status',
|
||||
rightWidget: FutureBuilder<String>(
|
||||
future: auth.fetchGitea('/version').then((v) => v['version']),
|
||||
builder: (context, snapshot) {
|
||||
return Text(snapshot.data ?? '');
|
||||
},
|
||||
),
|
||||
),
|
||||
TableViewItem(
|
||||
text: Text(AppLocalizations.of(context)!.switchAccounts),
|
||||
url: '/login',
|
||||
rightWidget: Text(auth.activeAccount!.login),
|
||||
),
|
||||
TableViewItem(
|
||||
text: Text('App Language'),
|
||||
rightWidget: Text(theme.locale == null
|
||||
? AppLocalizations.of(context)!.followSystem
|
||||
: localeNameMap[theme.locale!] ?? theme.locale!),
|
||||
onTap: () {
|
||||
theme.showActions(context, [
|
||||
for (final key in [
|
||||
null,
|
||||
...AppLocalizations.supportedLocales
|
||||
.map((l) => l.toString())
|
||||
.where((key) => localeNameMap[key] != null)
|
||||
])
|
||||
ActionItem(
|
||||
text: key == null
|
||||
? AppLocalizations.of(context)!.followSystem
|
||||
: localeNameMap[key],
|
||||
onTap: (_) async {
|
||||
final res = await theme.showConfirm(
|
||||
context,
|
||||
Text(
|
||||
'The app will reload to make the language setting take effect'),
|
||||
);
|
||||
if (res == true && theme.locale != key) {
|
||||
await theme.setLocale(key);
|
||||
auth.reloadApp();
|
||||
}
|
||||
},
|
||||
)
|
||||
]);
|
||||
},
|
||||
)
|
||||
]),
|
||||
),
|
||||
CommonStyle.verticalGap,
|
||||
TableView(headerText: 'theme', items: [
|
||||
TableViewItem(
|
||||
|
@ -95,7 +95,7 @@ class GithubPalette {
|
||||
}
|
||||
|
||||
// final pageSize = 5;
|
||||
final pageSize = 30;
|
||||
final PAGE_SIZE = 30;
|
||||
|
||||
var createWarning =
|
||||
(String text) => Text(text, style: TextStyle(color: Colors.redAccent));
|
||||
@ -189,12 +189,12 @@ const TOTAL_COUNT_FALLBACK = 999; // TODO:
|
||||
|
||||
class ListPayload<T, K> {
|
||||
K cursor;
|
||||
Iterable<T> items;
|
||||
bool hasMore;
|
||||
Iterable<T> items;
|
||||
|
||||
ListPayload({
|
||||
required this.items,
|
||||
required this.cursor,
|
||||
required this.hasMore,
|
||||
required this.items,
|
||||
});
|
||||
}
|
||||
|
@ -5,31 +5,27 @@ import 'package:git_touch/widgets/table_view.dart';
|
||||
import 'package:primer/primer.dart';
|
||||
import 'package:file_icon/file_icon.dart';
|
||||
|
||||
class ObjectTreeItem {
|
||||
class ObjectTreeItem extends StatelessWidget {
|
||||
final String type;
|
||||
final String name;
|
||||
final int? size;
|
||||
final String? url;
|
||||
final String? downloadUrl;
|
||||
final String? name;
|
||||
final String? type;
|
||||
final int? size;
|
||||
ObjectTreeItem({
|
||||
required this.name,
|
||||
required this.url,
|
||||
required this.downloadUrl,
|
||||
|
||||
const ObjectTreeItem({
|
||||
required this.type,
|
||||
required this.name,
|
||||
this.size,
|
||||
this.url,
|
||||
this.downloadUrl,
|
||||
});
|
||||
}
|
||||
|
||||
class ObjectTree extends StatelessWidget {
|
||||
final Iterable<ObjectTreeItem> items;
|
||||
ObjectTree({required this.items});
|
||||
|
||||
Widget _buildIcon(ObjectTreeItem item) {
|
||||
switch (item.type) {
|
||||
Widget _buildIcon() {
|
||||
switch (type) {
|
||||
case 'blob': // github gql, gitlab
|
||||
case 'file': // github rest, gitea
|
||||
case 'commit_file': // bitbucket
|
||||
return FileIcon(item.name!, size: 36);
|
||||
return FileIcon(name, size: 36);
|
||||
case 'tree': // github gql, gitlab
|
||||
case 'dir': // github rest, gitea
|
||||
case 'commit_directory': // bitbucket
|
||||
@ -51,32 +47,26 @@ class ObjectTree extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
for (var item in items)
|
||||
TableViewItem(
|
||||
leftWidget: _buildIcon(item),
|
||||
text: Text(item.name!),
|
||||
rightWidget: item.size == null ? null : Text(filesize(item.size)),
|
||||
url: [
|
||||
// Let system browser handle these files
|
||||
//
|
||||
// TODO:
|
||||
// Unhandled Exception: PlatformException(Error, Error while launching
|
||||
// https://github.com/flutter/flutter/issues/49162
|
||||
return TableViewItem(
|
||||
leftWidget: _buildIcon(),
|
||||
text: Text(name),
|
||||
rightWidget: size == null ? null : Text(filesize(size)),
|
||||
url: [
|
||||
// Let system browser handle these files
|
||||
//
|
||||
// TODO:
|
||||
// Unhandled Exception: PlatformException(Error, Error while launching
|
||||
// https://github.com/flutter/flutter/issues/49162
|
||||
|
||||
// Docs
|
||||
'pdf', 'docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls',
|
||||
// Fonts
|
||||
'ttf', 'otf', 'eot', 'woff', 'woff2',
|
||||
'svg',
|
||||
].contains(item.name!.ext)
|
||||
? item.downloadUrl
|
||||
: item.url,
|
||||
hideRightChevron: item.size != null,
|
||||
)
|
||||
],
|
||||
// Docs
|
||||
'pdf', 'docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls',
|
||||
// Fonts
|
||||
'ttf', 'otf', 'eot', 'woff', 'woff2',
|
||||
'svg',
|
||||
].contains(name.ext)
|
||||
? downloadUrl
|
||||
: url,
|
||||
hideRightChevron: size != null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -87,26 +87,29 @@ class ReleaseItem extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
children: <Widget>[
|
||||
TableView(items: [
|
||||
if (releaseAssets != null)
|
||||
for (var asset in releaseAssets!.nodes!)
|
||||
TableViewItem(
|
||||
text: Text(
|
||||
asset.name,
|
||||
style: TextStyle(
|
||||
color: theme.palette.primary,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
TableView(
|
||||
hasIcon: false,
|
||||
items: [
|
||||
if (releaseAssets != null)
|
||||
for (var asset in releaseAssets!.nodes!)
|
||||
TableViewItem(
|
||||
text: Text(
|
||||
asset.name,
|
||||
style: TextStyle(
|
||||
color: theme.palette.primary,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
rightWidget: IconButton(
|
||||
onPressed: () {
|
||||
theme.push(context, asset.downloadUrl);
|
||||
},
|
||||
icon: Icon(Ionicons.download_outline)),
|
||||
hideRightChevron: true,
|
||||
),
|
||||
rightWidget: IconButton(
|
||||
onPressed: () {
|
||||
theme.push(context, asset.downloadUrl);
|
||||
},
|
||||
icon: Icon(Ionicons.download_outline)),
|
||||
hideRightChevron: true,
|
||||
),
|
||||
])
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
|
@ -24,7 +24,7 @@ class TableViewHeader extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class TableViewItem {
|
||||
class TableViewItem extends StatelessWidget {
|
||||
final Widget text;
|
||||
final IconData? leftIconData;
|
||||
final Widget? leftWidget;
|
||||
@ -42,76 +42,74 @@ class TableViewItem {
|
||||
this.url,
|
||||
this.hideRightChevron = false,
|
||||
}) : assert(leftIconData == null || leftWidget == null);
|
||||
}
|
||||
|
||||
class TableView extends StatelessWidget {
|
||||
final String? headerText;
|
||||
final Iterable<TableViewItem> items;
|
||||
final bool hasIcon;
|
||||
|
||||
double get _leftPadding => hasIcon ? 44 : 12;
|
||||
|
||||
TableView({this.headerText, required this.items, this.hasIcon = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Provider.of<ThemeModel>(context);
|
||||
|
||||
return LinkWidget(
|
||||
onTap: onTap,
|
||||
url: url,
|
||||
child: DefaultTextStyle(
|
||||
style: TextStyle(fontSize: 17, color: theme.palette.text),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: Container(
|
||||
height: 44,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: (leftWidget == null && leftIconData == null) ? 12 : 44,
|
||||
child: Center(
|
||||
child: leftWidget ??
|
||||
Icon(
|
||||
leftIconData,
|
||||
color: theme.palette.primary,
|
||||
size: 20,
|
||||
)),
|
||||
),
|
||||
Expanded(child: text),
|
||||
if (rightWidget != null) ...[
|
||||
DefaultTextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
color: theme.palette.tertiaryText,
|
||||
),
|
||||
child: rightWidget!,
|
||||
),
|
||||
SizedBox(width: 6)
|
||||
],
|
||||
if ((onTap != null || url != null) && !hideRightChevron)
|
||||
Icon(Ionicons.chevron_forward,
|
||||
size: 20, color: theme.palette.tertiaryText)
|
||||
else
|
||||
SizedBox(width: 2),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TableView extends StatelessWidget {
|
||||
final String? headerText;
|
||||
final Iterable<Widget> items;
|
||||
final bool? hasIcon;
|
||||
|
||||
TableView({this.headerText, required this.items, this.hasIcon = true});
|
||||
|
||||
double get _leftPadding => hasIcon == true ? 44 : 12;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
if (headerText != null) TableViewHeader(headerText),
|
||||
CommonStyle.border,
|
||||
...join(
|
||||
BorderView(leftPadding: _leftPadding),
|
||||
items.map((item) {
|
||||
var leftWidget = item.leftWidget;
|
||||
if (leftWidget == null && hasIcon) {
|
||||
leftWidget = Icon(
|
||||
item.leftIconData,
|
||||
color: theme.palette.primary,
|
||||
size: 20,
|
||||
);
|
||||
}
|
||||
|
||||
return LinkWidget(
|
||||
onTap: item.onTap,
|
||||
url: item.url,
|
||||
child: DefaultTextStyle(
|
||||
style: TextStyle(fontSize: 17, color: theme.palette.text),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: Container(
|
||||
height: 44,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: _leftPadding,
|
||||
child: Center(child: leftWidget),
|
||||
),
|
||||
Expanded(child: item.text),
|
||||
if (item.rightWidget != null) ...[
|
||||
DefaultTextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
color: theme.palette.tertiaryText,
|
||||
),
|
||||
child: item.rightWidget!,
|
||||
),
|
||||
SizedBox(width: 6)
|
||||
],
|
||||
if ((item.onTap != null || item.url != null) &&
|
||||
!item.hideRightChevron)
|
||||
Icon(Ionicons.chevron_forward,
|
||||
size: 20, color: theme.palette.tertiaryText)
|
||||
else
|
||||
SizedBox(width: 2),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
items.toList(),
|
||||
),
|
||||
CommonStyle.border,
|
||||
],
|
||||
|
Loading…
x
Reference in New Issue
Block a user