✨ Post content local cache
This commit is contained in:
parent
6590062dcb
commit
58bb549217
@ -145,6 +145,9 @@ PODS:
|
|||||||
- Sentry/HybridSDK (= 8.32.0)
|
- Sentry/HybridSDK (= 8.32.0)
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
- sqflite (0.0.3):
|
- sqflite (0.0.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@ -179,6 +182,7 @@ DEPENDENCIES:
|
|||||||
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
||||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
|
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
|
||||||
@ -245,6 +249,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/sentry_flutter/ios"
|
:path: ".symlinks/plugins/sentry_flutter/ios"
|
||||||
share_plus:
|
share_plus:
|
||||||
:path: ".symlinks/plugins/share_plus/ios"
|
:path: ".symlinks/plugins/share_plus/ios"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
sqflite:
|
sqflite:
|
||||||
:path: ".symlinks/plugins/sqflite/darwin"
|
:path: ".symlinks/plugins/sqflite/darwin"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
@ -289,6 +295,7 @@ SPEC CHECKSUMS:
|
|||||||
Sentry: 96ae1dcdf01a644bc3a3b1dc279cecaf48a833fb
|
Sentry: 96ae1dcdf01a644bc3a3b1dc279cecaf48a833fb
|
||||||
sentry_flutter: f1d86adcb93a959bc47a40d8d55059bdf7569bc5
|
sentry_flutter: f1d86adcb93a959bc47a40d8d55059bdf7569bc5
|
||||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||||
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
|
@ -1,12 +1,19 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get/get_rx/get_rx.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
import 'package:solian/models/realm.dart';
|
import 'package:solian/models/realm.dart';
|
||||||
import 'package:solian/widgets/attachments/attachment_publish.dart';
|
import 'package:solian/widgets/attachments/attachment_publish.dart';
|
||||||
import 'package:solian/widgets/posts/editor/post_editor_overview.dart';
|
import 'package:solian/widgets/posts/editor/post_editor_overview.dart';
|
||||||
import 'package:textfield_tags/textfield_tags.dart';
|
import 'package:textfield_tags/textfield_tags.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class PostEditorController extends GetxController {
|
class PostEditorController extends GetxController {
|
||||||
|
late final SharedPreferences _prefs;
|
||||||
|
|
||||||
final titleController = TextEditingController();
|
final titleController = TextEditingController();
|
||||||
final descriptionController = TextEditingController();
|
final descriptionController = TextEditingController();
|
||||||
final contentController = TextEditingController();
|
final contentController = TextEditingController();
|
||||||
@ -23,7 +30,28 @@ class PostEditorController extends GetxController {
|
|||||||
|
|
||||||
RxBool isDraft = false.obs;
|
RxBool isDraft = false.obs;
|
||||||
|
|
||||||
|
RxBool isRestoreFromLocal = false.obs;
|
||||||
|
Rx<DateTime?> lastSaveTime = Rx(null);
|
||||||
|
Timer? _saveTimer;
|
||||||
|
|
||||||
PostEditorController() {
|
PostEditorController() {
|
||||||
|
SharedPreferences.getInstance().then((inst) {
|
||||||
|
_prefs = inst;
|
||||||
|
localRead();
|
||||||
|
_saveTimer = Timer.periodic(
|
||||||
|
const Duration(seconds: 3),
|
||||||
|
(Timer t) {
|
||||||
|
if (isNotEmpty) {
|
||||||
|
localSave();
|
||||||
|
lastSaveTime.value = DateTime.now();
|
||||||
|
lastSaveTime.refresh();
|
||||||
|
} else if (_prefs.containsKey('post_editor_local_save')) {
|
||||||
|
localClear();
|
||||||
|
lastSaveTime.value = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
contentController.addListener(() {
|
contentController.addListener(() {
|
||||||
contentLength.value = contentController.text.length;
|
contentLength.value = contentController.text.length;
|
||||||
});
|
});
|
||||||
@ -57,6 +85,46 @@ class PostEditorController extends GetxController {
|
|||||||
isDraft.value = !isDraft.value;
|
isDraft.value = !isDraft.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void localSave() {
|
||||||
|
_prefs.setString(
|
||||||
|
'post_editor_local_save',
|
||||||
|
jsonEncode({
|
||||||
|
...payload,
|
||||||
|
'reply_to': replyTo.value?.toJson(),
|
||||||
|
'repost_to': repostTo.value?.toJson(),
|
||||||
|
'edit_to': editTo.value?.toJson(),
|
||||||
|
'realm': realmZone.value?.toJson(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void localRead() {
|
||||||
|
if (_prefs.containsKey('post_editor_local_save')) {
|
||||||
|
isRestoreFromLocal.value = true;
|
||||||
|
payload = jsonDecode(_prefs.getString('post_editor_local_save')!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void localClear() {
|
||||||
|
_prefs.remove('post_editor_local_save');
|
||||||
|
}
|
||||||
|
|
||||||
|
void currentClear() {
|
||||||
|
titleController.clear();
|
||||||
|
descriptionController.clear();
|
||||||
|
contentController.clear();
|
||||||
|
tagController.clearTags();
|
||||||
|
attachments.clear();
|
||||||
|
isDraft.value = false;
|
||||||
|
isRestoreFromLocal.value = false;
|
||||||
|
lastSaveTime.value = null;
|
||||||
|
contentLength.value = 0;
|
||||||
|
editTo.value = null;
|
||||||
|
replyTo.value = null;
|
||||||
|
repostTo.value = null;
|
||||||
|
realmZone.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
set editTarget(Post? value) {
|
set editTarget(Post? value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
editTo.value = null;
|
editTo.value = null;
|
||||||
@ -99,14 +167,46 @@ class PostEditorController extends GetxController {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set payload(Map<String, dynamic> value) {
|
||||||
|
titleController.text = value['title'] ?? '';
|
||||||
|
descriptionController.text = value['description'] ?? '';
|
||||||
|
contentController.text = value['content'] ?? '';
|
||||||
|
attachments.value = value['attachments'].cast<int>() ?? List.empty();
|
||||||
|
attachments.refresh();
|
||||||
|
isDraft.value = value['is_draft'];
|
||||||
|
if (value['reply_to'] != null) {
|
||||||
|
replyTo.value = Post.fromJson(value['reply_to']);
|
||||||
|
}
|
||||||
|
if (value['repost_to'] != null) {
|
||||||
|
repostTo.value = Post.fromJson(value['repost_to']);
|
||||||
|
}
|
||||||
|
if (value['edit_to'] != null) {
|
||||||
|
editTo.value = Post.fromJson(value['edit_to']);
|
||||||
|
}
|
||||||
|
if (value['realm'] != null) {
|
||||||
|
realmZone.value = Realm.fromJson(value['realm']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool get isEmpty {
|
bool get isEmpty {
|
||||||
if (contentController.text.isEmpty) return true;
|
if (contentController.text.isEmpty) return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get isNotEmpty {
|
||||||
|
return [
|
||||||
|
titleController.text.isNotEmpty,
|
||||||
|
descriptionController.text.isNotEmpty,
|
||||||
|
contentController.text.isNotEmpty,
|
||||||
|
attachments.isNotEmpty,
|
||||||
|
tagController.getTags?.isNotEmpty ?? false,
|
||||||
|
].any((x) => x);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_saveTimer?.cancel();
|
||||||
|
|
||||||
contentController.dispose();
|
contentController.dispose();
|
||||||
tagController.dispose();
|
tagController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:solian/controllers/post_editor_controller.dart';
|
import 'package:solian/controllers/post_editor_controller.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
@ -70,6 +71,7 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
|
|||||||
if (resp.statusCode != 200) {
|
if (resp.statusCode != 200) {
|
||||||
context.showErrorDialog(resp.bodyString);
|
context.showErrorDialog(resp.bodyString);
|
||||||
} else {
|
} else {
|
||||||
|
_editorController.localClear();
|
||||||
AppRouter.instance.pop(resp.body);
|
AppRouter.instance.pop(resp.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,11 +236,63 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Material(
|
Material(
|
||||||
elevation: 8,
|
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
Obx(() {
|
||||||
|
final textStyle = TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSurface
|
||||||
|
.withOpacity(0.75),
|
||||||
|
);
|
||||||
|
final showFactors = [
|
||||||
|
_editorController.isRestoreFromLocal.value,
|
||||||
|
_editorController.lastSaveTime.value != null,
|
||||||
|
];
|
||||||
|
final doShow = showFactors.any((x) => x);
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 4,
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if (showFactors[0])
|
||||||
|
Text('postRestoreFromLocal'.tr, style: textStyle)
|
||||||
|
.paddingOnly(right: 4),
|
||||||
|
if (showFactors[0])
|
||||||
|
InkWell(
|
||||||
|
child: Text('clear'.tr, style: textStyle),
|
||||||
|
onTap: () {
|
||||||
|
_editorController.localClear();
|
||||||
|
_editorController.currentClear();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (showFactors.where((x) => x).length > 1)
|
||||||
|
Text(
|
||||||
|
'·',
|
||||||
|
style: textStyle,
|
||||||
|
).paddingSymmetric(horizontal: 8),
|
||||||
|
if (showFactors[1])
|
||||||
|
Text(
|
||||||
|
'postAutoSaveAt'.trParams({
|
||||||
|
'date': DateFormat('HH:mm:ss').format(
|
||||||
|
_editorController.lastSaveTime.value ??
|
||||||
|
DateTime.now(),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.animate(target: doShow ? 1 : 0)
|
||||||
|
.fade(curve: Curves.easeInOut, duration: 300.ms);
|
||||||
|
}),
|
||||||
if (_editorController.mode.value == 0)
|
if (_editorController.mode.value == 0)
|
||||||
Obx(
|
Obx(
|
||||||
() => TweenAnimationBuilder<double>(
|
() => TweenAnimationBuilder<double>(
|
||||||
|
@ -93,8 +93,11 @@ const i18nEnglish = {
|
|||||||
'totalPostCount': 'Posts',
|
'totalPostCount': 'Posts',
|
||||||
'totalUpvote': 'Upvote',
|
'totalUpvote': 'Upvote',
|
||||||
'totalDownvote': 'Downvote',
|
'totalDownvote': 'Downvote',
|
||||||
|
'clear': 'Clear',
|
||||||
'pinPost': 'Pin this post',
|
'pinPost': 'Pin this post',
|
||||||
'unpinPost': 'Unpin this post',
|
'unpinPost': 'Unpin this post',
|
||||||
|
'postRestoreFromLocal': 'Restore from local',
|
||||||
|
'postAutoSaveAt': 'Auto saved at @date',
|
||||||
'postOverview': 'Overview',
|
'postOverview': 'Overview',
|
||||||
'postPinned': 'Pinned',
|
'postPinned': 'Pinned',
|
||||||
'postListNews': 'News',
|
'postListNews': 'News',
|
||||||
|
@ -87,8 +87,11 @@ const i18nSimplifiedChinese = {
|
|||||||
'totalPostCount': '总帖数',
|
'totalPostCount': '总帖数',
|
||||||
'totalUpvote': '获顶数',
|
'totalUpvote': '获顶数',
|
||||||
'totalDownvote': '获踩数',
|
'totalDownvote': '获踩数',
|
||||||
|
'clear': '清除',
|
||||||
'pinPost': '置顶本帖',
|
'pinPost': '置顶本帖',
|
||||||
'unpinPost': '取消置顶本帖',
|
'unpinPost': '取消置顶本帖',
|
||||||
|
'postRestoreFromLocal': '内容从本地暂存回复',
|
||||||
|
'postAutoSaveAt': '已自动保存于 @date',
|
||||||
'postOverview': '帖子概览',
|
'postOverview': '帖子概览',
|
||||||
'postPinned': '已置顶',
|
'postPinned': '已置顶',
|
||||||
'postEditorModeStory': '发个帖子',
|
'postEditorModeStory': '发个帖子',
|
||||||
|
@ -24,6 +24,7 @@ import protocol_handler_macos
|
|||||||
import screen_brightness_macos
|
import screen_brightness_macos
|
||||||
import sentry_flutter
|
import sentry_flutter
|
||||||
import share_plus
|
import share_plus
|
||||||
|
import shared_preferences_foundation
|
||||||
import sqflite
|
import sqflite
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
import wakelock_plus
|
import wakelock_plus
|
||||||
@ -48,6 +49,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
|
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
|
||||||
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
|
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
|
||||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
||||||
|
56
pubspec.lock
56
pubspec.lock
@ -1384,6 +1384,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.0.0"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
sha256: "3d4571b3c5eb58ce52a419d86e655493d0bc3020672da79f72fa0c16ca3a8ec1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.4"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_foundation
|
||||||
|
sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
sha256: "034650b71e73629ca08a0bd789fd1d83cc63c2d1e405946f7cef7bc37432f93a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -59,6 +59,7 @@ dependencies:
|
|||||||
share_plus: ^10.0.0
|
share_plus: ^10.0.0
|
||||||
flutter_cache_manager: ^3.3.3
|
flutter_cache_manager: ^3.3.3
|
||||||
flutter_markdown_selectionarea: ^0.6.17+1
|
flutter_markdown_selectionarea: ^0.6.17+1
|
||||||
|
shared_preferences: ^2.2.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user