Compare commits
7 Commits
48e3b510cf
...
1.4.0+14
| Author | SHA1 | Date | |
|---|---|---|---|
| d22eac5c10 | |||
| e5381dd5e0 | |||
| 1c26944a05 | |||
| df787f02a1 | |||
| db43b7dca5 | |||
| 59c4d667f6 | |||
| 063c087089 |
@@ -140,7 +140,7 @@
|
|||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
"pinPost": "Pin this post",
|
"pinPost": "Pin this post",
|
||||||
"unpinPost": "Unpin this post",
|
"unpinPost": "Unpin this post",
|
||||||
"postRestoreFromLocal": "Restore from local",
|
"postRestoreFromLocal": "Restored",
|
||||||
"postAutoSaveAt": "Auto saved at @date",
|
"postAutoSaveAt": "Auto saved at @date",
|
||||||
"postCategoriesAndTags": "Categories n' Tags",
|
"postCategoriesAndTags": "Categories n' Tags",
|
||||||
"postPublishDate": "Publish Date",
|
"postPublishDate": "Publish Date",
|
||||||
|
|||||||
@@ -43,14 +43,17 @@ class PostEditorController extends GetxController {
|
|||||||
|
|
||||||
RxBool isRestoreFromLocal = false.obs;
|
RxBool isRestoreFromLocal = false.obs;
|
||||||
Rx<DateTime?> lastSaveTime = Rx(null);
|
Rx<DateTime?> lastSaveTime = Rx(null);
|
||||||
Timer? _saveTimer;
|
Future? _saveFuture;
|
||||||
|
|
||||||
PostEditorController() {
|
PostEditorController() {
|
||||||
SharedPreferences.getInstance().then((inst) {
|
SharedPreferences.getInstance().then((inst) {
|
||||||
_prefs = inst;
|
_prefs = inst;
|
||||||
_saveTimer = Timer.periodic(
|
});
|
||||||
const Duration(seconds: 3),
|
contentController.addListener(() {
|
||||||
(Timer t) {
|
contentLength.value = contentController.text.length;
|
||||||
|
_saveFuture ??= Future.delayed(
|
||||||
|
const Duration(seconds: 1),
|
||||||
|
() {
|
||||||
if (isNotEmpty) {
|
if (isNotEmpty) {
|
||||||
localSave();
|
localSave();
|
||||||
lastSaveTime.value = DateTime.now();
|
lastSaveTime.value = DateTime.now();
|
||||||
@@ -59,12 +62,10 @@ class PostEditorController extends GetxController {
|
|||||||
localClear();
|
localClear();
|
||||||
lastSaveTime.value = null;
|
lastSaveTime.value = null;
|
||||||
}
|
}
|
||||||
|
_saveFuture = null;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
contentController.addListener(() {
|
|
||||||
contentLength.value = contentController.text.length;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> editOverview(BuildContext context) {
|
Future<void> editOverview(BuildContext context) {
|
||||||
@@ -355,8 +356,6 @@ class PostEditorController extends GetxController {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_saveTimer?.cancel();
|
|
||||||
|
|
||||||
titleController.dispose();
|
titleController.dispose();
|
||||||
descriptionController.dispose();
|
descriptionController.dispose();
|
||||||
contentController.dispose();
|
contentController.dispose();
|
||||||
|
|||||||
@@ -47,16 +47,19 @@ class ChatListShell extends StatelessWidget {
|
|||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
divider: ResizableDivider(
|
divider: ResizableDivider(
|
||||||
thickness: 0.3,
|
thickness: 0.3,
|
||||||
color: Theme.of(context).dividerColor,
|
color: Theme.of(context).dividerColor.withOpacity(0.3),
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
const ResizableChild(
|
const ResizableChild(
|
||||||
minSize: 280,
|
minSize: 280,
|
||||||
maxSize: 520,
|
maxSize: 520,
|
||||||
size: ResizableSize.pixels(320),
|
size: ResizableSize.pixels(360),
|
||||||
child: ChatList(),
|
child: ChatList(),
|
||||||
),
|
),
|
||||||
ResizableChild(child: child ?? const EmptyPagePlaceholder()),
|
ResizableChild(
|
||||||
|
minSize: 280,
|
||||||
|
child: child ?? const EmptyPagePlaceholder(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|
||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
import 'package:solian/providers/content/posts.dart';
|
import 'package:solian/providers/content/posts.dart';
|
||||||
import 'package:solian/theme.dart';
|
import 'package:solian/theme.dart';
|
||||||
import 'package:solian/widgets/app_bar_leading.dart';
|
import 'package:solian/widgets/app_bar_leading.dart';
|
||||||
import 'package:solian/widgets/app_bar_title.dart';
|
import 'package:solian/widgets/app_bar_title.dart';
|
||||||
|
import 'package:solian/widgets/loading_indicator.dart';
|
||||||
import 'package:solian/widgets/posts/post_action.dart';
|
import 'package:solian/widgets/posts/post_action.dart';
|
||||||
import 'package:solian/widgets/posts/post_owned_list.dart';
|
import 'package:solian/widgets/posts/post_item.dart';
|
||||||
import 'package:solian/widgets/root_container.dart';
|
import 'package:solian/widgets/root_container.dart';
|
||||||
|
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
||||||
|
|
||||||
class DraftBoxScreen extends StatefulWidget {
|
class DraftBoxScreen extends StatefulWidget {
|
||||||
const DraftBoxScreen({super.key});
|
const DraftBoxScreen({super.key});
|
||||||
@@ -19,38 +20,50 @@ class DraftBoxScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DraftBoxScreenState extends State<DraftBoxScreen> {
|
class _DraftBoxScreenState extends State<DraftBoxScreen> {
|
||||||
final PagingController<int, Post> _pagingController =
|
bool _isBusy = true;
|
||||||
PagingController(firstPageKey: 0);
|
int? _totalPosts;
|
||||||
|
final List<Post> _posts = List.empty(growable: true);
|
||||||
|
|
||||||
_getPosts(int pageKey) async {
|
_getPosts() async {
|
||||||
final PostProvider provider = Get.find();
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
Response resp;
|
final PostProvider posts = Get.find();
|
||||||
try {
|
final resp = await posts.listDraft(_posts.length);
|
||||||
resp = await provider.listDraft(pageKey);
|
|
||||||
} catch (e) {
|
|
||||||
_pagingController.error = e;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final PaginationResult result = PaginationResult.fromJson(resp.body);
|
final PaginationResult result = PaginationResult.fromJson(resp.body);
|
||||||
if (result.count == 0) {
|
|
||||||
_pagingController.appendLastPage([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final parsed = result.data?.map((e) => Post.fromJson(e)).toList();
|
final parsed = result.data?.map((e) => Post.fromJson(e)).toList();
|
||||||
if (parsed != null && parsed.length >= 10) {
|
_totalPosts = result.count;
|
||||||
_pagingController.appendPage(parsed, pageKey + parsed.length);
|
_posts.addAll(parsed ?? List.empty());
|
||||||
} else if (parsed != null) {
|
|
||||||
_pagingController.appendLastPage(parsed);
|
setState(() => _isBusy = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _openActions(Post item) async {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => PostAction(
|
||||||
|
item: item,
|
||||||
|
noReact: true,
|
||||||
|
),
|
||||||
|
).then((value) {
|
||||||
|
if (value is Future) {
|
||||||
|
value.then((_) {
|
||||||
|
_posts.clear();
|
||||||
|
_getPosts();
|
||||||
|
});
|
||||||
|
} else if (value != null) {
|
||||||
|
_posts.clear();
|
||||||
|
_getPosts();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_pagingController.addPageRequestListener(_getPosts);
|
_getPosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -68,47 +81,48 @@ class _DraftBoxScreenState extends State<DraftBoxScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: Column(
|
||||||
onRefresh: () => Future.sync(() => _pagingController.refresh()),
|
children: [
|
||||||
child: PagedListView<int, Post>(
|
LoadingIndicator(isActive: _isBusy),
|
||||||
pagingController: _pagingController,
|
Expanded(
|
||||||
builderDelegate: PagedChildBuilderDelegate(
|
child: RefreshIndicator(
|
||||||
itemBuilder: (context, item, index) {
|
onRefresh: () {
|
||||||
return PostOwnedListEntry(
|
_posts.clear();
|
||||||
item: item,
|
return _getPosts();
|
||||||
isFullContent: true,
|
},
|
||||||
backgroundColor:
|
child: InfiniteList(
|
||||||
Theme.of(context).colorScheme.surfaceContainerLow,
|
itemCount: _posts.length,
|
||||||
onTap: () async {
|
hasReachedMax: _totalPosts == _posts.length,
|
||||||
showModalBottomSheet(
|
isLoading: _isBusy,
|
||||||
useRootNavigator: true,
|
onFetchData: () => _getPosts(),
|
||||||
context: context,
|
itemBuilder: (context, index) {
|
||||||
builder: (context) => PostAction(
|
final item = _posts[index];
|
||||||
item: item,
|
return Card(
|
||||||
noReact: true,
|
child: GestureDetector(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
PostItem(
|
||||||
|
key: Key('p${item.id}'),
|
||||||
|
item: item,
|
||||||
|
isShowEmbed: false,
|
||||||
|
isClickable: false,
|
||||||
|
isShowReply: false,
|
||||||
|
isReactable: false,
|
||||||
|
onTapMore: () => _openActions(item),
|
||||||
|
).paddingSymmetric(vertical: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () => _openActions(item),
|
||||||
),
|
),
|
||||||
).then((value) {
|
).paddingOnly(left: 12, right: 12, bottom: 4);
|
||||||
if (value is Future) {
|
|
||||||
value.then((_) {
|
|
||||||
_pagingController.refresh();
|
|
||||||
});
|
|
||||||
} else if (value != null) {
|
|
||||||
_pagingController.refresh();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
).paddingOnly(left: 12, right: 12, bottom: 4);
|
),
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_pagingController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:solian/providers/content/posts.dart';
|
|||||||
import 'package:solian/providers/last_read.dart';
|
import 'package:solian/providers/last_read.dart';
|
||||||
import 'package:solian/theme.dart';
|
import 'package:solian/theme.dart';
|
||||||
import 'package:solian/widgets/loading_indicator.dart';
|
import 'package:solian/widgets/loading_indicator.dart';
|
||||||
|
import 'package:solian/widgets/posts/post_action.dart';
|
||||||
import 'package:solian/widgets/posts/post_item.dart';
|
import 'package:solian/widgets/posts/post_item.dart';
|
||||||
import 'package:solian/widgets/posts/post_replies.dart';
|
import 'package:solian/widgets/posts/post_replies.dart';
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
|
|
||||||
Get.find<LastReadProvider>().feedLastReadAt = _item?.id;
|
Get.find<LastReadProvider>().feedLastReadAt = _item?.id;
|
||||||
|
|
||||||
setState(() => _isBusy = false);
|
if (mounted) setState(() => _isBusy = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -67,6 +68,7 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: PostItem(
|
child: PostItem(
|
||||||
|
key: ValueKey(_item),
|
||||||
item: _item!,
|
item: _item!,
|
||||||
isClickable: false,
|
isClickable: false,
|
||||||
isOverrideEmbedClickable: true,
|
isOverrideEmbedClickable: true,
|
||||||
@@ -79,6 +81,24 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
vertical: 8,
|
vertical: 8,
|
||||||
)
|
)
|
||||||
: EdgeInsets.zero,
|
: EdgeInsets.zero,
|
||||||
|
onTapMore: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => PostAction(
|
||||||
|
item: _item!,
|
||||||
|
noReact: true,
|
||||||
|
),
|
||||||
|
).then((value) {
|
||||||
|
if (value is Future) {
|
||||||
|
value.then((_) {
|
||||||
|
_getDetail();
|
||||||
|
});
|
||||||
|
} else if (value != null) {
|
||||||
|
_getDetail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -34,6 +35,24 @@ class ChatEventList extends StatelessWidget {
|
|||||||
return a.createdAt.difference(b.createdAt).inMinutes <= 3;
|
return a.createdAt.difference(b.createdAt).inMinutes <= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _openActions(BuildContext context, Event item) {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => ChatEventAction(
|
||||||
|
channel: channel,
|
||||||
|
realm: channel.realm,
|
||||||
|
item: item,
|
||||||
|
onEdit: () {
|
||||||
|
onEdit(item);
|
||||||
|
},
|
||||||
|
onReply: () {
|
||||||
|
onReply(item);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CustomScrollView(
|
return CustomScrollView(
|
||||||
@@ -65,50 +84,45 @@ class ChatEventList extends StatelessWidget {
|
|||||||
|
|
||||||
final item = chatController.currentEvents[index].data;
|
final item = chatController.currentEvents[index].data;
|
||||||
|
|
||||||
return GestureDetector(
|
return TapRegion(
|
||||||
behavior: HitTestBehavior.opaque,
|
child: GestureDetector(
|
||||||
child: Builder(builder: (context) {
|
behavior: HitTestBehavior.opaque,
|
||||||
final widget = ChatEvent(
|
child: Builder(builder: (context) {
|
||||||
key: Key('m${item!.uuid}'),
|
final widget = ChatEvent(
|
||||||
item: item,
|
key: Key('m${item!.uuid}'),
|
||||||
isMerged: isMerged,
|
item: item,
|
||||||
chatController: chatController,
|
isMerged: isMerged,
|
||||||
).paddingOnly(
|
chatController: chatController,
|
||||||
top: !isMerged ? 8 : 0,
|
).paddingOnly(
|
||||||
bottom: !hasMerged ? 8 : 0,
|
top: !isMerged ? 8 : 0,
|
||||||
);
|
bottom: !hasMerged ? 8 : 0,
|
||||||
|
);
|
||||||
|
|
||||||
if (noAnimated) {
|
if (noAnimated) {
|
||||||
return widget;
|
return widget;
|
||||||
} else {
|
} else {
|
||||||
return widget
|
return widget
|
||||||
.animate(
|
.animate(
|
||||||
key: Key('animated-m${item.uuid}'),
|
key: Key('animated-m${item.uuid}'),
|
||||||
)
|
)
|
||||||
.slideY(
|
.slideY(
|
||||||
curve: Curves.fastLinearToSlowEaseIn,
|
curve: Curves.fastLinearToSlowEaseIn,
|
||||||
duration: 250.ms,
|
duration: 250.ms,
|
||||||
begin: 0.5,
|
begin: 0.5,
|
||||||
end: 0,
|
end: 0,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
onLongPress: () {
|
||||||
|
_openActions(context, item!);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
onTapInside: (event) {
|
||||||
|
if (event.buttons == kSecondaryMouseButton) {
|
||||||
|
_openActions(context, item!);
|
||||||
|
} else if (event.buttons == kMiddleMouseButton) {
|
||||||
|
onReply(item!);
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
onLongPress: () {
|
|
||||||
showModalBottomSheet(
|
|
||||||
useRootNavigator: true,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => ChatEventAction(
|
|
||||||
channel: channel,
|
|
||||||
realm: channel.realm,
|
|
||||||
item: item!,
|
|
||||||
onEdit: () {
|
|
||||||
onEdit(item);
|
|
||||||
},
|
|
||||||
onReply: () {
|
|
||||||
onReply(item);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,8 +4,13 @@ import 'package:gap/gap.dart';
|
|||||||
|
|
||||||
class LoadingIndicator extends StatefulWidget {
|
class LoadingIndicator extends StatefulWidget {
|
||||||
final bool isActive;
|
final bool isActive;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
|
||||||
const LoadingIndicator({super.key, this.isActive = true});
|
const LoadingIndicator({
|
||||||
|
super.key,
|
||||||
|
this.isActive = true,
|
||||||
|
this.backgroundColor,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<LoadingIndicator> createState() => _LoadingIndicatorState();
|
State<LoadingIndicator> createState() => _LoadingIndicatorState();
|
||||||
@@ -63,7 +68,7 @@ class _LoadingIndicatorState extends State<LoadingIndicator>
|
|||||||
axisAlignment: -1, // Align animation from the top
|
axisAlignment: -1, // Align animation from the top
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
|
||||||
color:
|
color: widget.backgroundColor ??
|
||||||
Theme.of(context).colorScheme.surfaceContainerLow.withOpacity(0.5),
|
Theme.of(context).colorScheme.surfaceContainerLow.withOpacity(0.5),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import 'package:solian/exts.dart';
|
|||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
import 'package:solian/platform.dart';
|
import 'package:solian/platform.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
|
import 'package:solian/providers/content/posts.dart';
|
||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
import 'package:solian/screens/posts/post_editor.dart';
|
import 'package:solian/screens/posts/post_editor.dart';
|
||||||
import 'package:solian/widgets/loading_indicator.dart';
|
import 'package:solian/widgets/loading_indicator.dart';
|
||||||
@@ -28,20 +29,14 @@ class PostAction extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PostActionState extends State<PostAction> {
|
class _PostActionState extends State<PostAction> {
|
||||||
bool _isBusy = true;
|
bool _isBusy = false;
|
||||||
bool _canModifyContent = false;
|
bool _canModifyContent = false;
|
||||||
|
|
||||||
void _checkAbleToModifyContent() async {
|
void _checkAbleToModifyContent() async {
|
||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
if (auth.isAuthorized.isFalse) return;
|
if (auth.isAuthorized.isFalse) return;
|
||||||
|
|
||||||
setState(() => _isBusy = true);
|
_canModifyContent = auth.userProfile.value!['id'] == widget.item.author.id;
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_canModifyContent =
|
|
||||||
auth.userProfile.value!['id'] == widget.item.author.id;
|
|
||||||
_isBusy = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _doShare({bool noUri = false}) async {
|
Future<void> _doShare({bool noUri = false}) async {
|
||||||
@@ -94,17 +89,23 @@ class _PostActionState extends State<PostAction> {
|
|||||||
: List.empty();
|
: List.empty();
|
||||||
final hasMultipleAttachment = attachments.length > 1;
|
final hasMultipleAttachment = attachments.length > 1;
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
final double width = hasMultipleAttachment ? 640 : 480;
|
||||||
|
|
||||||
final screenshot = ScreenshotController();
|
final screenshot = ScreenshotController();
|
||||||
final image = await screenshot.captureFromLongWidget(
|
final image = await screenshot.captureFromLongWidget(
|
||||||
MediaQuery(
|
MediaQuery(
|
||||||
data: MediaQuery.of(context),
|
data: MediaQuery.of(context).copyWith(
|
||||||
|
size: Size(width, double.infinity),
|
||||||
|
),
|
||||||
child: PostShareImage(item: widget.item),
|
child: PostShareImage(item: widget.item),
|
||||||
),
|
),
|
||||||
context: context,
|
context: context,
|
||||||
pixelRatio: 2,
|
pixelRatio: 2,
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
minWidth: 480,
|
minWidth: 480,
|
||||||
maxWidth: hasMultipleAttachment ? 640 : 480,
|
maxWidth: width,
|
||||||
minHeight: 640,
|
minHeight: 640,
|
||||||
maxHeight: double.infinity,
|
maxHeight: double.infinity,
|
||||||
),
|
),
|
||||||
@@ -137,6 +138,21 @@ class _PostActionState extends State<PostAction> {
|
|||||||
);
|
);
|
||||||
context.showSnackbar('fileSavedAt'.trParams({'path': filepath}));
|
context.showSnackbar('fileSavedAt'.trParams({'path': filepath}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Post> _getFullPost() async {
|
||||||
|
final PostProvider posts = Get.find();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final resp = await posts.getPost(widget.item.id.toString());
|
||||||
|
return Post.fromJson(resp.body);
|
||||||
|
} catch (e) {
|
||||||
|
context.showErrorDialog(e).then((_) => Navigator.pop(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
return widget.item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -182,7 +198,13 @@ class _PostActionState extends State<PostAction> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingOnly(left: 24, right: 24, top: 32, bottom: 16),
|
).paddingOnly(left: 24, right: 24, top: 32, bottom: 16),
|
||||||
LoadingIndicator(isActive: _isBusy),
|
LoadingIndicator(
|
||||||
|
isActive: _isBusy,
|
||||||
|
backgroundColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainerHigh
|
||||||
|
.withOpacity(0.5),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
@@ -205,10 +227,12 @@ class _PostActionState extends State<PostAction> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.image),
|
icon: const Icon(Icons.image),
|
||||||
tooltip: 'shareImage'.tr,
|
tooltip: 'shareImage'.tr,
|
||||||
onPressed: () async {
|
onPressed: _isBusy
|
||||||
await _shareImage();
|
? null
|
||||||
Navigator.pop(context);
|
: () async {
|
||||||
},
|
await _shareImage();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -288,15 +312,23 @@ class _PostActionState extends State<PostAction> {
|
|||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
leading: const Icon(Icons.edit),
|
leading: const Icon(Icons.edit),
|
||||||
title: Text('edit'.tr),
|
title: Text('edit'.tr),
|
||||||
onTap: () async {
|
onTap: _isBusy
|
||||||
Navigator.pop(
|
? null
|
||||||
context,
|
: () async {
|
||||||
AppRouter.instance.pushNamed(
|
setState(() => _isBusy = true);
|
||||||
'postEditor',
|
var item = widget.item;
|
||||||
extra: PostPublishArguments(edit: widget.item),
|
if (item.body?['content_truncated'] == true) {
|
||||||
),
|
item = await _getFullPost();
|
||||||
);
|
}
|
||||||
},
|
Navigator.pop(
|
||||||
|
context,
|
||||||
|
AppRouter.instance.pushNamed(
|
||||||
|
'postEditor',
|
||||||
|
extra: PostPublishArguments(edit: item),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (mounted) setState(() => _isBusy = false);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
if (_canModifyContent)
|
if (_canModifyContent)
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class PostItem extends StatefulWidget {
|
|||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
|
|
||||||
final Function? onComment;
|
final Function? onComment;
|
||||||
|
final Function? onTapMore;
|
||||||
|
|
||||||
const PostItem({
|
const PostItem({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -55,6 +56,7 @@ class PostItem extends StatefulWidget {
|
|||||||
this.attachmentParent,
|
this.attachmentParent,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.onComment,
|
this.onComment,
|
||||||
|
this.onTapMore,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -99,6 +101,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
_PostHeaderWidget(
|
_PostHeaderWidget(
|
||||||
isCompact: widget.isCompact,
|
isCompact: widget.isCompact,
|
||||||
isFullDate: widget.isFullDate,
|
isFullDate: widget.isFullDate,
|
||||||
|
onTapMore: widget.onTapMore,
|
||||||
item: item,
|
item: item,
|
||||||
).paddingSymmetric(horizontal: 12),
|
).paddingSymmetric(horizontal: 12),
|
||||||
_PostHeaderDividerWidget(item: item).paddingSymmetric(horizontal: 12),
|
_PostHeaderDividerWidget(item: item).paddingSymmetric(horizontal: 12),
|
||||||
@@ -161,6 +164,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
_PostHeaderWidget(
|
_PostHeaderWidget(
|
||||||
isCompact: widget.isCompact,
|
isCompact: widget.isCompact,
|
||||||
isFullDate: widget.isFullDate,
|
isFullDate: widget.isFullDate,
|
||||||
|
onTapMore: widget.onTapMore,
|
||||||
item: item,
|
item: item,
|
||||||
),
|
),
|
||||||
_PostHeaderDividerWidget(item: item),
|
_PostHeaderDividerWidget(item: item),
|
||||||
@@ -588,10 +592,13 @@ class _PostHeaderWidget extends StatelessWidget {
|
|||||||
final bool isFullDate;
|
final bool isFullDate;
|
||||||
final Post item;
|
final Post item;
|
||||||
|
|
||||||
|
final Function? onTapMore;
|
||||||
|
|
||||||
const _PostHeaderWidget({
|
const _PostHeaderWidget({
|
||||||
required this.isCompact,
|
required this.isCompact,
|
||||||
required this.isFullDate,
|
required this.isFullDate,
|
||||||
required this.item,
|
required this.item,
|
||||||
|
required this.onTapMore,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -649,10 +656,12 @@ class _PostHeaderWidget extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (item.type == 'article')
|
if (onTapMore != null)
|
||||||
Badge(
|
IconButton(
|
||||||
label: Text('article'.tr),
|
color: Theme.of(context).colorScheme.primary,
|
||||||
).paddingOnly(top: 3),
|
icon: const Icon(Icons.more_vert),
|
||||||
|
onPressed: () => onTapMore!(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ class PostListEntryWidget extends StatelessWidget {
|
|||||||
isClickable: isNestedClickable,
|
isClickable: isNestedClickable,
|
||||||
showFeaturedReply: showFeaturedReply,
|
showFeaturedReply: showFeaturedReply,
|
||||||
padding: padding,
|
padding: padding,
|
||||||
|
onTapMore: () => _openActions(context),
|
||||||
onComment: () {
|
onComment: () {
|
||||||
AppRouter.instance
|
AppRouter.instance
|
||||||
.pushNamed(
|
.pushNamed(
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:solian/models/post.dart';
|
|
||||||
import 'package:solian/widgets/posts/post_item.dart';
|
|
||||||
|
|
||||||
class PostOwnedListEntry extends StatelessWidget {
|
|
||||||
final Post item;
|
|
||||||
final Function onTap;
|
|
||||||
final bool isFullContent;
|
|
||||||
final Color? backgroundColor;
|
|
||||||
|
|
||||||
const PostOwnedListEntry({
|
|
||||||
super.key,
|
|
||||||
required this.item,
|
|
||||||
required this.onTap,
|
|
||||||
this.isFullContent = false,
|
|
||||||
this.backgroundColor,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Card(
|
|
||||||
child: GestureDetector(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
PostItem(
|
|
||||||
key: Key('p${item.id}'),
|
|
||||||
item: item,
|
|
||||||
isShowEmbed: false,
|
|
||||||
isClickable: false,
|
|
||||||
isShowReply: false,
|
|
||||||
isReactable: false,
|
|
||||||
).paddingSymmetric(vertical: 8),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onTap: () => onTap(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -74,10 +74,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0"
|
version: "2.6.0"
|
||||||
async:
|
async:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: solian
|
|||||||
description: "The Solar Network App"
|
description: "The Solar Network App"
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
version: 1.3.8+13
|
version: 1.4.0+14
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.3.4 <4.0.0"
|
sdk: ">=3.3.4 <4.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user