Skip to content

Commit ffe5419

Browse files
committed
init related rooms
1 parent 8a6d364 commit ffe5419

File tree

7 files changed

+437
-1
lines changed

7 files changed

+437
-1
lines changed

lib/app/app.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:find_room/dependency_injection.dart';
99
import 'package:find_room/generated/i18n.dart';
1010
import 'package:find_room/pages/detail/comments/comments_tab_bloc.dart';
1111
import 'package:find_room/pages/detail/detail/room_detail_tab_bloc.dart';
12+
import 'package:find_room/pages/detail/related/related_rooms_tab_bloc.dart';
1213
import 'package:find_room/pages/detail/room_detail_bloc.dart';
1314
import 'package:find_room/pages/detail/room_detail_page.dart';
1415
import 'package:find_room/pages/home/home_bloc.dart';
@@ -233,7 +234,13 @@ class MyApp extends StatelessWidget {
233234
authBloc: authBloc,
234235
)..getComments.call();
235236
},
236-
child: RoomDetailPage(),
237+
child: BlocProvider<RelatedRoomsTabBloc>(
238+
child: RoomDetailPage(),
239+
blocSupplier: () {
240+
return RelatedRoomsTabBloc(injector.roomRepository)
241+
..fetch.call();
242+
},
243+
),
237244
),
238245
),
239246
);

lib/data/rooms/firestore_room_repository.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ abstract class FirestoreRoomRepository {
3333
String roomId, {
3434
Duration timeout = const Duration(seconds: 10),
3535
});
36+
37+
Future<List<RoomEntity>> getRelatedRooms();
3638
}

lib/data/rooms/firestore_room_repository_impl.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,9 @@ class FirestoreRoomRepositoryImpl implements FirestoreRoomRepository {
195195

196196
return _firestore.runTransaction(transactionHandler, timeout: timeout);
197197
}
198+
199+
@override
200+
Future<List<RoomEntity>> getRelatedRooms() async {
201+
return [];
202+
}
198203
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import 'dart:async';
2+
3+
import 'package:built_collection/built_collection.dart';
4+
import 'package:disposebag/disposebag.dart';
5+
import 'package:distinct_value_connectable_stream/distinct_value_connectable_stream.dart';
6+
import 'package:find_room/bloc/bloc_provider.dart';
7+
import 'package:find_room/data/rooms/firestore_room_repository.dart';
8+
import 'package:find_room/models/room_entity.dart';
9+
import 'package:find_room/pages/detail/related/related_rooms_tab_state.dart';
10+
import 'package:flutter/foundation.dart';
11+
import 'package:rxdart/rxdart.dart';
12+
13+
class RelatedRoomsTabBloc implements BaseBloc {
14+
/// Output
15+
final ValueStream<RelatedRoomsState> state$;
16+
final Stream<Message> message$;
17+
18+
/// Input
19+
final void Function() fetch;
20+
final Future<void> Function() refresh;
21+
22+
/// Dispose
23+
final void Function() _dispose;
24+
25+
@override
26+
void dispose() => _dispose();
27+
28+
RelatedRoomsTabBloc._(
29+
this._dispose, {
30+
@required this.state$,
31+
@required this.fetch,
32+
@required this.refresh,
33+
@required this.message$,
34+
});
35+
36+
factory RelatedRoomsTabBloc(FirestoreRoomRepository roomsRepo) {
37+
// ignore_for_file: close_sinks
38+
39+
///
40+
/// Subjects
41+
///
42+
final fetchSubject = PublishSubject<void>();
43+
final refreshSubject = PublishSubject<Completer<void>>();
44+
final messageSubject = PublishSubject<Message>();
45+
46+
///
47+
/// Input actions to state
48+
///
49+
final fetchChanges = fetchSubject.exhaustMap(
50+
(_) {
51+
return Rx.defer(() => Stream.fromFuture(roomsRepo.getRelatedRooms()))
52+
.map(_toItem)
53+
.map<PartialStateChange>((items) => GetUsersSuccessChange(items))
54+
.startWith(const LoadingChange())
55+
.doOnError((e, s) => messageSubject.add(GetRoomsErrorMessage(e)))
56+
.onErrorReturnWith((e) => GetUsersErrorChange(e));
57+
},
58+
);
59+
final refreshChanges = refreshSubject
60+
.throttleTime(const Duration(milliseconds: 600))
61+
.exhaustMap(
62+
(completer) {
63+
return Rx.defer(() => Stream.fromFuture(roomsRepo.getRelatedRooms()))
64+
.map(_toItem)
65+
.map<PartialStateChange>((items) => GetUsersSuccessChange(items))
66+
.doOnError((e, s) => messageSubject.add(RefreshFailureMessage(e)))
67+
.doOnData((_) => messageSubject.add(const RefreshSuccessMessage()))
68+
.onErrorResumeNext(Stream.empty())
69+
.doOnDone(() => completer.complete());
70+
},
71+
);
72+
73+
final initialState = RelatedRoomsState.initial();
74+
final state$ = Rx.merge([fetchChanges, refreshChanges])
75+
.scan(_reduce, initialState)
76+
.publishValueSeededDistinct(seedValue: initialState);
77+
78+
return RelatedRoomsTabBloc._(
79+
DisposeBag([
80+
//subscriptions
81+
state$.listen((state) => print('[HOME_BLOC] state=$state')),
82+
messageSubject
83+
.listen((message) => print('[HOME_BLOC] message=$message')),
84+
state$.connect(),
85+
//controllers
86+
fetchSubject,
87+
refreshSubject,
88+
messageSubject,
89+
]).dispose,
90+
state$: state$,
91+
fetch: () => fetchSubject.add(null),
92+
refresh: () {
93+
final completer = Completer<void>();
94+
refreshSubject.add(completer);
95+
return completer.future;
96+
},
97+
message$: messageSubject,
98+
);
99+
}
100+
101+
///
102+
/// Reduce
103+
///
104+
static RelatedRoomsState _reduce(
105+
RelatedRoomsState state,
106+
PartialStateChange change,
107+
int _,
108+
) {
109+
if (change is LoadingChange) {
110+
return state.rebuild((b) => b.isLoading = true);
111+
}
112+
if (change is GetUsersErrorChange) {
113+
return state.rebuild(
114+
(b) => b
115+
..isLoading = false
116+
..error = change.error,
117+
);
118+
}
119+
if (change is GetUsersSuccessChange) {
120+
return state.rebuild(
121+
(b) => b
122+
..isLoading = false
123+
..error = null
124+
..items = ListBuilder<RoomItem>(change.items),
125+
);
126+
}
127+
return state;
128+
}
129+
130+
static List<RoomItem> _toItem(List<RoomEntity> entities) {
131+
return entities
132+
.map((e) => RoomItem((b) => b..id = e.id))
133+
.toList(growable: false);
134+
}
135+
}

lib/pages/detail/related/related_rooms_tab_page.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:find_room/bloc/bloc_provider.dart';
2+
import 'package:find_room/pages/detail/related/related_rooms_tab_bloc.dart';
13
import 'package:flutter/material.dart';
24

35
class RelatedRoomsTabPage extends StatefulWidget {
@@ -8,6 +10,12 @@ class RelatedRoomsTabPage extends StatefulWidget {
810
}
911

1012
class _RelatedRoomsTabPageState extends State<RelatedRoomsTabPage> {
13+
@override
14+
void didChangeDependencies() {
15+
super.didChangeDependencies();
16+
print(BlocProvider.of<RelatedRoomsTabBloc>(context));
17+
}
18+
1119
@override
1220
Widget build(BuildContext context) {
1321
return Container(
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import 'package:built_collection/built_collection.dart';
2+
import 'package:built_value/built_value.dart';
3+
4+
part 'related_rooms_tab_state.g.dart';
5+
6+
abstract class RelatedRoomsState
7+
implements Built<RelatedRoomsState, RelatedRoomsStateBuilder> {
8+
bool get isLoading;
9+
10+
BuiltList<RoomItem> get items;
11+
12+
@nullable
13+
Object get error;
14+
15+
RelatedRoomsState._();
16+
17+
factory RelatedRoomsState([updates(RelatedRoomsStateBuilder b)]) =
18+
_$RelatedRoomsState;
19+
20+
factory RelatedRoomsState.initial() => RelatedRoomsState(
21+
(b) => b
22+
..isLoading = true
23+
..items = ListBuilder<RoomItem>()
24+
..error = null,
25+
);
26+
}
27+
28+
abstract class RoomItem implements Built<RoomItem, RoomItemBuilder> {
29+
String get id;
30+
31+
RoomItem._();
32+
33+
factory RoomItem([updates(RoomItemBuilder b)]) = _$RoomItem;
34+
}
35+
36+
///
37+
/// Partial change
38+
///
39+
abstract class PartialStateChange {}
40+
41+
class GetUsersSuccessChange implements PartialStateChange {
42+
final List<RoomItem> items;
43+
44+
GetUsersSuccessChange(this.items);
45+
}
46+
47+
class LoadingChange implements PartialStateChange {
48+
const LoadingChange();
49+
}
50+
51+
class GetUsersErrorChange implements PartialStateChange {
52+
final error;
53+
54+
const GetUsersErrorChange(this.error);
55+
}
56+
57+
///
58+
/// Single event message
59+
///
60+
abstract class Message {}
61+
62+
class RefreshSuccessMessage implements Message {
63+
const RefreshSuccessMessage();
64+
}
65+
66+
class RefreshFailureMessage implements Message {
67+
final error;
68+
69+
const RefreshFailureMessage(this.error);
70+
}
71+
72+
class GetRoomsErrorMessage implements Message {
73+
final error;
74+
75+
GetRoomsErrorMessage(this.error);
76+
}

0 commit comments

Comments
 (0)