Compare commits

...

4 Commits

Author SHA1 Message Date
7fbd4e9647 🚀 Launch 2.2.1+41 2024-12-29 12:15:22 +08:00
95d926b29f Bug fixes 2024-12-29 12:09:04 +08:00
f6cf6d0440 💄 Experimental new attachment layout 2024-12-29 12:02:26 +08:00
e503c3f02f Use analyze now for images 2024-12-29 11:09:54 +08:00
14 changed files with 106 additions and 89 deletions

View File

@ -458,6 +458,7 @@
"accountJoinedAt": "Joined at {}", "accountJoinedAt": "Joined at {}",
"accountBirthday": "Born on {}", "accountBirthday": "Born on {}",
"accountBadge": "Badge", "accountBadge": "Badge",
"accountCheckInNoRecords": "No check-in records",
"badgeCompanyStaff": "Solsynth Staff", "badgeCompanyStaff": "Solsynth Staff",
"badgeSiteMigration": "Solar Network Native", "badgeSiteMigration": "Solar Network Native",
"accountStatus": "Status", "accountStatus": "Status",

View File

@ -456,6 +456,7 @@
"accountJoinedAt": "加入于 {}", "accountJoinedAt": "加入于 {}",
"accountBirthday": "出生于 {}", "accountBirthday": "出生于 {}",
"accountBadge": "徽章", "accountBadge": "徽章",
"accountCheckInNoRecords": "暂无运势记录",
"badgeCompanyStaff": "索尔辛茨士大夫 · 员工", "badgeCompanyStaff": "索尔辛茨士大夫 · 员工",
"badgeSiteMigration": "Solar Network 原住民", "badgeSiteMigration": "Solar Network 原住民",
"accountStatus": "状态", "accountStatus": "状态",

View File

@ -173,7 +173,7 @@ PODS:
- in_app_review (2.0.0): - in_app_review (2.0.0):
- Flutter - Flutter
- Kingfisher (8.1.3) - Kingfisher (8.1.3)
- livekit_client (2.3.3): - livekit_client (2.3.4):
- Flutter - Flutter
- flutter_webrtc - flutter_webrtc
- WebRTC-SDK (= 125.6422.06) - WebRTC-SDK (= 125.6422.06)
@ -391,7 +391,7 @@ SPEC CHECKSUMS:
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011 in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
Kingfisher: f2af9028b16baf9dc6c07c570072bc41cbf009ef Kingfisher: f2af9028b16baf9dc6c07c570072bc41cbf009ef
livekit_client: 02cf2cc4357a655af12ccee70ff5596ae4e6feef livekit_client: 4eaa7a2968fc7e7c57888f43d90394547cc8d9e9
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e

View File

@ -247,6 +247,7 @@ class PostWriteController extends ChangeNotifier {
media.toFile()!, media.toFile()!,
place.$1, place.$1,
place.$2, place.$2,
analyzeNow: media.type == SnMediaType.image,
onProgress: (value) { onProgress: (value) {
progress = value; progress = value;
notifyListeners(); notifyListeners();

View File

@ -154,10 +154,12 @@ class SnAttachmentProvider {
String rid, String rid,
String cid, { String cid, {
Function(double progress)? onProgress, Function(double progress)? onProgress,
bool analyzeNow = false,
}) async { }) async {
final resp = await _sn.client.post( final resp = await _sn.client.post(
'/cgi/uc/fragments/$rid/$cid', '/cgi/uc/fragments/$rid/$cid',
data: data, data: data,
queryParameters: {'analyzeNow': analyzeNow},
options: Options(headers: {'Content-Type': 'application/octet-stream'}), options: Options(headers: {'Content-Type': 'application/octet-stream'}),
onSendProgress: (count, total) { onSendProgress: (count, total) {
if (onProgress != null) { if (onProgress != null) {
@ -178,6 +180,7 @@ class SnAttachmentProvider {
SnAttachmentFragment place, SnAttachmentFragment place,
int chunkSize, { int chunkSize, {
Function(double progress)? onProgress, Function(double progress)? onProgress,
bool analyzeNow = false,
}) async { }) async {
final Map<String, dynamic> chunks = place.fileChunks; final Map<String, dynamic> chunks = place.fileChunks;
var completedTasks = 0; var completedTasks = 0;
@ -200,6 +203,7 @@ class SnAttachmentProvider {
data, data,
place.rid, place.rid,
entry.key, entry.key,
analyzeNow: analyzeNow,
onProgress: (progress) { onProgress: (progress) {
final overallProgress = (completedTasks + progress) / chunks.length; final overallProgress = (completedTasks + progress) / chunks.length;
onProgress?.call(overallProgress); onProgress?.call(overallProgress);

View File

@ -517,6 +517,12 @@ class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateM
future: _getCheckInRecords(), future: _getCheckInRecords(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) return const SizedBox.shrink(); if (!snapshot.hasData) return const SizedBox.shrink();
if (snapshot.data!.length <= 1) {
return Text(
'accountCheckInNoRecords',
textAlign: TextAlign.center,
).tr().fontWeight(FontWeight.bold).center().padding(horizontal: 20, vertical: 8);
}
final records = snapshot.data!; final records = snapshot.data!;
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,

View File

@ -20,9 +20,11 @@ import 'package:uuid/uuid.dart';
class AttachmentItem extends StatelessWidget { class AttachmentItem extends StatelessWidget {
final SnAttachment? data; final SnAttachment? data;
final String? heroTag; final String? heroTag;
final BoxFit fit;
const AttachmentItem({ const AttachmentItem({
super.key, super.key,
this.fit = BoxFit.cover,
required this.data, required this.data,
required this.heroTag, required this.heroTag,
}); });
@ -43,7 +45,7 @@ class AttachmentItem extends StatelessWidget {
child: AutoResizeUniversalImage( child: AutoResizeUniversalImage(
sn.getAttachmentUrl(data!.rid), sn.getAttachmentUrl(data!.rid),
key: Key('attachment-${data!.rid}-$tag'), key: Key('attachment-${data!.rid}-$tag'),
fit: BoxFit.cover, fit: fit,
), ),
); );
case 'video': case 'video':
@ -322,7 +324,9 @@ class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo
MaterialDesktopCustomButton( MaterialDesktopCustomButton(
iconSize: 24, iconSize: 24,
onPressed: _toggleOriginal, onPressed: _toggleOriginal,
icon: _showOriginal ? const Icon(Symbols.high_quality, size: 24) : const Icon(Symbols.sd, size: 24), icon: Builder(builder: (context) {
return _showOriginal ? const Icon(Symbols.high_quality, size: 24) : const Icon(Symbols.sd, size: 24);
}),
), ),
], ],
), ),

View File

@ -4,8 +4,8 @@ import 'package:collection/collection.dart';
import 'package:dismissible_page/dismissible_page.dart'; import 'package:dismissible_page/dismissible_page.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:responsive_framework/responsive_framework.dart';
import 'package:surface/types/attachment.dart'; import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/attachment/attachment_zoom.dart'; import 'package:surface/widgets/attachment/attachment_zoom.dart';
import 'package:surface/widgets/attachment/attachment_item.dart'; import 'package:surface/widgets/attachment/attachment_item.dart';
@ -14,8 +14,9 @@ import 'package:uuid/uuid.dart';
class AttachmentList extends StatefulWidget { class AttachmentList extends StatefulWidget {
final List<SnAttachment?> data; final List<SnAttachment?> data;
final bool bordered; final bool bordered;
final bool gridded;
final bool noGrow; final bool noGrow;
final bool isFlatted; final BoxFit fit;
final double? maxHeight; final double? maxHeight;
final EdgeInsets? listPadding; final EdgeInsets? listPadding;
@ -23,8 +24,9 @@ class AttachmentList extends StatefulWidget {
super.key, super.key,
required this.data, required this.data,
this.bordered = false, this.bordered = false,
this.gridded = false,
this.noGrow = false, this.noGrow = false,
this.isFlatted = false, this.fit = BoxFit.cover,
this.maxHeight, this.maxHeight,
this.listPadding, this.listPadding,
}); });
@ -53,7 +55,6 @@ class _AttachmentListState extends State<AttachmentList> {
final constraints = BoxConstraints( final constraints = BoxConstraints(
minWidth: 80, minWidth: 80,
maxHeight: widget.maxHeight ?? double.infinity, maxHeight: widget.maxHeight ?? double.infinity,
maxWidth: layoutConstraints.maxWidth - 20,
); );
if (widget.data.isEmpty) return const SizedBox.shrink(); if (widget.data.isEmpty) return const SizedBox.shrink();
@ -66,52 +67,29 @@ class _AttachmentListState extends State<AttachmentList> {
} }
.toDouble(); .toDouble();
return Container( return Padding(
constraints: ResponsiveBreakpoints.of(context).largerThan(MOBILE) padding: widget.listPadding ?? EdgeInsets.zero,
? constraints.copyWith( child: Container(
maxWidth: math.min( constraints: constraints,
constraints.maxWidth, width: double.infinity,
kAttachmentMaxWidth,
),
)
: null,
child: AspectRatio(
aspectRatio: singleAspectRatio,
child: GestureDetector( child: GestureDetector(
child: Builder( child: AspectRatio(
builder: (context) { aspectRatio: singleAspectRatio,
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE) || widget.noGrow) { child: Container(
return Padding( decoration: BoxDecoration(
// Single child list-like displaying color: backgroundColor,
padding: widget.listPadding ?? EdgeInsets.zero, border: Border.fromBorderSide(borderSide),
child: Container( borderRadius: AttachmentList.kDefaultRadius,
decoration: BoxDecoration( ),
color: backgroundColor, child: ClipRRect(
border: Border(top: borderSide, bottom: borderSide), borderRadius: AttachmentList.kDefaultRadius,
borderRadius: AttachmentList.kDefaultRadius,
),
child: ClipRRect(
borderRadius: AttachmentList.kDefaultRadius,
child: AttachmentItem(
data: widget.data[0],
heroTag: heroTags[0],
),
),
),
);
}
return Container(
decoration: BoxDecoration(
color: backgroundColor,
border: Border(top: borderSide, bottom: borderSide),
),
child: AttachmentItem( child: AttachmentItem(
data: widget.data[0], data: widget.data[0],
heroTag: heroTags.first, heroTag: heroTags[0],
fit: widget.fit,
), ),
); ),
}, ),
), ),
onTap: () { onTap: () {
if (widget.data.firstOrNull?.mediaType != SnMediaType.image) return; if (widget.data.firstOrNull?.mediaType != SnMediaType.image) return;
@ -130,34 +108,53 @@ class _AttachmentListState extends State<AttachmentList> {
); );
} }
if (widget.isFlatted) { if (widget.gridded) {
return Wrap( return Padding(
spacing: 4, padding: widget.listPadding ?? EdgeInsets.zero,
runSpacing: 4, child: Container(
children: widget.data decoration: BoxDecoration(
.mapIndexed( color: backgroundColor,
(idx, ele) => AspectRatio( border: Border(
aspectRatio: (ele?.data['ratio'] ?? 1).toDouble(), top: borderSide,
child: Container( bottom: borderSide,
decoration: BoxDecoration( ),
color: backgroundColor, borderRadius: AttachmentList.kDefaultRadius,
border: Border( ),
top: borderSide, child: ClipRRect(
bottom: borderSide, borderRadius: AttachmentList.kDefaultRadius,
child: StaggeredGrid.count(
crossAxisCount: math.min(widget.data.length, 2),
crossAxisSpacing: 4,
mainAxisSpacing: 4,
children: widget.data
.mapIndexed(
(idx, ele) => GestureDetector(
child: Container(
constraints: constraints,
child: AttachmentItem(
data: ele,
heroTag: heroTags[idx],
fit: widget.fit,
),
),
onTap: () {
if (widget.data[idx]!.mediaType != SnMediaType.image) return;
context.pushTransparentRoute(
AttachmentZoomView(
data: widget.data.where((ele) => ele != null).cast(),
initialIndex: idx,
heroTags: heroTags,
),
backgroundColor: Colors.black.withOpacity(0.7),
rootNavigator: true,
);
},
), ),
borderRadius: AttachmentList.kDefaultRadius, )
), .toList(),
child: ClipRRect( ),
borderRadius: AttachmentList.kDefaultRadius, ),
child: AttachmentItem( ),
data: ele,
heroTag: heroTags[idx],
),
),
),
),
)
.toList(),
); );
} }

View File

@ -159,6 +159,7 @@ class ChatMessage extends StatelessWidget {
AttachmentList( AttachmentList(
data: data.preload!.attachments!, data: data.preload!.attachments!,
bordered: true, bordered: true,
gridded: true,
noGrow: true, noGrow: true,
maxHeight: 520, maxHeight: 520,
listPadding: const EdgeInsets.only(top: 8), listPadding: const EdgeInsets.only(top: 8),

View File

@ -83,6 +83,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
media.toFile()!, media.toFile()!,
place.$1, place.$1,
place.$2, place.$2,
analyzeNow: media.type == SnMediaType.image,
onProgress: (progress) { onProgress: (progress) {
// Calculate overall progress for attachments // Calculate overall progress for attachments
setState(() { setState(() {

View File

@ -251,7 +251,9 @@ class PostItem extends StatelessWidget {
AttachmentList( AttachmentList(
data: data.preload!.attachments!, data: data.preload!.attachments!,
bordered: true, bordered: true,
maxHeight: 560, gridded: true,
maxHeight: showFullPost ? null : 480,
fit: showFullPost ? BoxFit.cover : BoxFit.contain,
listPadding: const EdgeInsets.symmetric(horizontal: 12), listPadding: const EdgeInsets.symmetric(horizontal: 12),
), ),
if (data.body['content'] != null) if (data.body['content'] != null)
@ -332,12 +334,11 @@ class PostShareImageWidget extends StatelessWidget {
_PostQuoteContent( _PostQuoteContent(
child: data.repostTo!, child: data.repostTo!,
isRelativeDate: false, isRelativeDate: false,
isFlatted: true,
).padding(horizontal: 16, bottom: 8), ).padding(horizontal: 16, bottom: 8),
if (data.type != 'article' && (data.preload?.attachments?.isNotEmpty ?? false)) if (data.type != 'article' && (data.preload?.attachments?.isNotEmpty ?? false))
AttachmentList( AttachmentList(
data: data.preload!.attachments!, data: data.preload!.attachments!,
isFlatted: true, gridded: true,
).padding(horizontal: 16, bottom: 8), ).padding(horizontal: 16, bottom: 8),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -884,11 +885,9 @@ class _PostContentBody extends StatelessWidget {
class _PostQuoteContent extends StatelessWidget { class _PostQuoteContent extends StatelessWidget {
final SnPost child; final SnPost child;
final bool isRelativeDate; final bool isRelativeDate;
final bool isFlatted;
const _PostQuoteContent({ const _PostQuoteContent({
this.isRelativeDate = true, this.isRelativeDate = true,
this.isFlatted = false,
required this.child, required this.child,
}); });
@ -930,12 +929,14 @@ class _PostQuoteContent extends StatelessWidget {
), ),
child: AttachmentList( child: AttachmentList(
data: child.preload!.attachments!, data: child.preload!.attachments!,
isFlatted: isFlatted, maxHeight: 360,
fit: BoxFit.contain,
gridded: true,
listPadding: const EdgeInsets.symmetric(horizontal: 12), listPadding: const EdgeInsets.symmetric(horizontal: 12),
), ),
).padding( ).padding(
top: 8, top: 8,
bottom: (child.preload?.attachments?.length ?? 0) > 1 ? 12 : 0, bottom: 12,
) )
else else
const Gap(8), const Gap(8),

View File

@ -134,7 +134,7 @@ PODS:
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- in_app_review (2.0.0): - in_app_review (2.0.0):
- FlutterMacOS - FlutterMacOS
- livekit_client (2.3.3): - livekit_client (2.3.4):
- flutter_webrtc - flutter_webrtc
- FlutterMacOS - FlutterMacOS
- WebRTC-SDK (= 125.6422.06) - WebRTC-SDK (= 125.6422.06)
@ -304,7 +304,7 @@ SPEC CHECKSUMS:
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
in_app_review: a6a031b9acd03c7d103e341aa334adf2c493fb93 in_app_review: a6a031b9acd03c7d103e341aa334adf2c493fb93
livekit_client: 8b1b90a6f2445d127a018ce93cc8cf6d8ab62982 livekit_client: b7ab91e79e657d7d40da16cb2f90d517cb72d406
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82 media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5 media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5
media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5 media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5

View File

@ -1086,10 +1086,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: livekit_client name: livekit_client
sha256: a3ff529fe6745ee40cdedcd021d81c4a6ad946dd495e782596f2856eeeabc739 sha256: "7cdeb3eaeec7fb70a4cf88d9caabccbef9e3bd5f0b23c086320bc5c9acb2770b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.3" version: "2.3.4"
logging: logging:
dependency: transitive dependency: transitive
description: description:

View File

@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 2.2.1+40 version: 2.2.1+41
environment: environment:
sdk: ^3.5.4 sdk: ^3.5.4