Skip to content

Commit

Permalink
Merge branch 'main' into implement-map-view
Browse files Browse the repository at this point in the history
# Conflicts:
#	app/assets/locales/app_en.arb
#	app/lib/ui/app_route.dart
#	app/lib/ui/flow/home/components/home_top_bar.dart
#	app/lib/ui/flow/setting/profile/profile_screen.dart
#	app/pubspec.yaml
#	data/.flutter-plugins-dependencies
#	style/lib/button/icon_primary_button.dart
  • Loading branch information
kaushik committed Jul 3, 2024
2 parents 05a69ad + e99a693 commit 3c28ed3
Show file tree
Hide file tree
Showing 33 changed files with 4,196 additions and 131 deletions.
3 changes: 3 additions & 0 deletions app/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
analyzer:
errors:
constant_identifier_names: ignore
include: package:flutter_lints/flutter.yaml

linter:
Expand Down
4 changes: 4 additions & 0 deletions app/assets/images/ic_send_message.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions app/assets/locales/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@
"app_title": "YourSpace",
"app_tag_line": "Stay Close, Anywhere!",
"alert_confirm_default_title": "Are you sure",

"@_COMMON": {
},
"common_get_started": "Get Started",
"common_all": "All",
"common_next": "Next",
"common_yes": "Yes",
"common_cancel": "Cancel",
"common_delete": "Delete",
"common_okay": "Okay",
"common_today": "Today",
"common_yesterday": "Yesterday",
"common_now": "Now",
"common_just_now": "Just now",
"common_min_ago": "min ago",
"common_hour_ago": "hour ago",
"common_hours_ago": "hours ago",
"common_add": "Add",
"common_verify": "Verify",
"common_go_to_setting": "Go to settings",
Expand Down Expand Up @@ -107,6 +116,24 @@
"settings_other_option_about_us_text": "About us",
"settings_other_option_sign_out_text": "Sign out",

"message_add_member_to_send_message_title": "Add members to send messages",
"message_add_member_to_send_message_subtitle": "At least one member needs to join your space to be able to chat.",
"message_add_new_member_title": "Add a new member",
"message_tap_to_send_new_message_text": "Tap on ‘+’ to send new message to anyone in your space",
"message_delete_thread_title": "Delete thread?",
"message_delete_thread_subtitle": "Are you sure you want to delete this thread? This action cannot be undone",
"message_member_count_text": " +{memberCount}",
"@message_member_count_text": {
"placeholders": {
"memberCount": {
"type": "int"
}
}
},

"chat_start_new_chat_title": "Start new chat",
"chat_type_message_hint_text": "Type message",

"edit_profile_title": "Edit profile",
"edit_profile_first_name_title": "First name",
"edit_profile_last_name_title": "Last name",
Expand Down
184 changes: 184 additions & 0 deletions app/lib/domain/extenstions/date_formatter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import 'package:flutter/cupertino.dart';
import 'package:intl/intl.dart';
import 'package:style/extenstions/date_extenstions.dart';
import 'package:yourspace_flutter/domain/extenstions/context_extenstions.dart';

enum DateFormatType {
w,
weekShort,
month,
relativeMonth,
dayMonth,
dayMonthFull,
monthYear,
monthDayYear,
dayMonthYear,
year,
time,
pastTime,
relativeTime,
relativeDate,
relativeDateWeekday,
serverIso
}

enum DateRangeFormatType {
monthYear,
relativeDate,
dayMonthYear,
}

extension DateFormatter on DateTime {
String format(BuildContext context, DateFormatType type) {
if (isUtc) return toLocal().format(context, type);

switch (type) {
case DateFormatType.w:
return DateFormat('E').format(this).substring(0, 1);
case DateFormatType.weekShort:
return DateFormat('EEE').format(this);
case DateFormatType.month:
return DateFormat('MMMM').format(this);
case DateFormatType.relativeMonth:
return year == DateTime.now().year
? format(context, DateFormatType.month)
: format(context, DateFormatType.monthYear);
case DateFormatType.dayMonth:
return DateFormat('dd MMM').format(this);
case DateFormatType.dayMonthFull:
return DateFormat('dd MMMM').format(this);
case DateFormatType.monthYear:
return DateFormat('MMMM yyyy').format(this);
case DateFormatType.monthDayYear:
return DateFormat('MMM dd, yyyy').format(this);
case DateFormatType.dayMonthYear:
return DateFormat('dd MMM yyyy').format(this);
case DateFormatType.year:
return DateFormat('yyyy').format(this);
case DateFormatType.time:
final is24HourFormat = MediaQuery.of(context).alwaysUse24HourFormat;
return DateFormat(is24HourFormat ? 'HH:mm' : 'hh:mm a')
.format(toLocal());
case DateFormatType.relativeTime:
return _relativeTime(context);
case DateFormatType.relativeDate:
return _relativeDate(context, false);
case DateFormatType.relativeDateWeekday:
return _relativeDate(context, true);
case DateFormatType.pastTime:
return _formattedPastTime(context);
case DateFormatType.serverIso:
return DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(toUtc());
}
}

int get secondsSinceEpoch => millisecondsSinceEpoch ~/ 1000;

static String formatDateRange({
required BuildContext context,
required DateTime startDate,
required DateTime endDate,
DateRangeFormatType formatType = DateRangeFormatType.relativeDate,
}) {
if (startDate.startOfDay.isAtSameMomentAs(endDate.startOfDay)) {
if (DateRangeFormatType.relativeDate == formatType) {
return startDate.format(context, DateFormatType.relativeDate);
} else if (DateRangeFormatType.monthYear == formatType) {
return startDate.format(context, DateFormatType.monthYear);
} else {
return startDate.format(context, DateFormatType.dayMonthYear);
}
} else {
if (DateRangeFormatType.relativeDate == formatType) {
return '${startDate.format(context, DateFormatType.relativeDate)} - ${endDate.format(context, DateFormatType.relativeDate)}';
} else if (DateRangeFormatType.monthYear == formatType) {
return '${startDate.format(context, DateFormatType.monthYear)} - ${endDate.format(context, DateFormatType.monthYear)}';
} else {
if (startDate.year == endDate.year) {
return '${startDate.format(context, DateFormatType.dayMonth)} - ${endDate.format(context, DateFormatType.dayMonthYear)}';
} else {
return '${startDate.format(context, DateFormatType.dayMonthYear)} - ${endDate.format(context, DateFormatType.dayMonthYear)}';
}
}
}
}

String _relativeDate(BuildContext context, bool includeWeekday) {
final today = DateTime.now();
final yesterday = today.add(const Duration(days: -1));

if (isSameDay(today)) {
return context.l10n.common_today;
} else if (isSameDay(yesterday)) {
return context.l10n.common_yesterday;
} else if (year == today.year) {
return DateFormat('${includeWeekday ? 'EEEE, ' : ''}d MMMM').format(this);
} else {
return DateFormat('${includeWeekday ? 'EEEE, ' : ''}d MMM yyyy')
.format(this);
}
}

String _formattedPastTime(BuildContext context) {
final DateTime currentTime = DateTime.now();

if (isToday) {
final int dateSeconds = millisecondsSinceEpoch ~/ 1000;
final int currentTimeSeconds = currentTime.millisecondsSinceEpoch ~/ 1000;

if ((currentTimeSeconds - dateSeconds) < 60) {
return context.l10n.common_just_now;
} else if ((currentTimeSeconds - dateSeconds) < (60 * 60)) {
final int minutesAgo = (currentTimeSeconds - dateSeconds) ~/ 60;
return '$minutesAgo ${context.l10n.common_min_ago}';
} else {
final int hoursAgo = (currentTimeSeconds - dateSeconds) ~/ (60 * 60);

if (hoursAgo < 2) {
return '$hoursAgo ${context.l10n.common_hour_ago}';
} else {
return '$hoursAgo ${context.l10n.common_hours_ago}';
}
}
} else if (isYesterday) {
return context.l10n.common_yesterday;
} else {
final bool isSameYear = year == currentTime.year;

final String format =
isSameYear ? 'dd MMM' : 'dd MMM yyyy';
return DateFormat(format).format(this);
}
}

String _relativeTime(BuildContext context) {
final time = format(context, DateFormatType.time);

final today = DateTime.now();
final yesterday = today.add(const Duration(days: -1));

final String day;
if (isSameDay(today)) {
day = 'Today';
} else if (isSameDay(yesterday)) {
day = 'Yesterday';
} else if (year == today.year) {
day = DateFormat('d MMM').format(this);
} else {
day = DateFormat('d MMM yyyy').format(this);
}
return '$day, $time';
}

bool get isToday => startOfDay.isAtSameMomentAs(DateTime.now().startOfDay);

bool get isYesterday => startOfDay.isAtSameMomentAs(
DateTime.now().startOfDay.subtract(const Duration(days: 1)),
);
}

extension StringDateFormatter on String {
String convertTo12HrTime() => DateFormat("hh:mm a")
.format(DateFormat("HH:mm").parse(this))
.toLowerCase();
}
4 changes: 4 additions & 0 deletions app/lib/gen/assets.gen.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions app/lib/ui/app_route.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import 'dart:async';

import 'package:data/api/message/message_models.dart';
import 'package:data/api/space/space_models.dart';
import 'package:flutter/cupertino.dart';
import 'package:go_router/go_router.dart';
import 'package:yourspace_flutter/ui/flow/auth/sign_in/phone/verification/phone_verification_screen.dart';
import 'package:yourspace_flutter/ui/flow/geofence/places/places_list_view.dart';
import 'package:yourspace_flutter/ui/flow/message/chat/chat_screen.dart';
import 'package:yourspace_flutter/ui/flow/message/thread_list_screen.dart';
import 'package:yourspace_flutter/ui/flow/onboard/pick_name_screen.dart';
import 'package:yourspace_flutter/ui/flow/permission/enable_permission_view.dart';
import 'package:yourspace_flutter/ui/flow/setting/contact_support/contact_support_screen.dart';
Expand All @@ -26,6 +32,8 @@ class AppRoute {
static const pathProfile = '/profile';
static const pathEditSpace = '/space';
static const pathContactSupport = '/contact-support';
static const pathMessage = '/message';
static const pathChat = '/chat';
static const pathEnablePermission = '/enable-permission';
static const pathPlacesList = '/places-list';

Expand Down Expand Up @@ -164,6 +172,19 @@ class AppRoute {
builder: (_) => PlacesListView(spaceId: spaceId));
}

static AppRoute message(SpaceInfo space) {
return AppRoute(
pathMessage,
builder: (_) => ThreadListScreen(spaceInfo: space),
);
}
static AppRoute chat({required SpaceInfo spaceInfo, ThreadInfo? thread, List<ThreadInfo>? threadInfoList}) {
return AppRoute(
pathMessage,
builder: (_) => ChatScreen(spaceInfo: spaceInfo, threadInfo: thread, threadInfoList: threadInfoList),
);
}

static final routes = [
GoRoute(
path: intro.path,
Expand Down Expand Up @@ -231,6 +252,14 @@ class AppRoute {
path: pathContactSupport,
builder: (context, state) => state.widget(context),
),
GoRoute(
path: pathMessage,
builder: (context, state) => state.widget(context),
),
GoRoute(
path: pathChat,
builder: (context, state) => state.widget(context),
),
GoRoute(
path: pathEnablePermission,
builder: (context, state) => state.widget(context),
Expand Down
56 changes: 56 additions & 0 deletions app/lib/ui/components/profile_picture.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:style/extenstions/context_extenstions.dart';
import 'package:style/indicator/progress_indicator.dart';
import 'package:style/text/app_text_dart.dart';

class ProfileImage extends StatefulWidget {
final double size;
final String profileImageUrl;
final String firstLetter;
final TextStyle? style;
final Color? backgroundColor;

const ProfileImage({
super.key,
this.size = 64,
required this.profileImageUrl,
required this.firstLetter,
this.style,
this.backgroundColor,
});

@override
State<ProfileImage> createState() => _ProfileImageState();
}

class _ProfileImageState extends State<ProfileImage> {

@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.size,
height: widget.size,
child: ClipRRect(
borderRadius: BorderRadius.circular(widget.size / 2),
child: widget.profileImageUrl.isNotEmpty
? CachedNetworkImage(
imageUrl: widget.profileImageUrl,
placeholder: (context, url) => const AppProgressIndicator(
size: AppProgressIndicatorSize.small),
errorWidget: (context, url, error) => const Icon(Icons.error),
fit: BoxFit.cover,
)
: Container(
color: widget.backgroundColor ?? context.colorScheme.containerInverseHigh,
child: Center(
child: Text(
widget.firstLetter,
style: widget.style ?? AppTextStyle.subtitle2
.copyWith(color: context.colorScheme.textPrimary)),
),
),
),
);
}
}
Loading

0 comments on commit 3c28ed3

Please sign in to comment.