✨ Moment editor
This commit is contained in:
parent
048b8c0894
commit
a1fd936702
@ -2,6 +2,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:solaragent/screens/account.dart';
|
import 'package:solaragent/screens/account.dart';
|
||||||
import 'package:solaragent/screens/explore.dart';
|
import 'package:solaragent/screens/explore.dart';
|
||||||
import 'package:solaragent/screens/notifications.dart';
|
import 'package:solaragent/screens/notifications.dart';
|
||||||
|
import 'package:solaragent/screens/publish/moment_editor.dart';
|
||||||
|
|
||||||
final router = GoRouter(
|
final router = GoRouter(
|
||||||
routes: [
|
routes: [
|
||||||
@ -16,6 +17,11 @@ final router = GoRouter(
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/account',
|
path: '/account',
|
||||||
builder: (context, state) => const AccountScreen(),
|
builder: (context, state) => const AccountScreen(),
|
||||||
)
|
),
|
||||||
|
|
||||||
|
GoRoute(
|
||||||
|
path: '/post/moments',
|
||||||
|
builder: (context, state) => const MomentEditorScreen(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.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/feed.dart';
|
||||||
import 'package:solaragent/models/pagination.dart';
|
import 'package:solaragent/models/pagination.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:solaragent/router.dart';
|
||||||
import 'package:solaragent/widgets/feed.dart';
|
import 'package:solaragent/widgets/feed.dart';
|
||||||
|
import 'package:solaragent/screens/publish/moment_editor.dart';
|
||||||
|
|
||||||
class ExploreScreen extends StatefulWidget {
|
class ExploreScreen extends StatefulWidget {
|
||||||
const ExploreScreen({super.key});
|
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();
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
156
lib/screens/publish/moment_editor.dart
Normal file
156
lib/screens/publish/moment_editor.dart
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -20,70 +20,68 @@ class FeedItem extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Column(
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
Container(
|
||||||
Container(
|
color: Colors.grey[50],
|
||||||
color: Colors.grey[50],
|
child: ListTile(
|
||||||
child: ListTile(
|
title: Text(item.author.name),
|
||||||
title: Text(item.author.name),
|
leading: CircleAvatar(
|
||||||
leading: CircleAvatar(
|
backgroundImage: NetworkImage(item.author.avatar),
|
||||||
backgroundImage: NetworkImage(item.author.avatar),
|
),
|
||||||
),
|
subtitle: Text(
|
||||||
subtitle: Text(
|
getDescription(item.author.description),
|
||||||
getDescription(item.author.description),
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
maxLines: 1,
|
||||||
maxLines: 1,
|
softWrap: false,
|
||||||
softWrap: false,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Markdown(
|
),
|
||||||
data: item.content,
|
Markdown(
|
||||||
shrinkWrap: true,
|
data: item.content,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
shrinkWrap: true,
|
||||||
),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
hasAttachments()
|
),
|
||||||
? Container(
|
hasAttachments()
|
||||||
decoration: const BoxDecoration(
|
? Container(
|
||||||
border: Border(
|
decoration: const BoxDecoration(
|
||||||
top: BorderSide(width: 0.3, color: Color(0xffdedede))),
|
border: Border(
|
||||||
|
top: BorderSide(width: 0.3, color: Color(0xffdedede))),
|
||||||
|
),
|
||||||
|
child: FlutterCarousel(
|
||||||
|
options: CarouselOptions(
|
||||||
|
height: 240.0,
|
||||||
|
showIndicator: true,
|
||||||
|
slideIndicator: const CircularSlideIndicator(),
|
||||||
),
|
),
|
||||||
child: FlutterCarousel(
|
items: item.attachments?.map((x) {
|
||||||
options: CarouselOptions(
|
return Builder(
|
||||||
height: 240.0,
|
builder: (BuildContext context) {
|
||||||
showIndicator: true,
|
return Container(
|
||||||
slideIndicator: const CircularSlideIndicator(),
|
width: MediaQuery.of(context).size.width,
|
||||||
),
|
margin: const EdgeInsets.symmetric(horizontal: 5.0),
|
||||||
items: item.attachments?.map((x) {
|
child: InkWell(
|
||||||
return Builder(
|
child: Image.network(
|
||||||
builder: (BuildContext context) {
|
getFileUrl(x.fileId),
|
||||||
return Container(
|
fit: BoxFit.cover,
|
||||||
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),
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
onTap: () {
|
||||||
},
|
Navigator.push(context,
|
||||||
);
|
MaterialPageRoute(builder: (_) {
|
||||||
}).toList(),
|
return ImageLightbox(
|
||||||
),
|
url: getFileUrl(x.fileId),
|
||||||
)
|
);
|
||||||
: Container(),
|
}));
|
||||||
],
|
},
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user