Skip to content

Commit 8e49054

Browse files
committed
✨ Implement watch list
Signed-off-by: Peter Friese <[email protected]>
1 parent 4c8b01a commit 8e49054

File tree

12 files changed

+419
-26
lines changed

12 files changed

+419
-26
lines changed

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/App/FriendlyFlixApp.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
import SwiftUI
2020
import Firebase
21+
import FirebaseAuth
22+
import FirebaseDataConnect
23+
import FriendlyFlixSDK
2124

2225
@main
2326
struct FriendlyFlixApp: App {
@@ -31,15 +34,26 @@ struct FriendlyFlixApp: App {
3134
#endif
3235
}
3336

34-
init () {
37+
var authenticationService: AuthenticationService?
38+
39+
init() {
3540
loadRocketSimConnect()
3641
FirebaseApp.configure()
42+
43+
authenticationService = AuthenticationService()
44+
authenticationService?.onSignUp { user in
45+
print("User signed in \(user.displayName ?? "(no fullname)") with email \(user.email ?? "(no email)")")
46+
let userName = String(user.email?.split(separator: "@").first ?? "(unknown)")
47+
Task {
48+
try await DataConnect.friendlyFlixConnector.upsertUserMutation.execute(username: userName)
49+
}
50+
}
3751
}
3852

3953
var body: some Scene {
4054
WindowGroup {
4155
RootView()
42-
.environment(AuthenticationService())
56+
.environment(authenticationService)
4357
}
4458
}
4559
}

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/Authentication/AuthenticationService.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,23 @@ class AuthenticationService {
4444
}
4545
}
4646

47+
private var onSignUp: ((User) -> Void)?
48+
public func onSignUp(_ action: @escaping (User) -> Void) {
49+
onSignUp = action
50+
}
51+
4752
func signInWithEmailPassword(email: String, password: String) async throws {
4853
try await Auth.auth().signIn(withEmail: email, password: password)
4954
authenticationState = .authenticated
5055
}
5156

5257
func signUpWithEmailPassword(email: String, password: String) async throws {
5358
try await Auth.auth().createUser(withEmail: email, password: password)
59+
60+
if let onSignUp, let user = Auth.auth().currentUser {
61+
onSignUp(user)
62+
}
63+
5464
authenticationState = .authenticated
5565
}
5666

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/Details/MovieCardView.swift

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,43 @@
1919

2020
import SwiftUI
2121
import NukeUI
22+
import FirebaseDataConnect
23+
import FriendlyFlixSDK
2224

2325
struct MovieCardView: View {
24-
var showDetails: Bool = false
26+
public var showDetails: Bool = false
27+
public var movie: Movie
2528

26-
var movie: Movie
29+
private var connector = DataConnect.friendlyFlixConnector
2730

31+
public init(showDetails: Bool, movie: Movie) {
32+
self.showDetails = showDetails
33+
self.movie = movie
34+
35+
isFavouriteRef = connector.getIfFavoritedMovieQuery.ref(movieId: movie.id)
36+
}
37+
38+
private let isFavouriteRef: QueryRefObservation<GetIfFavoritedMovieQuery.Data, GetIfFavoritedMovieQuery.Variables>
39+
private var isFavourite: Bool {
40+
isFavouriteRef.data?.favorite_movie?.movieId != nil
41+
}
42+
43+
func toggleFavourite() {
44+
Task {
45+
if isFavourite {
46+
let _ = try await connector.deleteFavoritedMovieMutation.execute(movieId: movie.id)
47+
let _ = try await isFavouriteRef.execute()
48+
}
49+
else {
50+
let _ = try await connector.addFavoritedMovieMutation.execute(movieId: movie.id)
51+
let _ = try await isFavouriteRef.execute()
52+
}
53+
}
54+
}
55+
56+
}
57+
58+
extension MovieCardView {
2859
var body: some View {
2960
CardView(showDetails: showDetails) {
3061
if let imageUrl = URL(string: movie.imageUrl) {
@@ -71,8 +102,25 @@ struct MovieCardView: View {
71102
}
72103
} details: {
73104
Text(movie.title)
105+
Button {
106+
toggleFavourite()
107+
} label: {
108+
Label(
109+
isFavourite ? "Remove from watch list" : "Add to watch list",
110+
systemImage: isFavourite ? "heart.slash" :"heart"
111+
)
112+
}
113+
74114
// MovieDetailsView(movie: movie)
75115
}
116+
.task {
117+
do {
118+
let _ = try await isFavouriteRef.execute()
119+
}
120+
catch {
121+
print(error)
122+
}
123+
}
76124
}
77125
}
78126

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/Home/HomeScreen.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ struct HomeScreen: View {
5454
.data?.movies.map(Movie.init) ?? []
5555
}
5656

57+
let watchListRef: QueryRefObservation<GetUserFavoriteMoviesQuery.Data, GetUserFavoriteMoviesQuery.Variables>
5758
private var watchList: [Movie] {
58-
/// TODO: build a query that retrieves the user's watch list
59-
connector.listMoviesQuery.ref().data?.movies.map(Movie.init) ?? []
59+
watchListRef.data?.user?.favoriteMovies.map(Movie.init) ?? []
6060
}
6161

6262
private var featuredMovies: [Movie] {
@@ -68,6 +68,10 @@ struct HomeScreen: View {
6868
})
6969
.data?.movies.map(Movie.init) ?? []
7070
}
71+
72+
init() {
73+
watchListRef = connector.getUserFavoriteMoviesQuery.ref()
74+
}
7175
}
7276

7377
extension HomeScreen {
@@ -93,6 +97,11 @@ extension HomeScreen {
9397
Group {
9498
MovieListSection(namespace: namespace, title: "Top Movies", movies: topMovies)
9599
MovieListSection(namespace: namespace, title: "Watch List", movies: watchList)
100+
.onAppear {
101+
Task {
102+
try await watchListRef.execute()
103+
}
104+
}
96105
MovieListSection(namespace: namespace, title: "Featured", movies: featuredMovies)
97106
}
98107
.navigationDestination(for: [Movie].self) { movies in

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/Library/LibraryScreen.swift

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,39 @@
1717
// limitations under the License.
1818

1919
import SwiftUI
20+
import FirebaseDataConnect
21+
import FriendlyFlixSDK
2022

2123
struct LibraryScreen: View {
2224
@Namespace var namespace
2325
@Environment(AuthenticationService.self) var authenticationViewModel
2426

27+
private var connector = DataConnect.friendlyFlixConnector
28+
29+
init() {
30+
watchListRef = connector.getUserFavoriteMoviesQuery.ref()
31+
}
32+
33+
private let watchListRef: QueryRefObservation<GetUserFavoriteMoviesQuery.Data, GetUserFavoriteMoviesQuery.Variables>
34+
private var watchList: [Movie] {
35+
watchListRef.data?.user?.favoriteMovies.map(Movie.init) ?? []
36+
}
37+
}
38+
39+
extension LibraryScreen {
2540
var body: some View {
26-
@Bindable var authenticationViewModel = authenticationViewModel
2741
NavigationStack {
2842
ScrollView {
2943
if authenticationViewModel.authenticationState == .authenticated {
3044
Group {
31-
MovieListSection(namespace: namespace, title: "Watch List", movies: Movie.watchList)
32-
MovieListSection(namespace: namespace, title: "Favourites", movies: Movie.featured)
45+
MovieListSection(namespace: namespace, title: "Watch List", movies: watchList)
46+
.onAppear {
47+
Task {
48+
try await watchListRef.execute()
49+
}
50+
}
51+
52+
// TODO: insert section with list of all movies the user has rated
3353
}
3454
.padding()
3555
}

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/Search/SearchScreen.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ struct SearchScreen: View {
6464

6565
private var filteredMovies: [Movie] {
6666
connector.listMoviesByPartialTitleQuery
67-
.ref(searchTerm: searchText)
67+
.ref(searchTerm: searchText.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines))
6868
.data?.movies.map(Movie.init) ?? []
6969
}
7070

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Model/Movie+DataConnect.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import FriendlyFlixSDK
1717

1818
extension Movie {
1919
init(from: ListMoviesQuery.Data.Movie) {
20+
self.id = from.id
2021
self.title = from.title
2122
self.description = from.description ?? ""
2223
self.releaseYear = from.releaseYear
@@ -25,11 +26,21 @@ extension Movie {
2526
}
2627

2728
init(from: ListMoviesByPartialTitleQuery.Data.Movie) {
29+
self.id = from.id
2830
self.title = from.title
2931
self.description = from.description ?? ""
3032
self.releaseYear = from.releaseYear
3133
self.rating = from.rating
3234
self.imageUrl = from.imageUrl
3335
}
36+
37+
init(from: GetUserFavoriteMoviesQuery.Data.User.FavoriteMovieFavoriteMovies) {
38+
self.id = from.movie.id
39+
self.title = from.movie.title
40+
self.description = from.movie.description ?? ""
41+
self.releaseYear = from.movie.releaseYear
42+
self.rating = from.movie.rating
43+
self.imageUrl = from.movie.imageUrl
44+
}
3445
}
3546

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Model/Movie.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,28 @@ import Foundation
2020
import SwiftUI
2121

2222
struct Movie: Identifiable, Hashable {
23-
let id = UUID()
23+
let id: UUID
2424
let title: String
2525
let description: String
2626
let releaseYear: Int?
2727
var rating: Double?
2828
let imageUrl: String
29+
30+
init(
31+
id: UUID = UUID(),
32+
title: String,
33+
description: String,
34+
releaseYear: Int?,
35+
rating: Double? = nil,
36+
imageUrl: String
37+
) {
38+
self.id = id
39+
self.title = title
40+
self.description = description
41+
self.releaseYear = releaseYear
42+
self.rating = rating
43+
self.imageUrl = imageUrl
44+
}
2945
}
3046

3147
extension Movie: Mockable {

Examples/FriendlyFlix/app/FriendlyFlixSDK/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,39 @@ DataConnect.friendlyFlixConnector.listMoviesByPartialTitleQuery.execute(...)
323323
```
324324

325325

326+
## GetUserFavoriteMoviesQuery
327+
328+
329+
### Using the Query Reference
330+
```
331+
struct MyView: View {
332+
var getUserFavoriteMoviesQueryRef = DataConnect.friendlyFlixConnector.getUserFavoriteMoviesQuery.ref(...)
333+
334+
var body: some View {
335+
VStack {
336+
if let data = getUserFavoriteMoviesQueryRef.data {
337+
// use data in View
338+
}
339+
else {
340+
Text("Loading...")
341+
}
342+
}
343+
.task {
344+
do {
345+
let _ = try await getUserFavoriteMoviesQueryRef.execute()
346+
} catch {
347+
}
348+
}
349+
}
350+
}
351+
```
352+
353+
### One-shot execute
354+
```
355+
DataConnect.friendlyFlixConnector.getUserFavoriteMoviesQuery.execute(...)
356+
```
357+
358+
326359
# Mutations
327360
## UpsertUserMutation
328361

Examples/FriendlyFlix/app/FriendlyFlixSDK/Sources/FriendlyFlixClient.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public class FriendlyFlixConnector {
4343
self.getIfFavoritedMovieQuery = GetIfFavoritedMovieQuery(dataConnect: dataConnect)
4444
self.searchAllQuery = SearchAllQuery(dataConnect: dataConnect)
4545
self.listMoviesByPartialTitleQuery = ListMoviesByPartialTitleQuery(dataConnect: dataConnect)
46+
self.getUserFavoriteMoviesQuery = GetUserFavoriteMoviesQuery(dataConnect: dataConnect)
4647

4748
}
4849

@@ -64,6 +65,7 @@ public let getCurrentUserQuery: GetCurrentUserQuery
6465
public let getIfFavoritedMovieQuery: GetIfFavoritedMovieQuery
6566
public let searchAllQuery: SearchAllQuery
6667
public let listMoviesByPartialTitleQuery: ListMoviesByPartialTitleQuery
68+
public let getUserFavoriteMoviesQuery: GetUserFavoriteMoviesQuery
6769

6870

6971
}

0 commit comments

Comments
 (0)