✨ Add file from ID
🐛 Fix explore unauthorized render error on large screen
This commit is contained in:
@@ -147,6 +147,10 @@
|
|||||||
"addVideo": "Add video",
|
"addVideo": "Add video",
|
||||||
"addPhoto": "Add photo",
|
"addPhoto": "Add photo",
|
||||||
"addFile": "Add file",
|
"addFile": "Add file",
|
||||||
|
"addAttachmentById": "Add Attachment by ID",
|
||||||
|
"enterFileId": "Enter File ID",
|
||||||
|
"fileIdCannotBeEmpty": "File ID cannot be empty",
|
||||||
|
"failedToFetchFile": "Failed to fetch file: {}",
|
||||||
"createDirectMessage": "Send new DM",
|
"createDirectMessage": "Send new DM",
|
||||||
"gotoDirectMessage": "Go to DM",
|
"gotoDirectMessage": "Go to DM",
|
||||||
"react": "React",
|
"react": "React",
|
||||||
|
@@ -123,6 +123,10 @@
|
|||||||
"addVideo": "添加视频",
|
"addVideo": "添加视频",
|
||||||
"addPhoto": "添加照片",
|
"addPhoto": "添加照片",
|
||||||
"addFile": "添加文件",
|
"addFile": "添加文件",
|
||||||
|
"addAttachmentById": "通过 ID 添加附件",
|
||||||
|
"enterFileId": "输入文件 ID",
|
||||||
|
"fileIdCannotBeEmpty": "文件 ID 不能为空",
|
||||||
|
"failedToFetchFile": "获取文件失败: {}",
|
||||||
"createDirectMessage": "创建新私人消息",
|
"createDirectMessage": "创建新私人消息",
|
||||||
"gotoDirectMessage": "前往私信",
|
"gotoDirectMessage": "前往私信",
|
||||||
"react": "反应",
|
"react": "反应",
|
||||||
|
@@ -123,6 +123,10 @@
|
|||||||
"addVideo": "新增影片",
|
"addVideo": "新增影片",
|
||||||
"addPhoto": "新增照片",
|
"addPhoto": "新增照片",
|
||||||
"addFile": "新增檔案",
|
"addFile": "新增檔案",
|
||||||
|
"addAttachmentById": "透過 ID 新增附件",
|
||||||
|
"enterFileId": "輸入檔案 ID",
|
||||||
|
"fileIdCannotBeEmpty": "檔案 ID 不能為空",
|
||||||
|
"failedToFetchFile": "無法取得檔案: {}",
|
||||||
"createDirectMessage": "建立新私人訊息",
|
"createDirectMessage": "建立新私人訊息",
|
||||||
"gotoDirectMessage": "Go to DM",
|
"gotoDirectMessage": "Go to DM",
|
||||||
"react": "反應",
|
"react": "反應",
|
||||||
|
@@ -84,6 +84,8 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
selectedDay.value = day;
|
selectedDay.value = day;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final user = ref.watch(userInfoProvider);
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
noBackground: false,
|
noBackground: false,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@@ -198,6 +200,7 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Flexible(flex: 3, child: bodyView),
|
Flexible(flex: 3, child: bodyView),
|
||||||
const VerticalDivider(width: 1),
|
const VerticalDivider(width: 1),
|
||||||
|
if (user.value != null)
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -205,7 +208,11 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
CheckInWidget(),
|
CheckInWidget(),
|
||||||
Card(
|
Card(
|
||||||
margin: EdgeInsets.only(left: 16, right: 16, top: 8),
|
margin: EdgeInsets.only(
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
top: 8,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// Use the reusable EventCalendarWidget
|
// Use the reusable EventCalendarWidget
|
||||||
@@ -227,6 +234,25 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Flexible(
|
||||||
|
flex: 2,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Welcome to\nthe Solar Network',
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
).bold(),
|
||||||
|
const Gap(2),
|
||||||
|
Text(
|
||||||
|
'Login to explore more!',
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 36, vertical: 16),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@@ -469,6 +469,11 @@ class PostComposeScreen extends HookConsumerWidget {
|
|||||||
icon: const Icon(Symbols.videocam),
|
icon: const Icon(Symbols.videocam),
|
||||||
color: colorScheme.primary,
|
color: colorScheme.primary,
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => ComposeLogic.addAttachmentById(ref, state, context),
|
||||||
|
icon: const Icon(Symbols.attach_file),
|
||||||
|
color: colorScheme.primary,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
).padding(
|
).padding(
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 16,
|
bottom: MediaQuery.of(context).padding.bottom + 16,
|
||||||
|
@@ -392,6 +392,94 @@ class ComposeLogic {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> addAttachmentById(
|
||||||
|
WidgetRef ref,
|
||||||
|
ComposeState state,
|
||||||
|
BuildContext context,
|
||||||
|
) async {
|
||||||
|
final TextEditingController idController = TextEditingController();
|
||||||
|
String? errorMessage;
|
||||||
|
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext dialogContext) {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('addAttachmentById'.tr()),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: idController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'enterFileId'.tr(),
|
||||||
|
errorText: errorMessage,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text('cancel'.tr()),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(dialogContext).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: Text('add'.tr()),
|
||||||
|
onPressed: () async {
|
||||||
|
final fileId = idController.text.trim();
|
||||||
|
if (fileId.isEmpty) {
|
||||||
|
setState(() {
|
||||||
|
errorMessage = 'fileIdCannotBeEmpty'.tr();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
final response = await client.get(
|
||||||
|
'/drive/files/$fileId/info',
|
||||||
|
);
|
||||||
|
final SnCloudFile cloudFile = SnCloudFile.fromJson(
|
||||||
|
response.data,
|
||||||
|
);
|
||||||
|
|
||||||
|
state.attachments.value = [
|
||||||
|
...state.attachments.value,
|
||||||
|
UniversalFile(
|
||||||
|
data: cloudFile,
|
||||||
|
type: switch (cloudFile.mimeType
|
||||||
|
?.split('/')
|
||||||
|
.firstOrNull) {
|
||||||
|
'image' => UniversalFileType.image,
|
||||||
|
'video' => UniversalFileType.video,
|
||||||
|
'audio' => UniversalFileType.audio,
|
||||||
|
_ => UniversalFileType.file,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.of(dialogContext).pop();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
setState(() {
|
||||||
|
errorMessage = 'failedToFetchFile'.tr(
|
||||||
|
args: [e.toString()],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> uploadAttachment(
|
static Future<void> uploadAttachment(
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
ComposeState state,
|
ComposeState state,
|
||||||
|
Reference in New Issue
Block a user