This commit is contained in:
parent
0f052d018c
commit
182588e7ae
6 changed files with 277 additions and 9 deletions
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gitea_client/model/repouser.dart';
|
import 'package:gitea_client/model/repouser.dart';
|
||||||
import 'package:gitea_client/model/user.dart';
|
import 'package:gitea_client/model/user.dart';
|
||||||
|
import 'package:gitea_client/widget/code_page.dart';
|
||||||
import 'package:gitea_client/widget/login_form.dart';
|
import 'package:gitea_client/widget/login_form.dart';
|
||||||
import 'package:gitea_client/widget/login_status.dart';
|
import 'package:gitea_client/widget/login_status.dart';
|
||||||
import 'package:gitea_client/widget/repo_list_page.dart';
|
import 'package:gitea_client/widget/repo_list_page.dart';
|
||||||
|
@ -48,6 +49,12 @@ class MyApp extends StatelessWidget {
|
||||||
settings: const RouteSettings(name: "/repopage"),
|
settings: const RouteSettings(name: "/repopage"),
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
RepoPage(repo: repouser.repository, user: repouser.user));
|
RepoPage(repo: repouser.repository, user: repouser.user));
|
||||||
|
case "/fileview":
|
||||||
|
final data = route.arguments as CodePageData;
|
||||||
|
return MaterialPageRoute(
|
||||||
|
settings: const RouteSettings(name: "/fileview"),
|
||||||
|
builder: (context) =>
|
||||||
|
CodePage(filePath: data.filePath, repo: data.repo, owner: data.owner, user: data.user));
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
|
@ -35,6 +35,23 @@ class GiteaService {
|
||||||
|
|
||||||
return RepoFile.fromJson(jsonDecode(response.body));
|
return RepoFile.fromJson(jsonDecode(response.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<RepoFile>> getFolder(String owner,String repo,String path) async{
|
||||||
|
var response = await http.get(
|
||||||
|
Uri.https(apiAccess.instance, "api/v1/repos/$owner/$repo/contents/$path", {
|
||||||
|
"token": apiAccess.token,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final body = json.decode(response.body) as List;
|
||||||
|
var result = body.map((dynamic json) {
|
||||||
|
return RepoFile.fromJson(json);
|
||||||
|
}).toList();
|
||||||
|
result.sort((a,b) => a.type.compareTo(b.type));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
throw Exception('error fetching files');
|
||||||
|
}
|
||||||
//
|
//
|
||||||
Future<List<Repository>> getUserRepositories([int page = 1, int limit = 10]) async{
|
Future<List<Repository>> getUserRepositories([int page = 1, int limit = 10]) async{
|
||||||
var response = await http.get(
|
var response = await http.get(
|
||||||
|
|
95
lib/widget/code_page.dart
Normal file
95
lib/widget/code_page.dart
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_highlight/flutter_highlight.dart';
|
||||||
|
import 'package:flutter_highlight/themes/github.dart';
|
||||||
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
import 'package:gitea_client/model/user.dart';
|
||||||
|
import 'package:gitea_client/service/gitea_service.dart';
|
||||||
|
|
||||||
|
import '../model/File.dart';
|
||||||
|
|
||||||
|
class CodePageData {
|
||||||
|
final String filePath;
|
||||||
|
final String repo;
|
||||||
|
final String owner;
|
||||||
|
final SavedUser user;
|
||||||
|
|
||||||
|
const CodePageData(this.filePath, this.repo, this.owner, this.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CodePage extends StatefulWidget {
|
||||||
|
final String filePath;
|
||||||
|
final String repo;
|
||||||
|
final String owner;
|
||||||
|
final SavedUser user;
|
||||||
|
|
||||||
|
const CodePage(
|
||||||
|
{Key? key,
|
||||||
|
required this.filePath,
|
||||||
|
required this.repo,
|
||||||
|
required this.owner,
|
||||||
|
required this.user})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CodePage createState() => _CodePage();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CodePage extends State<CodePage> {
|
||||||
|
Future<RepoFile>? fileRequest;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
GiteaService giteaService = GiteaService(apiAccess: widget.user.apiAccess);
|
||||||
|
fileRequest =
|
||||||
|
giteaService.getFile(widget.owner, widget.repo, widget.filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(context) {
|
||||||
|
final media = MediaQuery.of(context).size;
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(widget.filePath),
|
||||||
|
),
|
||||||
|
body: FutureBuilder<RepoFile>(
|
||||||
|
future: fileRequest,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text("No Readme file found!",
|
||||||
|
style: Theme.of(context).textTheme.headline6),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (snapshot.hasData) {
|
||||||
|
final file = snapshot.data!;
|
||||||
|
|
||||||
|
final content = utf8.decode(base64.decode(file.content!));
|
||||||
|
|
||||||
|
return Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width:
|
||||||
|
(media.width > 600) ? media.width * 0.6 : media.width * 0.9,
|
||||||
|
height: media.height * 0.7,
|
||||||
|
child: (file.name.endsWith(".md"))
|
||||||
|
? Markdown(selectable: true, data: content)
|
||||||
|
: SingleChildScrollView(
|
||||||
|
child: HighlightView(
|
||||||
|
content,
|
||||||
|
language: "dart",
|
||||||
|
theme: githubTheme,
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:gitea_client/model/repository.dart';
|
import 'package:gitea_client/model/repository.dart';
|
||||||
import 'package:gitea_client/model/user.dart';
|
import 'package:gitea_client/model/user.dart';
|
||||||
import 'package:gitea_client/service/gitea_service.dart';
|
import 'package:gitea_client/service/gitea_service.dart';
|
||||||
|
import 'package:gitea_client/widget/code_page.dart';
|
||||||
|
|
||||||
import '../model/File.dart';
|
import '../model/File.dart';
|
||||||
|
|
||||||
|
@ -121,8 +122,10 @@ class _RepoHome extends State<RepoHome> {
|
||||||
final media = MediaQuery.of(context).size;
|
final media = MediaQuery.of(context).size;
|
||||||
|
|
||||||
return Column(children: [
|
return Column(children: [
|
||||||
Text(widget.repo.fullName!,
|
Text(
|
||||||
style: Theme.of(context).textTheme.headline3,),
|
widget.repo.fullName!,
|
||||||
|
style: Theme.of(context).textTheme.headline3,
|
||||||
|
),
|
||||||
FutureBuilder<RepoFile>(
|
FutureBuilder<RepoFile>(
|
||||||
future: readmeRequest,
|
future: readmeRequest,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
@ -139,10 +142,13 @@ class _RepoHome extends State<RepoHome> {
|
||||||
var file = snapshot.data!;
|
var file = snapshot.data!;
|
||||||
final content = utf8.decode(base64.decode(file.content!));
|
final content = utf8.decode(base64.decode(file.content!));
|
||||||
|
|
||||||
return SingleChildScrollView(child: Center(
|
return SingleChildScrollView(
|
||||||
|
child: Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: (media.width > 600) ? media.width * 0.6 : media.width*0.9,
|
width: (media.width > 600)
|
||||||
height: media.height*0.7,
|
? media.width * 0.6
|
||||||
|
: media.width * 0.9,
|
||||||
|
height: media.height * 0.7,
|
||||||
child: Markdown(selectable: true, data: content)),
|
child: Markdown(selectable: true, data: content)),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
@ -177,10 +183,106 @@ class RepoFiles extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RepoFiles extends State<RepoFiles> {
|
class _RepoFiles extends State<RepoFiles> {
|
||||||
|
String path = "/";
|
||||||
|
Future<List<RepoFile>>? readmeRequest;
|
||||||
|
late final GiteaService giteaService;
|
||||||
|
final _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
giteaService = GiteaService(apiAccess: widget.user.apiAccess);
|
||||||
|
readmeRequest = giteaService.getFolder(
|
||||||
|
widget.repo.owner.username!, widget.repo.name, path);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// TODO: implement build
|
return FutureBuilder<List<RepoFile>>(
|
||||||
throw UnimplementedError();
|
future: readmeRequest,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text("No Readme file found!",
|
||||||
|
style: Theme.of(context).textTheme.headline6),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (snapshot.hasData) {
|
||||||
|
var files = (path == "/")
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
RepoFile(
|
||||||
|
name: "..",
|
||||||
|
path: path,
|
||||||
|
sha: "",
|
||||||
|
type: "dir",
|
||||||
|
size: 0,
|
||||||
|
url: "",
|
||||||
|
htmlUrl: "",
|
||||||
|
gitUrl: "",
|
||||||
|
lLinks: Links(self: "", git: "", html: ""))
|
||||||
|
];
|
||||||
|
files.addAll(snapshot.data!);
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return FileListItem(
|
||||||
|
file: files[index],
|
||||||
|
onTap: () => {
|
||||||
|
if (files[index].type == "dir")
|
||||||
|
setState(() {
|
||||||
|
String prev = files[index].path.contains("/")
|
||||||
|
? files[index].path.substring(
|
||||||
|
0, files[index].path.lastIndexOf('/'))
|
||||||
|
: "/";
|
||||||
|
path = (files[index].name == "..")
|
||||||
|
? prev
|
||||||
|
: files[index].path;
|
||||||
|
readmeRequest = null;
|
||||||
|
readmeRequest = giteaService.getFolder(
|
||||||
|
widget.repo.owner.username!,
|
||||||
|
widget.repo.name,
|
||||||
|
path);
|
||||||
|
})
|
||||||
|
else
|
||||||
|
Navigator.of(context).pushNamed("/fileview",
|
||||||
|
arguments: CodePageData(
|
||||||
|
files[index].path,
|
||||||
|
widget.repo.name,
|
||||||
|
widget.repo.owner.username!,
|
||||||
|
widget.user))
|
||||||
|
});
|
||||||
|
},
|
||||||
|
itemCount: files.length,
|
||||||
|
controller: _scrollController,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileListItem extends StatelessWidget {
|
||||||
|
final RepoFile file;
|
||||||
|
final Function onTap;
|
||||||
|
|
||||||
|
const FileListItem({Key? key, required this.file, required this.onTap})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListTile(
|
||||||
|
leading: (file.type == "file")
|
||||||
|
? const Icon(Icons.insert_drive_file_outlined)
|
||||||
|
: const Icon(Icons.folder_outlined),
|
||||||
|
title: Text(file.name),
|
||||||
|
onTap: () => {onTap()},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,8 +300,40 @@ class RepoIssues extends StatefulWidget {
|
||||||
class _RepoIssues extends State<RepoIssues> {
|
class _RepoIssues extends State<RepoIssues> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// TODO: implement build
|
return DefaultTabController(
|
||||||
throw UnimplementedError();
|
length: 2,
|
||||||
|
child:DefaultTabController(
|
||||||
|
length: 2,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: PreferredSize(
|
||||||
|
preferredSize: const Size.fromHeight(kToolbarHeight),
|
||||||
|
child: Container(
|
||||||
|
color: Colors.lightGreen, ,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(child: Container()),
|
||||||
|
const TabBar(
|
||||||
|
tabs: [Text("Open"), Text("Closed")],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: TabBarView(
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
children: const [Text("Lunches Page")],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: const [ Text("Cart Page")],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
pubspec.lock
14
pubspec.lock
|
@ -174,6 +174,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.0.1"
|
version: "8.0.1"
|
||||||
|
flutter_highlight:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_highlight
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.0"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -210,6 +217,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
|
highlight:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: highlight
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.0"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -46,6 +46,7 @@ dependencies:
|
||||||
shared_preferences: ^2.0.13
|
shared_preferences: ^2.0.13
|
||||||
badges: ^2.0.2
|
badges: ^2.0.2
|
||||||
flutter_markdown: ^0.6.10
|
flutter_markdown: ^0.6.10
|
||||||
|
flutter_highlight: ^0.7.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue