Moment editor

This commit is contained in:
LittleSheep 2024-03-24 01:22:01 +08:00
parent 048b8c0894
commit a1fd936702
4 changed files with 240 additions and 62 deletions

View File

@ -2,6 +2,7 @@ import 'package:go_router/go_router.dart';
import 'package:solaragent/screens/account.dart';
import 'package:solaragent/screens/explore.dart';
import 'package:solaragent/screens/notifications.dart';
import 'package:solaragent/screens/publish/moment_editor.dart';
final router = GoRouter(
routes: [
@ -16,6 +17,11 @@ final router = GoRouter(
GoRoute(
path: '/account',
builder: (context, state) => const AccountScreen(),
)
),
GoRoute(
path: '/post/moments',
builder: (context, state) => const MomentEditorScreen(),
),
],
);
);

View File

@ -1,11 +1,15 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:solaragent/auth.dart';
import 'package:solaragent/models/feed.dart';
import 'package:solaragent/models/pagination.dart';
import 'package:http/http.dart' as http;
import 'package:solaragent/router.dart';
import 'package:solaragent/widgets/feed.dart';
import 'package:solaragent/screens/publish/moment_editor.dart';
class ExploreScreen extends StatefulWidget {
const ExploreScreen({super.key});
@ -67,6 +71,20 @@ class _ExploreScreenState extends State<ExploreScreen> {
),
),
),
floatingActionButton: FutureBuilder(
future: authClient.isAuthorized(),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data == true) {
return FloatingActionButton(
child: const Icon(Icons.edit),
onPressed: () {
router.push("/post/moments");
},
);
} else {
return Container();
}
}),
);
}

View File

@ -0,0 +1,156 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:solaragent/auth.dart';
import 'package:solaragent/router.dart';
import 'package:url_launcher/url_launcher.dart';
class MomentEditorScreen extends StatefulWidget {
const MomentEditorScreen({super.key});
@override
State<MomentEditorScreen> createState() => _MomentEditorScreenState();
}
class _MomentEditorScreenState extends State<MomentEditorScreen> {
final contentController = TextEditingController();
bool isSubmitting = false;
bool showRecommendationBanner = true;
Future<void> postMoment() async {
if (authClient.client == null) return;
setState(() => isSubmitting = true);
var res = await authClient.client!.post(
Uri.parse("https://co.solsynth.dev/api/p/moments"),
headers: <String, String>{
'Content-Type': 'application/json',
},
body: jsonEncode(<String, dynamic>{
'content': contentController.value.text,
}),
);
if (res.statusCode != 200) {
var message = utf8.decode(res.bodyBytes);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Something went wrong... $message")),
);
} else {
if (router.canPop()) {
router.pop();
}
}
setState(() => isSubmitting = false);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Record a moment"),
actions: <Widget>[
TextButton(
onPressed: !isSubmitting ? postMoment : null,
child: const Text('POST'),
),
],
),
body: Column(
children: [
// Loading indicator
isSubmitting ? const LinearProgressIndicator() : Container(),
// Userinfo
FutureBuilder(
future: authClient.getProfiles(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var userinfo = snapshot.data;
return Container(
color: Colors.grey[50],
margin: const EdgeInsets.only(bottom: 12),
child: ListTile(
title: Text(userinfo["nick"]),
subtitle: const Text("You will post this post as"),
leading: CircleAvatar(
backgroundImage: NetworkImage(userinfo["picture"]),
),
),
);
} else {
return Container();
}
}),
// Editor
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: TextField(
maxLines: null,
autofocus: true,
autocorrect: true,
keyboardType: TextInputType.multiline,
controller: contentController,
decoration: const InputDecoration.collapsed(
hintText: "What\'s happened?!"),
),
),
),
// Recommend website banner
showRecommendationBanner
? FutureBuilder(
future: SharedPreferences.getInstance(),
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.data?.getBool(
"editor.hide_website_recommendation") ==
null) {
snapshot.data?.remove("editor.hide_website_recommendation");
return MaterialBanner(
padding: const EdgeInsets.all(20),
content: const Text(
'SolarAgent still in early stage development. Some features isn\'t available. We recommend use our website, also optimized for moblie!',
),
leading: const Icon(Icons.construction),
backgroundColor: const Color(0xFFE0E0E0),
actions: <Widget>[
TextButton(
child: const Text('OPEN'),
onPressed: () async {
await launchUrl(
Uri.parse("https://co.solsynth.dev"));
},
),
TextButton(
child: const Text('DISMISS'),
onPressed: () async {
await snapshot.data?.setBool(
"editor.hide_website_recommendation",
true,
);
setState(() {
showRecommendationBanner = false;
});
},
),
],
);
} else {
return Container();
}
})
: Container(),
],
),
);
}
@override
void dispose() {
contentController.dispose();
super.dispose();
}
}

View File

@ -20,70 +20,68 @@ class FeedItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Container(
color: Colors.grey[50],
child: ListTile(
title: Text(item.author.name),
leading: CircleAvatar(
backgroundImage: NetworkImage(item.author.avatar),
),
subtitle: Text(
getDescription(item.author.description),
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
),
return Column(
children: [
Container(
color: Colors.grey[50],
child: ListTile(
title: Text(item.author.name),
leading: CircleAvatar(
backgroundImage: NetworkImage(item.author.avatar),
),
subtitle: Text(
getDescription(item.author.description),
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
),
),
Markdown(
data: item.content,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
hasAttachments()
? Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(width: 0.3, color: Color(0xffdedede))),
),
Markdown(
data: item.content,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
hasAttachments()
? Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(width: 0.3, color: Color(0xffdedede))),
),
child: FlutterCarousel(
options: CarouselOptions(
height: 240.0,
showIndicator: true,
slideIndicator: const CircularSlideIndicator(),
),
child: FlutterCarousel(
options: CarouselOptions(
height: 240.0,
showIndicator: true,
slideIndicator: const CircularSlideIndicator(),
),
items: item.attachments?.map((x) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
child: InkWell(
child: Image.network(
getFileUrl(x.fileId),
fit: BoxFit.cover,
),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) {
return ImageLightbox(
url: getFileUrl(x.fileId),
);
}));
},
items: item.attachments?.map((x) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
child: InkWell(
child: Image.network(
getFileUrl(x.fileId),
fit: BoxFit.cover,
),
);
},
);
}).toList(),
),
)
: Container(),
],
),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) {
return ImageLightbox(
url: getFileUrl(x.fileId),
);
}));
},
),
);
},
);
}).toList(),
),
)
: Container(),
],
);
}
}