diff --git a/dudu-tweet/dudu-tweet.xcodeproj/project.pbxproj b/dudu-tweet/dudu-tweet.xcodeproj/project.pbxproj index f381ec2..942bffe 100644 --- a/dudu-tweet/dudu-tweet.xcodeproj/project.pbxproj +++ b/dudu-tweet/dudu-tweet.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 24A07CB62A020FF900F4ECA8 /* UploadTweetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CB52A020FF900F4ECA8 /* UploadTweetViewModel.swift */; }; 24A07CB82A02173400F4ECA8 /* FeedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CB72A02173400F4ECA8 /* FeedViewModel.swift */; }; 24A07CBA2A02186500F4ECA8 /* Tweet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CB92A02186500F4ECA8 /* Tweet.swift */; }; + 24A07CBC2A022B2000F4ECA8 /* ProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CBB2A022B2000F4ECA8 /* ProfileViewModel.swift */; }; 24A59AB42A002EB8009C9E3E /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A59AB32A002EB8009C9E3E /* MainTabView.swift */; }; 24A59ABA2A0030CB009C9E3E /* ExploreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A59AB92A0030CB009C9E3E /* ExploreView.swift */; }; 24A59ABC2A0030EC009C9E3E /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A59ABB2A0030EC009C9E3E /* NotificationsView.swift */; }; @@ -66,6 +67,7 @@ 24A07CB52A020FF900F4ECA8 /* UploadTweetViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadTweetViewModel.swift; sourceTree = ""; }; 24A07CB72A02173400F4ECA8 /* FeedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedViewModel.swift; sourceTree = ""; }; 24A07CB92A02186500F4ECA8 /* Tweet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tweet.swift; sourceTree = ""; }; + 24A07CBB2A022B2000F4ECA8 /* ProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewModel.swift; sourceTree = ""; }; 24A59AB32A002EB8009C9E3E /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = ""; }; 24A59AB92A0030CB009C9E3E /* ExploreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreView.swift; sourceTree = ""; }; 24A59ABB2A0030EC009C9E3E /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = ""; }; @@ -295,6 +297,7 @@ isa = PBXGroup; children = ( 24A59AC32A003A52009C9E3E /* TwewtFilterViewModel.swift */, + 24A07CBB2A022B2000F4ECA8 /* ProfileViewModel.swift */, ); path = ViewModels; sourceTree = ""; @@ -534,6 +537,7 @@ 24A59AB42A002EB8009C9E3E /* MainTabView.swift in Sources */, 24A59ABC2A0030EC009C9E3E /* NotificationsView.swift in Sources */, 24A59AD42A00C07D009C9E3E /* UserStatsView.swift in Sources */, + 24A07CBC2A022B2000F4ECA8 /* ProfileViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/dudu-tweet/dudu-tweet.xcodeproj/project.xcworkspace/xcuserdata/ching.xcuserdatad/UserInterfaceState.xcuserstate b/dudu-tweet/dudu-tweet.xcodeproj/project.xcworkspace/xcuserdata/ching.xcuserdatad/UserInterfaceState.xcuserstate index 176d7e4..d69d3a0 100644 Binary files a/dudu-tweet/dudu-tweet.xcodeproj/project.xcworkspace/xcuserdata/ching.xcuserdatad/UserInterfaceState.xcuserstate and b/dudu-tweet/dudu-tweet.xcodeproj/project.xcworkspace/xcuserdata/ching.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/dudu-tweet/dudu-tweet/ContentView.swift b/dudu-tweet/dudu-tweet/ContentView.swift index e804562..e4665b1 100644 --- a/dudu-tweet/dudu-tweet/ContentView.swift +++ b/dudu-tweet/dudu-tweet/ContentView.swift @@ -37,7 +37,6 @@ extension ContentView { ZStack(alignment: .topLeading) { MainTabView() .toolbar(showMenu ? .hidden : .visible) - if showMenu { ZStack { Color(.black) @@ -55,8 +54,6 @@ extension ContentView { .offset(x: showMenu ? 0: -300, y: 0) .background(showMenu ? Color.white: Color.clear) } - .navigationTitle("Home") - .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { if let user = viewModel.currentUser { diff --git a/dudu-tweet/dudu-tweet/Core/Authentication/ViewModels/AuthViewModel.swift b/dudu-tweet/dudu-tweet/Core/Authentication/ViewModels/AuthViewModel.swift index d58e9e4..a9ed8ce 100644 --- a/dudu-tweet/dudu-tweet/Core/Authentication/ViewModels/AuthViewModel.swift +++ b/dudu-tweet/dudu-tweet/Core/Authentication/ViewModels/AuthViewModel.swift @@ -19,7 +19,6 @@ class AuthViewModel: ObservableObject { init() { self.userSession = Auth.auth().currentUser self.fetchUser() - print("DEBUG: user session is \(self.userSession?.uid)") } func login(withEmail email: String, password: String) { diff --git a/dudu-tweet/dudu-tweet/Core/Explore/Views/ExploreView.swift b/dudu-tweet/dudu-tweet/Core/Explore/Views/ExploreView.swift index 89634d8..1b3a2d7 100644 --- a/dudu-tweet/dudu-tweet/Core/Explore/Views/ExploreView.swift +++ b/dudu-tweet/dudu-tweet/Core/Explore/Views/ExploreView.swift @@ -9,7 +9,7 @@ import SwiftUI struct ExploreView: View { @ObservedObject var viewModel = ExploreViewModel() - + var body: some View { VStack { SearchBar(text: $viewModel.searchText) @@ -18,17 +18,14 @@ struct ExploreView: View { LazyVStack { ForEach(viewModel.searchableUsers) { user in NavigationLink { - ProfileView(user: user) + ProfileView(user: user) } label: { UserRowView(user: user) } - } } } } - .navigationTitle("Explore") - .navigationBarTitleDisplayMode(.inline) } } diff --git a/dudu-tweet/dudu-tweet/Core/Feed/View/FeedView.swift b/dudu-tweet/dudu-tweet/Core/Feed/View/FeedView.swift index bc27e55..261dd6e 100644 --- a/dudu-tweet/dudu-tweet/Core/Feed/View/FeedView.swift +++ b/dudu-tweet/dudu-tweet/Core/Feed/View/FeedView.swift @@ -10,7 +10,7 @@ import SwiftUI struct FeedView: View { @State private var showNewTweetView = false @ObservedObject var viewModel = FeedViewModel() - + var body: some View { ZStack(alignment: .bottomTrailing) { ScrollView { @@ -20,7 +20,7 @@ struct FeedView: View { } } } - + Button { showNewTweetView.toggle() } label: { @@ -37,7 +37,6 @@ struct FeedView: View { .fullScreenCover(isPresented: $showNewTweetView) { NewTweetView() } - } } } diff --git a/dudu-tweet/dudu-tweet/Core/MainTab/MainTabView.swift b/dudu-tweet/dudu-tweet/Core/MainTab/MainTabView.swift index f74f9a7..3668a46 100644 --- a/dudu-tweet/dudu-tweet/Core/MainTab/MainTabView.swift +++ b/dudu-tweet/dudu-tweet/Core/MainTab/MainTabView.swift @@ -9,37 +9,56 @@ import SwiftUI struct MainTabView: View { @State private var selectedIndex: Int = 0 - + var body: some View { - TabView(selection: $selectedIndex) { - FeedView() - .onTapGesture { - self.selectedIndex = 0 - } - .tabItem { - Image(systemName: "house") - }.tag(0) - ExploreView() - .onTapGesture { - self.selectedIndex = 1 - } - .tabItem { - Image(systemName: "magnifyingglass") - }.tag(1) - NotificationsView() - .onTapGesture { - self.selectedIndex = 2 - } - .tabItem { - Image(systemName: "bell") - }.tag(2) - MessagesView() - .onTapGesture { - self.selectedIndex = 3 - } - .tabItem { - Image(systemName: "envelope") - }.tag(3) + NavigationView { + TabView(selection: $selectedIndex) { + FeedView() + .onTapGesture { + self.selectedIndex = 0 + } + .tabItem { + Image(systemName: "house") + }.tag(0) + ExploreView() + .onTapGesture { + self.selectedIndex = 1 + } + .tabItem { + Image(systemName: "magnifyingglass") + }.tag(1) + NotificationsView() + .onTapGesture { + self.selectedIndex = 2 + } + .tabItem { + Image(systemName: "bell") + }.tag(2) + MessagesView() + .onTapGesture { + self.selectedIndex = 3 + } + .tabItem { + Image(systemName: "envelope") + }.tag(3) + } + } + .navigationTitle(title(for: selectedIndex)) + .navigationBarTitleDisplayMode(.inline) + } + + func title(for tab: Int) -> String { + switch tab { + case 0: + return "Home" + case 1: + return "Explore" + case 2: + return "Notification" + case 3: + return "Message" + default: + return "" } } } diff --git a/dudu-tweet/dudu-tweet/Core/Profile/ViewModels/ProfileViewModel.swift b/dudu-tweet/dudu-tweet/Core/Profile/ViewModels/ProfileViewModel.swift new file mode 100644 index 0000000..36fc673 --- /dev/null +++ b/dudu-tweet/dudu-tweet/Core/Profile/ViewModels/ProfileViewModel.swift @@ -0,0 +1,30 @@ +// +// ProfileViewModel.swift +// dudu-tweet +// +// Created by ching on 2023/5/3. +// + +import Foundation + + +class ProfileViewModel: ObservableObject { + @Published var tweets = [Tweet]() + private let service = TweetService() + let user: User + + init(user: User) { + self.user = user + self.fetchUserTweet() + } + + func fetchUserTweet() { + guard let uid = user.id else { return } + service.fetchTweets(forUid: uid) { tweets in + self.tweets = tweets + for i in 0 ..< tweets.count { + self.tweets[i].user = self.user + } + } + } +} diff --git a/dudu-tweet/dudu-tweet/Core/Profile/Views/ProfileView.swift b/dudu-tweet/dudu-tweet/Core/Profile/Views/ProfileView.swift index ad61344..3b4b728 100644 --- a/dudu-tweet/dudu-tweet/Core/Profile/Views/ProfileView.swift +++ b/dudu-tweet/dudu-tweet/Core/Profile/Views/ProfileView.swift @@ -12,12 +12,12 @@ struct ProfileView: View { @State private var selectedFilter: TweetFilterViewModel = .tweets // @Environment(\.presentationMode) var mode // 用来从explore 页面跳转过来之后跳转回去 @Environment(\.dismiss) private var dismiss - private let user: User + @ObservedObject var viewModel: ProfileViewModel @Namespace var animation init(user: User) { - self.user = user + self.viewModel = ProfileViewModel(user: user) } var body: some View { @@ -65,7 +65,7 @@ extension ProfileView { .offset(x: 16, y: -4) } - KFImage(URL(string: user.profileImageUrl)) + KFImage(URL(string: viewModel.user.profileImageUrl)) .resizable() .scaledToFill() .clipShape(Circle()) @@ -100,12 +100,12 @@ extension ProfileView { var userInfoDetails: some View { VStack(alignment: .leading, spacing: 4) { HStack { - Text(user.fullname) + Text(viewModel.user.fullname) .font(.title2).bold() Image(systemName: "checkmark.seal.fill") .foregroundColor(Color(.systemBlue)) } - Text("@\(user.username)") + Text("@\(viewModel.user.username)") .font(.subheadline) .foregroundColor(.gray) Text("一个普通人") @@ -165,8 +165,8 @@ extension ProfileView { var tweetsView: some View { ScrollView { LazyVStack { - ForEach(0...9, id: \.self) { _ in -// TweetRowView() + ForEach(viewModel.tweets) { tweet in + TweetRowView(tweet: tweet) } } } diff --git a/dudu-tweet/dudu-tweet/Service/TweetService.swift b/dudu-tweet/dudu-tweet/Service/TweetService.swift index 11065cf..94ecd87 100644 --- a/dudu-tweet/dudu-tweet/Service/TweetService.swift +++ b/dudu-tweet/dudu-tweet/Service/TweetService.swift @@ -30,12 +30,25 @@ struct TweetService { func fetchTweets(completion: @escaping([Tweet]) -> Void) { - Firestore.firestore().collection("tweets").getDocuments { snapshot, _ in + Firestore.firestore().collection("tweets") + .order(by: "timestamp", descending: true) + .getDocuments { snapshot, _ in guard let documents = snapshot?.documents else { return } let tweets = documents.compactMap({ try? $0.data(as: Tweet.self) }) completion(tweets) } } + + func fetchTweets(forUid uid: String, completion: @escaping([Tweet]) -> Void) { + Firestore.firestore().collection("tweets") + .whereField("uid", isEqualTo: uid) + .getDocuments { snapshot, _ in + guard let documents = snapshot?.documents else { return } + + let tweets = documents.compactMap({ try? $0.data(as: Tweet.self) }) + completion(tweets.sorted(by: { $0.timestamp.dateValue() > $1.timestamp.dateValue()})) + } + } }