From 182588e7ae5925318614530dcde7060c4d06213d Mon Sep 17 00:00:00 2001 From: Bazsalanszky Date: Sun, 15 May 2022 18:51:38 +0200 Subject: [PATCH] Basic code page --- lib/main.dart | 7 ++ lib/service/gitea_service.dart | 17 ++++ lib/widget/code_page.dart | 95 +++++++++++++++++++++ lib/widget/repo_overview.dart | 152 +++++++++++++++++++++++++++++++-- pubspec.lock | 14 +++ pubspec.yaml | 1 + 6 files changed, 277 insertions(+), 9 deletions(-) create mode 100644 lib/widget/code_page.dart diff --git a/lib/main.dart b/lib/main.dart index 57e803b..57916b7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:gitea_client/model/repouser.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_status.dart'; import 'package:gitea_client/widget/repo_list_page.dart'; @@ -48,6 +49,12 @@ class MyApp extends StatelessWidget { settings: const RouteSettings(name: "/repopage"), builder: (context) => 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; }, diff --git a/lib/service/gitea_service.dart b/lib/service/gitea_service.dart index 088864b..4b108f5 100644 --- a/lib/service/gitea_service.dart +++ b/lib/service/gitea_service.dart @@ -35,6 +35,23 @@ class GiteaService { return RepoFile.fromJson(jsonDecode(response.body)); } + + Future> 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> getUserRepositories([int page = 1, int limit = 10]) async{ var response = await http.get( diff --git a/lib/widget/code_page.dart b/lib/widget/code_page.dart new file mode 100644 index 0000000..2f7b17e --- /dev/null +++ b/lib/widget/code_page.dart @@ -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 { + Future? 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( + 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(), + ); + } + }), + ); + } +} diff --git a/lib/widget/repo_overview.dart b/lib/widget/repo_overview.dart index d95445b..ca27fea 100644 --- a/lib/widget/repo_overview.dart +++ b/lib/widget/repo_overview.dart @@ -6,6 +6,7 @@ import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:gitea_client/model/repository.dart'; import 'package:gitea_client/model/user.dart'; import 'package:gitea_client/service/gitea_service.dart'; +import 'package:gitea_client/widget/code_page.dart'; import '../model/File.dart'; @@ -121,8 +122,10 @@ class _RepoHome extends State { final media = MediaQuery.of(context).size; return Column(children: [ - Text(widget.repo.fullName!, - style: Theme.of(context).textTheme.headline3,), + Text( + widget.repo.fullName!, + style: Theme.of(context).textTheme.headline3, + ), FutureBuilder( future: readmeRequest, builder: (context, snapshot) { @@ -139,10 +142,13 @@ class _RepoHome extends State { var file = snapshot.data!; final content = utf8.decode(base64.decode(file.content!)); - return SingleChildScrollView(child: Center( + return SingleChildScrollView( + child: Center( child: SizedBox( - width: (media.width > 600) ? media.width * 0.6 : media.width*0.9, - height: media.height*0.7, + width: (media.width > 600) + ? media.width * 0.6 + : media.width * 0.9, + height: media.height * 0.7, child: Markdown(selectable: true, data: content)), )); } else { @@ -177,10 +183,106 @@ class RepoFiles extends StatefulWidget { } class _RepoFiles extends State { + String path = "/"; + Future>? 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 Widget build(BuildContext context) { - // TODO: implement build - throw UnimplementedError(); + return FutureBuilder>( + 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 { @override Widget build(BuildContext context) { - // TODO: implement build - throw UnimplementedError(); + return DefaultTabController( + 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: [ + Expanded(child: Container()), + const TabBar( + tabs: [Text("Open"), Text("Closed")], + ), + ], + ), + ), + ), + ), + body: TabBarView( + children: [ + Column( + children: const [Text("Lunches Page")], + ), + Column( + children: const [ Text("Cart Page")], + ) + ], + ), + ), + ), + ); } } diff --git a/pubspec.lock b/pubspec.lock index 0bcaa27..15dc01c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -174,6 +174,13 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: "direct dev" description: @@ -210,6 +217,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.2" + highlight: + dependency: transitive + description: + name: highlight + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0" http: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a1ef526..3babb77 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -46,6 +46,7 @@ dependencies: shared_preferences: ^2.0.13 badges: ^2.0.2 flutter_markdown: ^0.6.10 + flutter_highlight: ^0.7.0 dev_dependencies: flutter_test: