chore: update how activity summary description expansion button layout works (#3930)
This commit is contained in:
parent
8fdeb2d48a
commit
d057cee222
3 changed files with 103 additions and 34 deletions
|
|
@ -5230,5 +5230,6 @@
|
|||
"myActivities": "My activities",
|
||||
"openToJoin": "Open to join",
|
||||
"results": "Results",
|
||||
"activityDone": "Activity Done!"
|
||||
"activityDone": "Activity Done!",
|
||||
"moreLabel": "more"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,37 +69,43 @@ class ActivitySummary extends StatelessWidget {
|
|||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.bottomRight,
|
||||
children: [
|
||||
Text(
|
||||
activity.description,
|
||||
style: const TextStyle(
|
||||
fontSize: 12.0,
|
||||
InlineEllipsisText(
|
||||
text: activity.description,
|
||||
maxLines: showInstructions ? null : 2,
|
||||
trailingWidth: 50.0,
|
||||
style: DefaultTextStyle.of(context)
|
||||
.style
|
||||
.copyWith(fontSize: 12.0),
|
||||
trailing: WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: toggleInstructions,
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: Size.zero,
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0.0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
),
|
||||
child: TextButton(
|
||||
onPressed: toggleInstructions,
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: Size.zero,
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
),
|
||||
child: Text(
|
||||
showInstructions
|
||||
? L10n.of(context).less
|
||||
: L10n.of(context).more,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: theme.colorScheme.primary,
|
||||
child: Text(
|
||||
showInstructions
|
||||
? L10n.of(context).less
|
||||
: L10n.of(context).moreLabel,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (showInstructions) ...[
|
||||
Row(
|
||||
|
|
@ -179,3 +185,62 @@ class ActivitySummary extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InlineEllipsisText extends StatelessWidget {
|
||||
final String text;
|
||||
final int? maxLines;
|
||||
final TextStyle? style;
|
||||
final WidgetSpan trailing;
|
||||
final double trailingWidth;
|
||||
|
||||
const InlineEllipsisText({
|
||||
super.key,
|
||||
required this.text,
|
||||
required this.trailing,
|
||||
required this.trailingWidth,
|
||||
this.maxLines,
|
||||
this.style,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final effectiveStyle = style ?? DefaultTextStyle.of(context).style;
|
||||
final span = TextSpan(text: text, style: effectiveStyle);
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final tp = TextPainter(
|
||||
text: span,
|
||||
maxLines: maxLines,
|
||||
textDirection: TextDirection.ltr,
|
||||
ellipsis: '…',
|
||||
);
|
||||
|
||||
tp.layout(maxWidth: constraints.maxWidth);
|
||||
String truncated = text;
|
||||
if (tp.didExceedMaxLines && maxLines != null) {
|
||||
// Find cutoff point where text fits
|
||||
final pos = tp.getPositionForOffset(
|
||||
Offset(
|
||||
constraints.maxWidth - trailingWidth,
|
||||
tp.preferredLineHeight * maxLines!,
|
||||
),
|
||||
);
|
||||
final endIndex = tp.getOffsetBefore(pos.offset) ?? text.length;
|
||||
truncated = '${text.substring(0, endIndex).trimRight()}…';
|
||||
}
|
||||
|
||||
tp.dispose();
|
||||
return RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(text: truncated, style: effectiveStyle),
|
||||
trailing, // always visible
|
||||
],
|
||||
),
|
||||
maxLines: maxLines,
|
||||
overflow: TextOverflow.clip, // prevent extra wrapping
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import 'package:fluffychat/pangea/course_chats/course_chats_page.dart';
|
|||
import 'package:fluffychat/pangea/course_creation/course_info_chip_widget.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_builder.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/map_clipper.dart';
|
||||
import 'package:fluffychat/pangea/course_settings/course_settings.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
|
|
@ -221,13 +222,15 @@ class SpaceDetailsContent extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (isColumnMode) ...[
|
||||
Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
userId: room.directChatMatrixID,
|
||||
size: 80.0,
|
||||
borderRadius:
|
||||
room.isSpace ? BorderRadius.circular(24.0) : null,
|
||||
ClipPath(
|
||||
clipper: MapClipper(),
|
||||
child: Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
userId: room.directChatMatrixID,
|
||||
size: 80.0,
|
||||
borderRadius: BorderRadius.circular(0.0),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16.0),
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue