Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PART-2] TW-1690: Show external channel after search #1775

Closed
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion assets/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3059,5 +3059,7 @@
"placeholders": {
"user": {}
}
}
},
"view": "View",
"join": "Join"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import 'package:fluffychat/pages/search/public_room/empty_search_public_room_widget_style.dart';
import 'package:fluffychat/pages/search/public_room/search_public_room_view_style.dart';
import 'package:fluffychat/widgets/twake_components/twake_text_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

class EmptySearchPublicRoomWidget extends StatelessWidget {
final String genericSearchTerm;
final VoidCallback? onTapJoin;

const EmptySearchPublicRoomWidget({
super.key,
required this.genericSearchTerm,
this.onTapJoin,
});

@override
Widget build(BuildContext context) {
return Padding(
padding: SearchPublicRoomViewStyle.paddingListItem,
Te-Z marked this conversation as resolved.
Show resolved Hide resolved
child: Padding(
padding: SearchPublicRoomViewStyle.paddingInsideListItem,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: SearchPublicRoomViewStyle.paddingAvatar,
child: ClipRRect(
borderRadius:
EmptySearchPublicRoomWidgetStyle.avatarBorderRadius,
child: Container(
width: EmptySearchPublicRoomWidgetStyle.avatarSize,
height: EmptySearchPublicRoomWidgetStyle.avatarSize,
decoration: BoxDecoration(
borderRadius:
EmptySearchPublicRoomWidgetStyle.avatarBorderRadius,
color:
EmptySearchPublicRoomWidgetStyle.avatarBackgroundColor(
context,
),
border:
EmptySearchPublicRoomWidgetStyle.avatarBorder(context),
),
child: Center(
child: Text(
genericSearchTerm[0],
sherlockvn marked this conversation as resolved.
Show resolved Hide resolved
style: EmptySearchPublicRoomWidgetStyle
.avatarLetterTextStyle(
context,
),
),
),
),
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
genericSearchTerm,
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
style: SearchPublicRoomViewStyle.roomNameTextStyle,
),
const SizedBox(
height: SearchPublicRoomViewStyle.nameToButtonSpace,
),
TwakeTextButton(
message: L10n.of(context)!.join,
styleMessage:
SearchPublicRoomViewStyle.joinButtonLabelStyle(context),
paddingAll: 0.0,
onTap: onTapJoin,
),
],
),
),
],
),
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';

class EmptySearchPublicRoomWidgetStyle {
static const double avatarSize = 56.0;

static const BorderRadiusGeometry avatarBorderRadius =
BorderRadius.all(Radius.circular(28.0));
static BoxBorder avatarBorder(BuildContext context) {
return Border.all(
color: Theme.of(context).brightness == Brightness.dark
? const Color(0x222c2d2f)
: const Color(0x222c2d2f),
Te-Z marked this conversation as resolved.
Show resolved Hide resolved
width: 1,
Te-Z marked this conversation as resolved.
Show resolved Hide resolved
);
}

static Color avatarBackgroundColor(BuildContext context) {
return Theme.of(context).brightness == Brightness.dark
? const Color(0x222c2d2f)
Te-Z marked this conversation as resolved.
Show resolved Hide resolved
: Colors.transparent;
}

static TextStyle avatarLetterTextStyle(BuildContext context) {
return TextStyle(
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black,
fontSize: 24,
);
}
}
26 changes: 26 additions & 0 deletions lib/pages/search/public_room/public_room_actions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:fluffychat/pages/search/public_room/search_public_room_view_style.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter/material.dart';

enum PublicRoomActions {
join,
view;

String getLabel(BuildContext context) {
switch (this) {
case PublicRoomActions.join:
return L10n.of(context)!.join;
case PublicRoomActions.view:
return L10n.of(context)!.view;
}
}

TextStyle? getLabelStyle(BuildContext context) {
switch (this) {
case PublicRoomActions.join:
return SearchPublicRoomViewStyle.joinButtonLabelStyle(context);
case PublicRoomActions.view:
return SearchPublicRoomViewStyle.viewButtonLabelStyle(context);
}
}
}
186 changes: 186 additions & 0 deletions lib/pages/search/public_room/search_public_room_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import 'package:dartz/dartz.dart';
import 'package:fluffychat/app_state/failure.dart';
import 'package:fluffychat/app_state/success.dart';
import 'package:fluffychat/di/global/get_it_initializer.dart';
import 'package:fluffychat/domain/app_state/search/public_room_state.dart';
import 'package:fluffychat/domain/usecase/search/public_room_interactor.dart';
import 'package:fluffychat/pages/search/public_room/public_room_actions.dart';
import 'package:fluffychat/pages/search/search_debouncer_mixin.dart';
import 'package:fluffychat/presentation/model/search/public_room/presentation_search_public_room.dart';
import 'package:fluffychat/presentation/model/search/public_room/presentation_search_public_room_empty.dart';
import 'package:fluffychat/presentation/model/search/public_room/presentation_search_public_room_state.dart';
import 'package:fluffychat/utils/dialog/twake_dialog.dart';
import 'package:fluffychat/utils/string_extension.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';

class SearchPublicRoomController with SearchDebouncerMixin {
SearchPublicRoomController();

final PublicRoomInteractor _publicRoomInteractor =
getIt.get<PublicRoomInteractor>();

final searchResultsNotifier =
ValueNotifier<PresentationSearchPublicRoomUIState>(
PresentationSearchPublicRoomInitial(),
);

static const int _limitPublicRoomSearchFilter = 20;

PublicRoomQueryFilter? _filter;

bool get searchTermIsNotEmpty =>
_filter?.genericSearchTerm?.isNotEmpty == true;

String? get genericSearchTerm => _filter?.genericSearchTerm;

void init() {
initializeDebouncer((keyword) {
_updateFilter(keyword);

if (keyword.isEmpty) return;

if (keyword.isRoomAlias()) {
_resetSearchResults();
_searchPublicRoom();
} else if (keyword.isRoomId()) {
_handleKeywordIsRoomId();
}
});
}

void _updateFilter(String keyword) {
_filter = PublicRoomQueryFilter(
genericSearchTerm: keyword,
);
}

void _resetSearchResults() {
searchResultsNotifier.value = PresentationSearchPublicRoom(
searchResults: [],
);
}

void _searchPublicRoom() {
_publicRoomInteractor
.execute(
filter: _filter,
limit: _limitPublicRoomSearchFilter,
server: getServerName(_filter?.genericSearchTerm),
)
.listen(
(searchResult) => _handleListenSearchPublicRoom(searchResult),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create a streamSubscription, then dispose it when done

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

);
}

void _handleListenSearchPublicRoom(Either<Failure, Success> searchResult) {
searchResult.fold((failure) => _resetSearchResults(), (success) {
if (!searchTermIsNotEmpty) {
return;
}

if (success is PublicRoomSuccess) {
if (success.publicRoomsChunk == null) {
Te-Z marked this conversation as resolved.
Show resolved Hide resolved
searchResultsNotifier.value = PresentationSearchPublicRoomEmpty();
} else if (success.publicRoomsChunk!.isEmpty) {
searchResultsNotifier.value = PresentationSearchPublicRoomEmpty();
} else {
searchResultsNotifier.value = PresentationSearchPublicRoom(
searchResults: success.publicRoomsChunk!,
);
}
}
});
}

void _handleKeywordIsRoomId() {
searchResultsNotifier.value = PresentationSearchPublicRoomEmpty();
}

void _viewRoom(BuildContext context, String roomId) {
context.go('/rooms/$roomId');
}

String? getServerName(String? roomIdOrAlias) {
if (roomIdOrAlias != null) {
return roomIdOrAlias.getServerNameFromRoomIdOrAlias();
}
return null;
}

void joinRoom(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO: should use Future<void>

BuildContext context,
String roomIdOrAlias,
String? server,
) async {
final client = Matrix.of(context).client;
final result = await TwakeDialog.showFutureLoadingDialogFullScreen(
future: () => client.joinRoom(
roomIdOrAlias,
serverName: server != null ? [server] : null,
),
);
if (result.error == null) {
if (client.getRoomById(result.result!) == null) {
await client.onSync.stream.firstWhere(
(sync) => sync.rooms?.join?.containsKey(result.result) ?? false,
);
sherlockvn marked this conversation as resolved.
Show resolved Hide resolved
}
if (!client.getRoomById(result.result!)!.isSpace) {
context.go('/rooms/${result.result!}');
}
return;
}
Te-Z marked this conversation as resolved.
Show resolved Hide resolved
}

void handlePublicRoomActions(
BuildContext context,
PublicRoomsChunk room,
PublicRoomActions action,
) {
switch (action) {
case PublicRoomActions.join:
joinRoom(
context,
room.roomId,
getServerName(room.roomId),
);
break;
case PublicRoomActions.view:
_viewRoom(context, room.roomId);
break;
}
}

PublicRoomActions? getAction(
BuildContext context,
PublicRoomsChunk room,
) {
final client = Matrix.of(context).client;
if (client.getRoomById(room.roomId) != null) {
return PublicRoomActions.view;
} else if (room.joinRule == 'public') {
sherlockvn marked this conversation as resolved.
Show resolved Hide resolved
return PublicRoomActions.join;
}
return null;
Te-Z marked this conversation as resolved.
Show resolved Hide resolved
}

void onSearchBarChanged(String keyword) {
if (keyword.isRoomAlias() || keyword.isRoomId()) {
setDebouncerValue(keyword);
} else {
setDebouncerValue('');
_resetSearchResults();
}
}

void dispose() {
super.disposeDebouncer();
searchResultsNotifier.dispose();
_resetSearchResults();
disposeDebouncer();
Te-Z marked this conversation as resolved.
Show resolved Hide resolved
_filter = null;
}
}
Loading