184 lines
5.8 KiB
Dart
184 lines
5.8 KiB
Dart
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 ?? ""),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|