GopherShy/lib/gopher_browser.dart

185 lines
5.8 KiB
Dart
Raw Normal View History

2024-11-05 01:30:46 +01:00
import 'package:flutter/material.dart';
import 'package:gophershy/gopherlib.dart';
import 'package:gophershy/gopher_widgets.dart';
class _GopherBrowserState extends State<GopherBrowser> {
// TODO: when is this updated? how is the widget.initialUrl at play?
// TODO: We must have button: Menu! to attempt to render any text file as menu
// Because when we visit from direct url we don't know what file type we get
// Other detection methods could be applied later
String currentUrl = "gopher://treebrary.org";
// Directory listing doesn't have to end with / so we can never
// be sure whether we're visiting menu or not.
// When true, we try to parse anything as menu
bool forceMenu = false;
void gotoUrl(String link) {
if (link == currentUrl) return;
setState(() {
print("prev: $currentUrl Submitted: $link");
currentUrl = link;
});
}
@override
Widget build(BuildContext context) {
print("GopherBrowseState: $currentUrl");
return Column(
children: [
TextField(onSubmitted: gotoUrl),
Expanded(
child: GopherLoader(
key: ValueKey(currentUrl),
forceMenu: forceMenu,
parsedGopherItem: ParsedGopherItem.parseUrl(currentUrl),
)),
],
);
}
}
class GopherBrowser extends StatefulWidget {
final String initialUrl;
const GopherBrowser({super.key, required this.initialUrl});
@override
State<GopherBrowser> createState() => _GopherBrowserState();
}
class _GopherLoaderState extends State<GopherLoader> {
Future<LoadedGopherItem>? _data;
@override
void initState() {
super.initState();
_data = widget.parsedGopherItem.load(forceMenu: widget.forceMenu);
print("Reiniterd");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("did:change: _data $_data");
setState(() {
_data = widget.parsedGopherItem.load(forceMenu: widget.forceMenu);
});
print('didChangeDependencies, mounted: ${1} done? ');
print("$_data");
}
void goToNext(ParsedGopherItem item) {
setState(() {
print("requesting view of: ${item.url} ${item.documentType}");
_data = item.load(forceMenu: false);
});
}
@override
Widget build(BuildContext context) {
return Center(
child: FutureBuilder<LoadedGopherItem>(
future: _data,
builder: (cont, snap) {
print("${snap.connectionState}");
if (snap.connectionState != ConnectionState.done) {
return const CircularProgressIndicator();
} else {
if (snap.hasData) {
print("data & doctype: ${snap.data!}, ${snap.data!.parsed.documentType}, ");
return switch (snap.data!.parsed.documentType) {
DocumentType.TextFile => GopherText(snap.data!),
DocumentType.Directory => GopherDirectory(
parsedDirectory: snap.data!,
onItemClick: goToNext,
),
_ => Container(color: Colors.yellow),
};
} else {
return const Text("Errored");
}
}
}));
}
}
class GopherLoader extends StatefulWidget {
final ParsedGopherItem parsedGopherItem;
final bool? forceMenu;
const GopherLoader(
{super.key, required this.parsedGopherItem, this.forceMenu});
@override
State<GopherLoader> createState() => _GopherLoaderState();
}
class MenuItem extends StatelessWidget {
static const Map<DocumentType, IconData> _iconMap = {
DocumentType.TextFile: Icons.short_text,
DocumentType.Directory: Icons.folder,
DocumentType.CSO: Icons.contact_page,
DocumentType.ErrorCode: Icons.error,
DocumentType.BinHex: Icons.precision_manufacturing_sharp,
DocumentType.DosBinary: Icons.precision_manufacturing_rounded,
DocumentType.Uuencoded: Icons.precision_manufacturing_outlined,
DocumentType.Search: Icons.search_rounded,
DocumentType.Telnet: Icons.smart_screen_sharp,
DocumentType.Telnet3270: Icons.smart_screen_sharp,
DocumentType.Binary: Icons.file_download,
DocumentType.MirrorOrAlternate: Icons.content_copy,
DocumentType.Gif: Icons.animation,
DocumentType.Image: Icons.image,
DocumentType.Bitmap: Icons.picture_as_pdf,
DocumentType.Movie: Icons.local_movies,
DocumentType.Soundfile: Icons.music_note,
DocumentType.Doc: Icons.document_scanner,
DocumentType.Html: Icons.html,
DocumentType.Informational: Icons.info,
DocumentType.PngImage: Icons.hide_image_sharp,
DocumentType.RichTextDocument: Icons.format_color_text,
DocumentType.SoundFile: Icons.library_music,
DocumentType.Pdf: Icons.picture_as_pdf,
DocumentType.Xml: Icons.html_rounded,
DocumentType.Unknown: Icons.question_mark,
};
IconData _getIcon(DocumentType forWhat) {
return _iconMap[forWhat] ?? Icons.question_mark_sharp;
}
final ParsedGopherItem item;
const MenuItem(this.item, {super.key});
void _itemTap(BuildContext cont) {
Navigator.push(cont, MaterialPageRoute(builder: (cont) {
if (item.documentType == DocumentType.Directory) {
return const Text("it was a directory");
} else {
return Container();
}
}));
}
@override
Widget build(BuildContext context) {
// TODO: Something something virtual item here as well
if (item.documentType == DocumentType.Unknown) {
return Text(item.name ?? "?");
}
// Put it into GestureDetector and on click open new route loading given item
return GestureDetector(
onTap: () => _itemTap(context),
child: Row(
children: [
Icon(_getIcon(item.documentType)),
Column(
children: [
Text(item.name ?? ""),
],
),
],
),
);
}
}