Compare commits

...

4 Commits

Author SHA1 Message Date
7fe26d0df0 🚀 Launch 1.2.1+16 2024-08-19 00:33:20 +08:00
80bade0e03 View posts posted by friends 2024-08-19 00:33:03 +08:00
b63db7fe76 👽 Support use realm alias instead of id 2024-08-19 00:14:09 +08:00
49f73f5f04 ⬆️ Support new attachments system 2024-08-18 22:51:52 +08:00
27 changed files with 219 additions and 177 deletions

View File

@ -31,9 +31,9 @@ class PostEditorController extends GetxController {
Rx<Realm?> realmZone = Rx(null);
Rx<DateTime?> publishedAt = Rx(null);
Rx<DateTime?> publishedUntil = Rx(null);
RxList<int> attachments = RxList<int>.empty(growable: true);
RxList<String> attachments = RxList<String>.empty(growable: true);
RxList<String> tags = RxList<String>.empty(growable: true);
Rx<int?> thumbnail = Rx(null);
Rx<String?> thumbnail = Rx(null);
RxList<int> visibleUsers = RxList.empty(growable: true);
RxList<int> invisibleUsers = RxList.empty(growable: true);
@ -116,12 +116,12 @@ class PostEditorController extends GetxController {
return showModalBottomSheet(
context: context,
builder: (context) => AttachmentEditorPopup(
usage: 'i.attachment',
pool: 'interactive',
initialAttachments: attachments,
onAdd: (int value) {
onAdd: (String value) {
attachments.add(value);
},
onRemove: (int value) {
onRemove: (String value) {
attachments.remove(value);
},
),
@ -169,6 +169,7 @@ class PostEditorController extends GetxController {
}
void currentClear() {
aliasController.clear();
titleController.clear();
descriptionController.clear();
contentController.clear();

View File

@ -9,11 +9,12 @@ class PostListController extends GetxController {
/// The polling source modifier.
/// - `0`: default recommendations
/// - `1`: shuffle mode
/// - `1`: friend mode
/// - `2`: shuffle mode
RxInt mode = 0.obs;
/// The paging controller for infinite loading.
/// Only available when mode is `0`.
/// Only available when mode is `0` or `1`.
PagingController<int, Post> pagingController =
PagingController(firstPageKey: 0);
@ -111,10 +112,23 @@ class PostListController extends GetxController {
author: author,
);
} else {
resp = await provider.listRecommendations(
pageKey,
channel: mode.value == 0 ? null : 'shuffle',
);
switch (mode.value) {
case 2:
resp = await provider.listRecommendations(
pageKey,
channel: 'shuffle',
);
break;
case 1:
resp = await provider.listRecommendations(
pageKey,
channel: 'friends',
);
break;
default:
resp = await provider.listRecommendations(pageKey);
break;
}
}
} catch (e) {
rethrow;

View File

@ -5,11 +5,11 @@ class Attachment {
DateTime createdAt;
DateTime updatedAt;
DateTime? deletedAt;
String rid;
String uuid;
int size;
String name;
String alt;
String usage;
String mimetype;
String hash;
int destination;
@ -24,11 +24,11 @@ class Attachment {
required this.createdAt,
required this.updatedAt,
required this.deletedAt,
required this.rid,
required this.uuid,
required this.size,
required this.name,
required this.alt,
required this.usage,
required this.mimetype,
required this.hash,
required this.destination,
@ -40,42 +40,45 @@ class Attachment {
});
factory Attachment.fromJson(Map<String, dynamic> json) => Attachment(
id: json['id'],
createdAt: DateTime.parse(json['created_at']),
updatedAt: DateTime.parse(json['updated_at']),
deletedAt: json['deleted_at'] != null ? DateTime.parse(json['deleted_at']) : null,
uuid: json['uuid'],
size: json['size'],
name: json['name'],
alt: json['alt'],
usage: json['usage'],
mimetype: json['mimetype'],
hash: json['hash'],
destination: json['destination'],
isAnalyzed: json['is_analyzed'],
metadata: json['metadata'],
isMature: json['is_mature'],
account: json['account'] != null ? Account.fromJson(json['account']) : null,
accountId: json['account_id'],
);
id: json['id'],
createdAt: DateTime.parse(json['created_at']),
updatedAt: DateTime.parse(json['updated_at']),
deletedAt: json['deleted_at'] != null
? DateTime.parse(json['deleted_at'])
: null,
rid: json['rid'],
uuid: json['uuid'],
size: json['size'],
name: json['name'],
alt: json['alt'],
mimetype: json['mimetype'],
hash: json['hash'],
destination: json['destination'],
isAnalyzed: json['is_analyzed'],
metadata: json['metadata'],
isMature: json['is_mature'],
account:
json['account'] != null ? Account.fromJson(json['account']) : null,
accountId: json['account_id'],
);
Map<String, dynamic> toJson() => {
'id': id,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
'deleted_at': deletedAt?.toIso8601String(),
'uuid': uuid,
'size': size,
'name': name,
'alt': alt,
'usage': usage,
'mimetype': mimetype,
'hash': hash,
'destination': destination,
'is_analyzed': isAnalyzed,
'metadata': metadata,
'is_mature': isMature,
'account': account?.toJson(),
'account_id': accountId,
};
'id': id,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
'deleted_at': deletedAt?.toIso8601String(),
'rid': rid,
'uuid': uuid,
'size': size,
'name': name,
'alt': alt,
'mimetype': mimetype,
'hash': hash,
'destination': destination,
'is_analyzed': isAnalyzed,
'metadata': metadata,
'is_mature': isMature,
'account': account?.toJson(),
'account_id': accountId,
};
}

View File

@ -148,7 +148,7 @@ class AttachmentUploaderController extends GetxController {
Future<void> uploadAttachmentWithCallback(
Uint8List data,
String path,
String usage,
String pool,
Map<String, dynamic>? metadata,
Function(Attachment?) callback,
) async {
@ -158,7 +158,7 @@ class AttachmentUploaderController extends GetxController {
final result = await _rawUploadAttachment(
data,
path,
usage,
pool,
metadata,
onProgress: (progress) {
progressOfUpload.value = progress;
@ -171,7 +171,7 @@ class AttachmentUploaderController extends GetxController {
Future<Attachment?> uploadAttachment(
Uint8List data,
String path,
String usage,
String pool,
Map<String, dynamic>? metadata,
) async {
if (isUploading.value) throw Exception('uploading blocked');
@ -180,7 +180,7 @@ class AttachmentUploaderController extends GetxController {
final result = await _rawUploadAttachment(
data,
path,
usage,
pool,
metadata,
onProgress: (progress) {
progressOfUpload.value = progress;
@ -191,14 +191,14 @@ class AttachmentUploaderController extends GetxController {
}
Future<Attachment?> _rawUploadAttachment(
Uint8List data, String path, String usage, Map<String, dynamic>? metadata,
Uint8List data, String path, String pool, Map<String, dynamic>? metadata,
{Function(double)? onProgress, Function(dynamic err)? onError}) async {
final AttachmentProvider provider = Get.find();
try {
final result = await provider.createAttachment(
data,
path,
usage,
pool,
metadata,
onProgress: onProgress,
);

View File

@ -88,7 +88,30 @@ class ChatCallProvider extends GetxController {
void initRoom() {
initHardware();
room = Room();
room = Room(
roomOptions: const RoomOptions(
dynacast: true,
adaptiveStream: true,
defaultAudioPublishOptions: AudioPublishOptions(
name: 'call_voice',
stream: 'call_stream',
),
defaultVideoPublishOptions: VideoPublishOptions(
name: 'call_video',
stream: 'call_stream',
simulcast: true,
backupVideoCodec: BackupVideoCodec(enabled: true),
),
defaultScreenShareCaptureOptions: ScreenShareCaptureOptions(
useiOSBroadcastExtension: true,
params: VideoParametersPresets.screenShareH1080FPS30,
),
defaultCameraCaptureOptions: CameraCaptureOptions(
maxFrameRate: 30,
params: VideoParametersPresets.h1080_169,
),
),
);
listener = room.createListener();
WakelockPlus.enable();
}
@ -104,28 +127,6 @@ class ChatCallProvider extends GetxController {
await room.connect(
url,
token,
roomOptions: const RoomOptions(
dynacast: true,
adaptiveStream: true,
defaultAudioPublishOptions: AudioPublishOptions(
name: 'call_voice',
stream: 'call_stream',
),
defaultVideoPublishOptions: VideoPublishOptions(
name: 'call_video',
stream: 'call_stream',
simulcast: true,
backupVideoCodec: BackupVideoCodec(enabled: true),
),
defaultScreenShareCaptureOptions: ScreenShareCaptureOptions(
useiOSBroadcastExtension: true,
params: VideoParametersPresets.screenShareH1080FPS30,
),
defaultCameraCaptureOptions: CameraCaptureOptions(
maxFrameRate: 30,
params: VideoParametersPresets.h1080_169,
),
),
fastConnectOptions: FastConnectOptions(
microphone: TrackOption(track: audioTrack.value),
camera: TrackOption(track: videoTrack.value),
@ -152,7 +153,7 @@ class ChatCallProvider extends GetxController {
void onRoomDidUpdate() => sortParticipants();
void setupRoom() {
if(isInitialized.value) return;
if (isInitialized.value) return;
sortParticipants();
room.addListener(onRoomDidUpdate);

View File

@ -20,22 +20,22 @@ class AttachmentProvider extends GetConnect {
httpClient.baseUrl = ServiceFinder.buildUrl('files', null);
}
final Map<int, Attachment> _cachedResponses = {};
final Map<String, Attachment> _cachedResponses = {};
Future<List<Attachment?>> listMetadata(
List<int> id, {
List<String> rid, {
noCache = false,
}) async {
if (id.isEmpty) return List.empty();
if (rid.isEmpty) return List.empty();
List<Attachment?> result = List.filled(id.length, null);
List<int> pendingQuery = List.empty(growable: true);
List<Attachment?> result = List.filled(rid.length, null);
List<String> pendingQuery = List.empty(growable: true);
if (!noCache) {
for (var idx = 0; idx < id.length; idx++) {
if (_cachedResponses.containsKey(id[idx])) {
result[idx] = _cachedResponses[id[idx]];
for (var idx = 0; idx < rid.length; idx++) {
if (_cachedResponses.containsKey(rid[idx])) {
result[idx] = _cachedResponses[rid[idx]];
} else {
pendingQuery.add(id[idx]);
pendingQuery.add(rid[idx]);
}
}
}
@ -52,12 +52,12 @@ class AttachmentProvider extends GetConnect {
rawOut.data!.map((x) => Attachment.fromJson(x)).toList();
for (final item in out) {
if (item.destination != 0 && item.isAnalyzed) {
_cachedResponses[item.id] = item;
_cachedResponses[item.rid] = item;
}
}
for (var i = 0; i < out.length; i++) {
for (var j = 0; j < id.length; j++) {
if (out[i].id == id[j]) {
for (var j = 0; j < rid.length; j++) {
if (out[i].rid == rid[j]) {
result[j] = out[i];
}
}
@ -66,16 +66,16 @@ class AttachmentProvider extends GetConnect {
return result;
}
Future<Attachment?> getMetadata(int id, {noCache = false}) async {
if (!noCache && _cachedResponses.containsKey(id)) {
return _cachedResponses[id]!;
Future<Attachment?> getMetadata(String rid, {noCache = false}) async {
if (!noCache && _cachedResponses.containsKey(rid)) {
return _cachedResponses[rid]!;
}
final resp = await get('/attachments/$id/meta');
final resp = await get('/attachments/$rid/meta');
if (resp.statusCode == 200) {
final result = Attachment.fromJson(resp.body);
if (result.destination != 0 && result.isAnalyzed) {
_cachedResponses[id] = result;
_cachedResponses[rid] = result;
}
return result;
}
@ -84,7 +84,7 @@ class AttachmentProvider extends GetConnect {
}
Future<Attachment> createAttachment(
Uint8List data, String path, String usage, Map<String, dynamic>? metadata,
Uint8List data, String path, String pool, Map<String, dynamic>? metadata,
{Function(double)? onProgress}) async {
final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw Exception('unauthorized');
@ -108,7 +108,7 @@ class AttachmentProvider extends GetConnect {
final payload = dio.FormData.fromMap({
'alt': fileAlt,
'file': filePayload,
'usage': usage,
'pool': pool,
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
'metadata': jsonEncode(metadata),
});
@ -133,8 +133,7 @@ class AttachmentProvider extends GetConnect {
Future<Response> updateAttachment(
int id,
String alt,
String usage, {
String alt, {
bool isMature = false,
}) async {
final AuthProvider auth = Get.find();
@ -144,7 +143,6 @@ class AttachmentProvider extends GetConnect {
var resp = await client.put('/attachments/$id', {
'alt': alt,
'usage': usage,
'is_mature': isMature,
});
@ -169,7 +167,7 @@ class AttachmentProvider extends GetConnect {
return resp;
}
void clearCache({int? id}) {
void clearCache({String? id}) {
if (id != null) {
_cachedResponses.remove(id);
} else {

View File

@ -9,13 +9,20 @@ class PostProvider extends GetConnect {
}
Future<Response> listRecommendations(int page,
{int? realm, String? channel}) async {
{String? realm, String? channel}) async {
GetConnect client;
final AuthProvider auth = Get.find();
final queries = [
'take=${10}',
'offset=$page',
if (realm != null) 'realmId=$realm',
if (realm != null) 'realm=$realm',
];
final resp = await get(
if (auth.isAuthorized.value) {
client = auth.configureClient('co');
} else {
client = ServiceFinder.configureClient('co');
}
final resp = await client.get(
channel == null
? '/recommendations?${queries.join('&')}'
: '/recommendations/$channel?${queries.join('&')}',
@ -45,14 +52,14 @@ class PostProvider extends GetConnect {
}
Future<Response> listPost(int page,
{int? realm, String? author, tag, category}) async {
{String? realm, String? author, tag, category}) async {
final queries = [
'take=${10}',
'offset=$page',
if (tag != null) 'tag=$tag',
if (category != null) 'category=$category',
if (author != null) 'author=$author',
if (realm != null) 'realmId=$realm',
if (realm != null) 'realm=$realm',
];
final resp = await get('/posts?${queries.join('&')}');
if (resp.statusCode != 200) {

View File

@ -116,7 +116,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
attachResult = await provider.createAttachment(
await file.readAsBytes(),
file.path,
'p.$position',
'avatar',
null,
);
} catch (e) {

View File

@ -5,6 +5,7 @@ import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart';
import 'package:solian/screens/account/notification.dart';
import 'package:solian/theme.dart';
import 'package:solian/widgets/account/signin_required_overlay.dart';
import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/current_state_action.dart';
import 'package:solian/widgets/app_bar_leading.dart';
@ -27,20 +28,18 @@ class _HomeScreenState extends State<HomeScreen>
void initState() {
super.initState();
_postController = PostListController();
_tabController = TabController(length: 2, vsync: this);
_tabController = TabController(length: 3, vsync: this);
_tabController.addListener(() {
switch (_tabController.index) {
case 0:
case 1:
if (_postController.mode.value == _tabController.index) return;
_postController.mode.value = _tabController.index;
_postController.reloadAllOver();
}
if (_postController.mode.value == _tabController.index) return;
_postController.mode.value = _tabController.index;
_postController.reloadAllOver();
});
}
@override
Widget build(BuildContext context) {
final AuthProvider auth = Get.find();
return Material(
color: Theme.of(context).colorScheme.surface,
child: Scaffold(
@ -82,6 +81,7 @@ class _HomeScreenState extends State<HomeScreen>
controller: _tabController,
tabs: [
Tab(text: 'postListNews'.tr),
Tab(text: 'postListFriends'.tr),
Tab(text: 'postListShuffle'.tr),
],
),
@ -108,6 +108,23 @@ class _HomeScreenState extends State<HomeScreen>
),
]),
),
Obx(() {
if (auth.isAuthorized.value) {
return RefreshIndicator(
onRefresh: () => _postController.reloadAllOver(),
child: CustomScrollView(slivers: [
PostWarpedListWidget(
controller: _postController.pagingController,
onUpdate: () => _postController.reloadAllOver(),
),
]),
);
} else {
return SigninRequiredOverlay(
onSignedIn: () => _postController.reloadAllOver(),
);
}
}),
PostShuffleSwiper(controller: _postController),
],
);

View File

@ -65,7 +65,7 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
final AttachmentUploaderController uploader = Get.find();
if (uploader.queueOfUpload.any(
((x) => x.usage == 'i.attachment' && x.isUploading),
((x) => x.isUploading),
)) {
context.showErrorDialog('attachmentUploadInProgress'.tr);
return;
@ -90,8 +90,8 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
if (resp.statusCode != 200) {
context.showErrorDialog(resp.bodyString);
} else {
_editorController.localClear();
_editorController.currentClear();
_editorController.localClear();
AppRouter.instance.pop(resp.body);
}

View File

@ -171,7 +171,7 @@ class _RealmPostListWidgetState extends State<RealmPostListWidget> {
Response resp;
try {
resp = await provider.listPost(pageKey, realm: widget.realm.id);
resp = await provider.listPost(pageKey, realm: widget.realm.alias);
} catch (e) {
_pagingController.error = e;
return;

View File

@ -125,6 +125,7 @@ const i18nEnglish = {
'postThumbnailAttachment': 'Attachment serial number',
'postPinned': 'Pinned',
'postListNews': 'News',
'postListFriends': 'Friends',
'postListShuffle': 'Random',
'postEditorModeStory': 'Post a post',
'postEditorModeArticle': 'Post an article',

View File

@ -125,6 +125,7 @@ const i18nSimplifiedChinese = {
'articleDetail': '文章详情',
'draftBoxOpen': '打开草稿箱',
'postListNews': '新鲜事',
'postListFriends': '好友圈',
'postListShuffle': '打乱看',
'postNew': '创建新帖子',
'postNewInRealmHint': '在领域 @realm 里发表新帖子',

View File

@ -24,7 +24,6 @@ class AccountAvatar extends StatelessWidget {
if (content is String) {
direct = content.startsWith('http');
if (!isEmpty) isEmpty = content.isEmpty;
if (!isEmpty) isEmpty = content.endsWith('/attachments/0');
}
final url = direct

View File

@ -16,10 +16,12 @@ class AttachmentAttrEditorDialog extends StatefulWidget {
});
@override
State<AttachmentAttrEditorDialog> createState() => _AttachmentAttrEditorDialogState();
State<AttachmentAttrEditorDialog> createState() =>
_AttachmentAttrEditorDialogState();
}
class _AttachmentAttrEditorDialogState extends State<AttachmentAttrEditorDialog> {
class _AttachmentAttrEditorDialogState
extends State<AttachmentAttrEditorDialog> {
final _altController = TextEditingController();
bool _isBusy = false;
@ -33,11 +35,10 @@ class _AttachmentAttrEditorDialogState extends State<AttachmentAttrEditorDialog>
final resp = await provider.updateAttachment(
widget.item.id,
_altController.value.text,
widget.item.usage,
isMature: _isMature,
);
Get.find<AttachmentProvider>().clearCache(id: widget.item.id);
Get.find<AttachmentProvider>().clearCache(id: widget.item.rid);
setState(() => _isBusy = false);
return Attachment.fromJson(resp.body);
@ -109,7 +110,7 @@ class _AttachmentAttrEditorDialogState extends State<AttachmentAttrEditorDialog>
TextButton(
style: TextButton.styleFrom(
foregroundColor:
Theme.of(context).colorScheme.onSurfaceVariant),
Theme.of(context).colorScheme.onSurfaceVariant),
onPressed: () => Navigator.pop(context),
child: Text('cancel'.tr),
),

View File

@ -22,19 +22,19 @@ import 'package:solian/widgets/attachments/attachment_attr_editor.dart';
import 'package:solian/widgets/attachments/attachment_fullscreen.dart';
class AttachmentEditorPopup extends StatefulWidget {
final String usage;
final String pool;
final bool singleMode;
final bool imageOnly;
final bool autoUpload;
final double? imageMaxWidth;
final double? imageMaxHeight;
final List<int>? initialAttachments;
final void Function(int) onAdd;
final void Function(int) onRemove;
final List<String>? initialAttachments;
final void Function(String) onAdd;
final void Function(String) onRemove;
const AttachmentEditorPopup({
super.key,
required this.usage,
required this.pool,
required this.onAdd,
required this.onRemove,
this.singleMode = false,
@ -73,7 +73,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
_enqueueTaskBatch(medias.map((x) {
final file = File(x.path);
return AttachmentUploadTask(file: file, usage: widget.usage);
return AttachmentUploadTask(file: file, usage: widget.pool);
}));
} else {
final media = await _imagePicker.pickMedia(
@ -83,7 +83,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
if (media == null) return;
_enqueueTask(
AttachmentUploadTask(file: File(media.path), usage: widget.usage),
AttachmentUploadTask(file: File(media.path), usage: widget.pool),
);
}
}
@ -97,7 +97,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
final file = File(media.path);
_enqueueTask(
AttachmentUploadTask(file: file, usage: widget.usage),
AttachmentUploadTask(file: file, usage: widget.pool),
);
}
@ -113,7 +113,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
List<File> files = result.paths.map((path) => File(path!)).toList();
_enqueueTaskBatch(files.map((x) {
return AttachmentUploadTask(file: x, usage: widget.usage);
return AttachmentUploadTask(file: x, usage: widget.pool);
}));
}
@ -131,7 +131,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
final file = File(media.path);
_enqueueTask(
AttachmentUploadTask(file: file, usage: widget.usage),
AttachmentUploadTask(file: file, usage: widget.pool),
);
}
@ -181,13 +181,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
WidgetsBinding.instance.addPostFrameCallback((_) => controller.dispose());
if (input == null || input.isEmpty) return;
final value = int.tryParse(input);
if (value == null) return;
final AttachmentProvider attach = Get.find();
final result = await attach.getMetadata(value);
final result = await attach.getMetadata(input);
if (result != null) {
widget.onAdd(result.id);
widget.onAdd(result.rid);
setState(() => _attachments.add(result));
if (widget.singleMode) Navigator.pop(context);
}
@ -202,11 +200,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
_uploadController.uploadAttachmentWithCallback(
data,
'Pasted Image',
widget.usage,
widget.pool,
null,
(item) {
if (item == null) return;
widget.onAdd(item.id);
widget.onAdd(item.rid);
if (mounted) {
setState(() => _attachments.add(item));
if (widget.singleMode) Navigator.pop(context);
@ -413,11 +411,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
: () {
_uploadController
.performSingleTask(index)
.then((r) {
if (r == null) return;
widget.onAdd(r.id);
.then((out) {
if (out == null) return;
widget.onAdd(out.rid);
if (mounted) {
setState(() => _attachments.add(r));
setState(() => _attachments.add(out));
if (widget.singleMode) {
Navigator.pop(context);
}
@ -515,7 +513,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
),
onTap: () {
_deleteAttachment(element).then((_) {
widget.onRemove(element.id);
widget.onRemove(element.rid);
setState(() => _attachments.removeAt(index));
});
},
@ -529,7 +527,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
),
),
onTap: () {
widget.onRemove(element.id);
widget.onRemove(element.rid);
setState(() => _attachments.removeAt(index));
},
),
@ -560,7 +558,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
void _startUploading() {
_uploadController.performUploadQueue(onData: (r) {
widget.onAdd(r.id);
widget.onAdd(r.rid);
if (mounted) {
setState(() => _attachments.add(r));
if (widget.singleMode) Navigator.pop(context);
@ -584,7 +582,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
if (_uploadController.isUploading.value) return;
_enqueueTaskBatch(detail.files.map((x) {
final file = File(x.path);
return AttachmentUploadTask(file: file, usage: widget.usage);
return AttachmentUploadTask(file: file, usage: widget.pool);
}));
},
child: Column(

View File

@ -67,7 +67,7 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
Future<void> _saveToAlbum() async {
final url = ServiceFinder.buildUrl(
'files',
'/attachments/${widget.item.id}',
'/attachments/${widget.item.rid}',
);
if (PlatformInfo.isWeb || PlatformInfo.isDesktop) {
@ -258,7 +258,7 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
spacing: 6,
children: [
Text(
'#${widget.item.id}',
'#${widget.item.rid}',
style: metaTextStyle,
),
if (widget.item.metadata?['width'] != null &&

View File

@ -91,7 +91,7 @@ class _AttachmentItemState extends State<AttachmentItem> {
launchUrlString(
ServiceFinder.buildUrl(
'files',
'/attachments/${widget.item.id}',
'/attachments/${widget.item.rid}',
),
);
},
@ -135,7 +135,7 @@ class _AttachmentItemImage extends StatelessWidget {
fit: fit,
imageUrl: ServiceFinder.buildUrl(
'files',
'/attachments/${item.id}',
'/attachments/${item.rid}',
),
progressIndicatorBuilder: (context, url, downloadProgress) {
return Center(
@ -240,7 +240,7 @@ class _AttachmentItemVideoState extends State<_AttachmentItemVideo> {
final ratio = widget.item.metadata?['ratio'] ?? 16 / 9;
_playerController = VideoPlayerController.networkUrl(
Uri.parse(
ServiceFinder.buildUrl('files', '/attachments/${widget.item.id}'),
ServiceFinder.buildUrl('files', '/attachments/${widget.item.rid}'),
),
);
_playerController!.initialize();

View File

@ -14,7 +14,7 @@ import 'package:solian/widgets/sized_container.dart';
class AttachmentList extends StatefulWidget {
final String parentId;
final List<int> attachmentsId;
final List<String> attachmentsId;
final bool isGrid;
final bool isForceGrid;
final bool autoload;
@ -334,13 +334,13 @@ class AttachmentListEntry extends StatelessWidget {
}
class AttachmentSelfContainedEntry extends StatefulWidget {
final int id;
final String rid;
final String parentId;
final bool isDense;
const AttachmentSelfContainedEntry({
super.key,
required this.id,
required this.rid,
required this.parentId,
this.isDense = false,
});
@ -359,10 +359,12 @@ class _AttachmentSelfContainedEntryState
final AttachmentProvider attachments = Get.find();
return FutureBuilder(
future: attachments.getMetadata(widget.id),
future: attachments.getMetadata(widget.rid),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Text('Loading...');
return const Center(
child: CircularProgressIndicator(),
);
}
return AttachmentListEntry(

View File

@ -39,8 +39,8 @@ class ChatEvent extends StatelessWidget {
Widget _buildAttachment(BuildContext context, {bool isMinimal = false}) {
final attachments = item.body['attachments'] != null
? List<int>.from(item.body['attachments'].map((x) => x))
: List<int>.empty();
? List<String>.from(item.body['attachments'].map((x) => x))
: List<String>.empty();
if (attachments.isEmpty) return const SizedBox();

View File

@ -59,7 +59,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
final TextEditingController _textController = TextEditingController();
final FocusNode _focusNode = FocusNode();
final List<int> _attachments = List.empty(growable: true);
final List<String> _attachments = List.empty(growable: true);
Event? _editTo;
Event? _replyTo;
@ -68,7 +68,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
showModalBottomSheet(
context: context,
builder: (context) => AttachmentEditorPopup(
usage: 'm.attachment',
pool: 'messaging',
initialAttachments: _attachments,
onAdd: (value) {
setState(() {
@ -103,7 +103,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
final AttachmentUploaderController uploader = Get.find();
if (uploader.queueOfUpload.any(
((x) => x.usage == 'm.attachment' && x.isUploading),
((x) => x.isUploading),
)) {
context.showErrorDialog('attachmentUploadInProgress'.tr);
return;

View File

@ -131,7 +131,7 @@ class MarkdownTextContent extends StatelessWidget {
child: AttachmentSelfContainedEntry(
isDense: true,
parentId: parentId,
id: int.parse(segments[1]),
rid: segments[1],
),
),
).paddingSymmetric(vertical: 4);

View File

@ -20,7 +20,7 @@ class _PostEditorThumbnailDialogState extends State<PostEditorThumbnailDialog> {
showModalBottomSheet(
context: context,
builder: (context) => AttachmentEditorPopup(
usage: 'i.attachment',
pool: 'interactive',
singleMode: true,
imageOnly: true,
autoUpload: true,
@ -84,8 +84,7 @@ class _PostEditorThumbnailDialogState extends State<PostEditorThumbnailDialog> {
actions: [
TextButton(
onPressed: () {
widget.controller.thumbnail.value =
int.tryParse(_attachmentController.text);
widget.controller.thumbnail.value = _attachmentController.text;
Navigator.pop(context);
},
child: Text('confirm'.tr),

View File

@ -45,7 +45,7 @@ class _PostActionState extends State<PostAction> {
String id;
final box = context.findRenderObject() as RenderBox?;
if (widget.item.alias?.isNotEmpty ?? false) {
id = '${widget.item.areaAlias}:${widget.item.alias}';
id = '${widget.item.areaAlias}/${widget.item.alias}';
} else {
id = '${widget.item.id}';
}
@ -55,10 +55,10 @@ class _PostActionState extends State<PostAction> {
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);
} else {
final extraContent = [
final extraContent = <String?>[
widget.item.body['title'],
widget.item.body['description'],
];
].where((x) => x != null && x.isNotEmpty).toList();
final isExtraNotEmpty = extraContent.any((x) => x != null);
result = await Share.share(
'postShareContent'.trParams({

View File

@ -87,7 +87,7 @@ class _PostItemState extends State<PostItem> {
child: AspectRatio(
aspectRatio: 16 / 9,
child: AttachmentSelfContainedEntry(
id: widget.item.body['thumbnail'],
rid: widget.item.body['thumbnail'],
parentId: 'p${item.id}-thumbnail',
),
),
@ -292,8 +292,8 @@ class _PostItemState extends State<PostItem> {
@override
Widget build(BuildContext context) {
final List<int> attachments = item.body['attachments'] is List
? item.body['attachments']?.cast<int>()
final List<String> attachments = item.body['attachments'] is List
? item.body['attachments']?.cast<String>()
: List.empty();
final hasAttachment = attachments.isNotEmpty;

View File

@ -29,7 +29,7 @@ class _StickerUploadDialogState extends State<StickerUploadDialog> {
showModalBottomSheet(
context: context,
builder: (context) => AttachmentEditorPopup(
usage: 'sticker',
pool: 'sticker',
singleMode: true,
imageOnly: true,
autoUpload: true,

View File

@ -2,7 +2,7 @@ name: solian
description: "The Solar Network App"
publish_to: "none"
version: 1.2.1+15
version: 1.2.1+16
environment:
sdk: ">=3.3.4 <4.0.0"