Skip to content

Commit

Permalink
Add a preview widget for the editing user profile image + polish layout
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesChenX committed Jan 18, 2025
1 parent 90a01b2 commit f934473
Show file tree
Hide file tree
Showing 18 changed files with 794 additions and 316 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class MessageService {
if (value == MentionSyntax.mentionAllValue) {
mentionAll = true;
} else {
if (Int64.tryParseInt(value) case final Int64 userId) {
if (Int64.tryParseInt(value) case final userId?) {
mentionedUserIds.add(userId);
}
}
Expand Down
4 changes: 2 additions & 2 deletions turms-chat-demo-flutter/lib/infra/media/image_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ extension ImageProviderExtensions on ImageProvider {
[ImageConfiguration configuration = ImageConfiguration.empty]) {
final completer = Completer<ImageInfo>();
resolve(configuration).addListener(ImageStreamListener(
(imageInfo, _) async {
(imageInfo, _) {
completer.complete(imageInfo);
},
onError: completer.completeError,
));
return completer.future;
}
}
}
80 changes: 63 additions & 17 deletions turms-chat-demo-flutter/lib/infra/media/image_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ final class ImageUtils {

static ImageFormat findFormat(Uint8List data) => findFormatForData(data);

static Uint8List crop({
static Image crop({
required Image image,
required ImageShape shape,
required Offset topLeft,
required Offset bottomRight,
ImageFormat outputFormat = ImageFormat.jpg,
bool flipX = false,
bool flipY = false,
double rotationAngle = 0,
}) {
if (topLeft.dx.isNegative ||
topLeft.dy.isNegative ||
Expand All @@ -38,30 +40,74 @@ final class ImageUtils {
bottomRight.dx - topLeft.dx,
bottomRight.dy - topLeft.dy,
);
Image outputImage;
switch (shape) {
case ImageShape.rectangle:
return _findEncodeFuncForRect(outputFormat)(
copyCrop(
image,
x: topLeft.dx.toInt(),
y: topLeft.dy.toInt(),
width: size.width.toInt(),
height: size.height.toInt(),
),
outputImage = copyCrop(
image,
x: topLeft.dx.toInt(),
y: topLeft.dy.toInt(),
width: size.width.toInt(),
height: size.height.toInt(),
);
case ImageShape.circle:
final center = Offset(
topLeft.dx + size.width / 2,
topLeft.dy + size.height / 2,
);
final radius = min(size.width, size.height) / 2;
outputImage = copyCropCircle(
image.numChannels == 4 ? image : image.convert(numChannels: 4),
centerX: center.dx.toInt(),
centerY: center.dy.toInt(),
radius: radius.toInt(),
);
}
if (rotationAngle != 0) {
outputImage = copyRotate(
outputImage,
angle: rotationAngle,
);
}
if (flipX) {
if (flipY) {
outputImage = copyFlip(outputImage, direction: FlipDirection.both);
} else {
outputImage =
copyFlip(outputImage, direction: FlipDirection.horizontal);
}
} else if (flipY) {
outputImage = copyFlip(outputImage, direction: FlipDirection.vertical);
}
return outputImage;
}

static Uint8List cropAsBytes({
required Image image,
required ImageShape shape,
required Offset topLeft,
required Offset bottomRight,
bool flipX = false,
bool flipY = false,
double rotationAngle = 0,
ImageFormat outputFormat = ImageFormat.jpg,
}) {
final outputImage = crop(
image: image,
shape: shape,
topLeft: topLeft,
bottomRight: bottomRight,
flipX: flipX,
flipY: flipY,
rotationAngle: rotationAngle);
switch (shape) {
case ImageShape.rectangle:
return _findEncodeFuncForRect(outputFormat)(
outputImage,
);
case ImageShape.circle:
return _findEncodeFuncForCircle(outputFormat)(
copyCropCircle(
image.numChannels == 4 ? image : image.convert(numChannels: 4),
centerX: center.dx.toInt(),
centerY: center.dy.toInt(),
radius: radius.toInt(),
),
outputImage,
);
}
}
Expand Down Expand Up @@ -104,4 +150,4 @@ final class ImageUtils {
ImageFormat.png => encodePng,
_ => throw UnsupportedError('Unsupported format: $outputFormat'),
};
}
}
7 changes: 3 additions & 4 deletions turms-chat-demo-flutter/lib/infra/tray/tray_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ final class TrayUtils {
))
.toList()));
await trayManager.setToolTip(tooltip);
final keyToOnTap = <String, void Function()>{};
for (final item in menuItems) {
keyToOnTap[item.key] = item.onTap;
}
final keyToOnTap = <String, void Function()>{
for (final item in menuItems) item.key: item.onTap
};
trayManager.addListener(_TrayListener(onTrayMenuItemTap: (item) {
keyToOnTap[item.key]!.call();
}));
Expand Down
70 changes: 70 additions & 0 deletions turms-chat-demo-flutter/lib/infra/ui/rect_extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'dart:math';
import 'dart:ui';

import '../units/math_extensions.dart';

extension RectExtensions on Rect {
Rect scale(double scale) => Rect.fromLTRB(
left * scale,
top * scale,
right * scale,
bottom * scale,
);

Rect flip(Offset containerCenter) => Rect.fromLTRB(
containerCenter.dx - (right - containerCenter.dx),
containerCenter.dy - (bottom - containerCenter.dy),
containerCenter.dx - (left - containerCenter.dx),
containerCenter.dy - (top - containerCenter.dy),
);

Rect flipX(Offset containerCenter) => Rect.fromLTRB(
containerCenter.dx - (right - containerCenter.dx),
top,
containerCenter.dx - (left - containerCenter.dx),
bottom,
);

Rect flipY(Offset containerCenter) => Rect.fromLTRB(
left,
containerCenter.dy - (bottom - containerCenter.dy),
right,
containerCenter.dy - (top - containerCenter.dy),
);

Rect rotate(Offset containerCenter, double angleDegrees) {
final angle = angleDegrees.degreesToRadians();
final cosVal = cos(angle);
final sinVal = sin(angle);
final x1 = containerCenter.dx +
(left - containerCenter.dx) * cosVal -
(top - containerCenter.dy) * sinVal;
final y1 = containerCenter.dy +
(left - containerCenter.dx) * sinVal +
(top - containerCenter.dy) * cosVal;
final x2 = containerCenter.dx +
(right - containerCenter.dx) * cosVal -
(top - containerCenter.dy) * sinVal;
final y2 = containerCenter.dy +
(right - containerCenter.dx) * sinVal +
(top - containerCenter.dy) * cosVal;
final x3 = containerCenter.dx +
(right - containerCenter.dx) * cosVal -
(bottom - containerCenter.dy) * sinVal;
final y3 = containerCenter.dy +
(right - containerCenter.dx) * sinVal +
(bottom - containerCenter.dy) * cosVal;
final x4 = containerCenter.dx +
(left - containerCenter.dx) * cosVal -
(bottom - containerCenter.dy) * sinVal;
final y4 = containerCenter.dy +
(left - containerCenter.dx) * sinVal +
(bottom - containerCenter.dy) * cosVal;
return Rect.fromLTRB(
min(x1, min(x2, min(x3, x4))),
min(y1, min(y2, min(y3, y4))),
max(x1, max(x2, max(x3, x4))),
max(y1, max(y2, max(y3, y4))),
);
}
}
4 changes: 4 additions & 0 deletions turms-chat-demo-flutter/lib/infra/units/math_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ const _degreesToRadians = pi / 180;
extension IntMathExtension on int {
double degreesToRadians() => _degreesToRadians * this;
}

extension DoubleMathExtension on double {
double degreesToRadians() => _degreesToRadians * this;
}
2 changes: 2 additions & 0 deletions turms-chat-demo-flutter/lib/ui/desktop/components/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export 't_empty/t_empty_result.dart';
export 't_floating/t_floating.dart';
export 't_focus_tracker/t_focus_tracker.dart';
export 't_form/t_form.dart';
export 't_image/t_image.dart';
export 't_image/t_image_broken.dart';
export 't_image_cropper/t_image_cropper.dart';
export 't_image_viewer/t_image_viewer.dart';
Expand All @@ -46,4 +47,5 @@ export 't_text_field/t_text_field.dart';
export 't_title_bar/t_title_bar.dart';
export 't_toast/t_toast.dart';
export 't_tooltip/t_tooltip.dart';
export 't_transform/t_transform.dart';
export 't_window_control_zone/t_window_control_zone.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class TAvatar extends ConsumerWidget {
this.presencePopupOffset,
}) : firstChar = name.isEmpty ? '' : name.substring(0, 1);

static const borderRadius = Sizes.borderRadiusCircular4;

/// This can be any ID (e.g. user ID, group ID).
final Int64 id;
final String name;
Expand All @@ -67,7 +69,7 @@ class TAvatar extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final appLocalizations = ref.watch(appLocalizationsViewModel);
final avatar = ClipRRect(
borderRadius: Sizes.borderRadiusCircular4,
borderRadius: borderRadius,
child: _buildAvatar(context.theme),
);
if (presence == UserPresence.none) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ class _TDropZoneState extends ConsumerState<TDropZone> {
onDropEnter: _onDropEnter,
onDropLeave: _onDropLeave,
onPerformDrop: widget.onPerformDrop,
child: Padding(
padding: Sizes.paddingV8H16,
child: widget.child,
)),
child: widget.child),
_buildMask(theme, appLocalizations)
],
);
Expand All @@ -65,7 +62,7 @@ class _TDropZoneState extends ConsumerState<TDropZone> {
opacity: _dragging ? 1.0 : 0.0,
duration: const Duration(milliseconds: 100),
child: Padding(
padding: const EdgeInsets.only(right: 4, bottom: 4),
padding: Sizes.paddingV4H4,
child: DottedBorder(
borderType: BorderType.RRect,
dashPattern: [12, 10],
Expand Down Expand Up @@ -105,8 +102,9 @@ class _TDropZoneState extends ConsumerState<TDropZone> {
}

extension DropSessionExtensions on DropSession {
Future<List<DataReaderFile>> readFiles(
{bool includeDirectories = false}) async {
Future<List<DataReaderFile>> readFiles({
bool includeDirectories = false,
}) async {
final futures = items.map((item) {
final completer = Completer<DataReaderFile>();
try {
Expand Down
Loading

0 comments on commit f934473

Please sign in to comment.