Compare commits
No commits in common. "d34651e3064bcfbb04981041b09e8a80d510d4c6" and "9d237961b694196669ab3006b2f706d6fc5e16dc" have entirely different histories.
d34651e306
...
9d237961b6
@ -19,8 +19,6 @@
|
|||||||
24A07CB62A020FF900F4ECA8 /* UploadTweetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CB52A020FF900F4ECA8 /* UploadTweetViewModel.swift */; };
|
24A07CB62A020FF900F4ECA8 /* UploadTweetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CB52A020FF900F4ECA8 /* UploadTweetViewModel.swift */; };
|
||||||
24A07CB82A02173400F4ECA8 /* FeedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CB72A02173400F4ECA8 /* FeedViewModel.swift */; };
|
24A07CB82A02173400F4ECA8 /* FeedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CB72A02173400F4ECA8 /* FeedViewModel.swift */; };
|
||||||
24A07CBA2A02186500F4ECA8 /* Tweet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CB92A02186500F4ECA8 /* Tweet.swift */; };
|
24A07CBA2A02186500F4ECA8 /* Tweet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CB92A02186500F4ECA8 /* Tweet.swift */; };
|
||||||
24A07CBC2A022B2000F4ECA8 /* ProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CBB2A022B2000F4ECA8 /* ProfileViewModel.swift */; };
|
|
||||||
24A07CC22A02302700F4ECA8 /* TweetRowViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A07CC12A02302700F4ECA8 /* TweetRowViewModel.swift */; };
|
|
||||||
24A59AB42A002EB8009C9E3E /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A59AB32A002EB8009C9E3E /* MainTabView.swift */; };
|
24A59AB42A002EB8009C9E3E /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A59AB32A002EB8009C9E3E /* MainTabView.swift */; };
|
||||||
24A59ABA2A0030CB009C9E3E /* ExploreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A59AB92A0030CB009C9E3E /* ExploreView.swift */; };
|
24A59ABA2A0030CB009C9E3E /* ExploreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A59AB92A0030CB009C9E3E /* ExploreView.swift */; };
|
||||||
24A59ABC2A0030EC009C9E3E /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A59ABB2A0030EC009C9E3E /* NotificationsView.swift */; };
|
24A59ABC2A0030EC009C9E3E /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A59ABB2A0030EC009C9E3E /* NotificationsView.swift */; };
|
||||||
@ -68,8 +66,6 @@
|
|||||||
24A07CB52A020FF900F4ECA8 /* UploadTweetViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadTweetViewModel.swift; sourceTree = "<group>"; };
|
24A07CB52A020FF900F4ECA8 /* UploadTweetViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadTweetViewModel.swift; sourceTree = "<group>"; };
|
||||||
24A07CB72A02173400F4ECA8 /* FeedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedViewModel.swift; sourceTree = "<group>"; };
|
24A07CB72A02173400F4ECA8 /* FeedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedViewModel.swift; sourceTree = "<group>"; };
|
||||||
24A07CB92A02186500F4ECA8 /* Tweet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tweet.swift; sourceTree = "<group>"; };
|
24A07CB92A02186500F4ECA8 /* Tweet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tweet.swift; sourceTree = "<group>"; };
|
||||||
24A07CBB2A022B2000F4ECA8 /* ProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
24A07CC12A02302700F4ECA8 /* TweetRowViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TweetRowViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
24A59AB32A002EB8009C9E3E /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
|
24A59AB32A002EB8009C9E3E /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
|
||||||
24A59AB92A0030CB009C9E3E /* ExploreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreView.swift; sourceTree = "<group>"; };
|
24A59AB92A0030CB009C9E3E /* ExploreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreView.swift; sourceTree = "<group>"; };
|
||||||
24A59ABB2A0030EC009C9E3E /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
|
24A59ABB2A0030EC009C9E3E /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
|
||||||
@ -238,28 +234,11 @@
|
|||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
2492CC262A0025A50086C525 /* Tweets */ = {
|
2492CC262A0025A50086C525 /* Tweets */ = {
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
24A07CBE2A022FCB00F4ECA8 /* ViewModels */,
|
|
||||||
24A07CBD2A022FC600F4ECA8 /* Views */,
|
|
||||||
);
|
|
||||||
path = Tweets;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
24A07CBD2A022FC600F4ECA8 /* Views */ = {
|
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
2492CC272A0025DD0086C525 /* TweetRowView.swift */,
|
2492CC272A0025DD0086C525 /* TweetRowView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Tweets;
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
24A07CBE2A022FCB00F4ECA8 /* ViewModels */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
24A07CC12A02302700F4ECA8 /* TweetRowViewModel.swift */,
|
|
||||||
);
|
|
||||||
path = ViewModels;
|
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
24A59AB22A002E79009C9E3E /* MainTab */ = {
|
24A59AB22A002E79009C9E3E /* MainTab */ = {
|
||||||
@ -316,7 +295,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
24A59AC32A003A52009C9E3E /* TwewtFilterViewModel.swift */,
|
24A59AC32A003A52009C9E3E /* TwewtFilterViewModel.swift */,
|
||||||
24A07CBB2A022B2000F4ECA8 /* ProfileViewModel.swift */,
|
|
||||||
);
|
);
|
||||||
path = ViewModels;
|
path = ViewModels;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -541,7 +519,6 @@
|
|||||||
24A59ABA2A0030CB009C9E3E /* ExploreView.swift in Sources */,
|
24A59ABA2A0030CB009C9E3E /* ExploreView.swift in Sources */,
|
||||||
24A07CB62A020FF900F4ECA8 /* UploadTweetViewModel.swift in Sources */,
|
24A07CB62A020FF900F4ECA8 /* UploadTweetViewModel.swift in Sources */,
|
||||||
24A07CB42A020ECB00F4ECA8 /* TweetService.swift in Sources */,
|
24A07CB42A020ECB00F4ECA8 /* TweetService.swift in Sources */,
|
||||||
24A07CC22A02302700F4ECA8 /* TweetRowViewModel.swift in Sources */,
|
|
||||||
24A59AD22A00BE14009C9E3E /* SideMenuViewModel.swift in Sources */,
|
24A59AD22A00BE14009C9E3E /* SideMenuViewModel.swift in Sources */,
|
||||||
24A59ADF2A00DCC2009C9E3E /* TextArea.swift in Sources */,
|
24A59ADF2A00DCC2009C9E3E /* TextArea.swift in Sources */,
|
||||||
24A07CB22A01869F00F4ECA8 /* SearchBar.swift in Sources */,
|
24A07CB22A01869F00F4ECA8 /* SearchBar.swift in Sources */,
|
||||||
@ -557,7 +534,6 @@
|
|||||||
24A59AB42A002EB8009C9E3E /* MainTabView.swift in Sources */,
|
24A59AB42A002EB8009C9E3E /* MainTabView.swift in Sources */,
|
||||||
24A59ABC2A0030EC009C9E3E /* NotificationsView.swift in Sources */,
|
24A59ABC2A0030EC009C9E3E /* NotificationsView.swift in Sources */,
|
||||||
24A59AD42A00C07D009C9E3E /* UserStatsView.swift in Sources */,
|
24A59AD42A00C07D009C9E3E /* UserStatsView.swift in Sources */,
|
||||||
24A07CBC2A022B2000F4ECA8 /* ProfileViewModel.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
@ -37,6 +37,7 @@ extension ContentView {
|
|||||||
ZStack(alignment: .topLeading) {
|
ZStack(alignment: .topLeading) {
|
||||||
MainTabView()
|
MainTabView()
|
||||||
.toolbar(showMenu ? .hidden : .visible)
|
.toolbar(showMenu ? .hidden : .visible)
|
||||||
|
|
||||||
if showMenu {
|
if showMenu {
|
||||||
ZStack {
|
ZStack {
|
||||||
Color(.black)
|
Color(.black)
|
||||||
@ -54,6 +55,8 @@ extension ContentView {
|
|||||||
.offset(x: showMenu ? 0: -300, y: 0)
|
.offset(x: showMenu ? 0: -300, y: 0)
|
||||||
.background(showMenu ? Color.white: Color.clear)
|
.background(showMenu ? Color.white: Color.clear)
|
||||||
}
|
}
|
||||||
|
.navigationTitle("Home")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
if let user = viewModel.currentUser {
|
if let user = viewModel.currentUser {
|
||||||
|
|||||||
@ -19,6 +19,7 @@ class AuthViewModel: ObservableObject {
|
|||||||
init() {
|
init() {
|
||||||
self.userSession = Auth.auth().currentUser
|
self.userSession = Auth.auth().currentUser
|
||||||
self.fetchUser()
|
self.fetchUser()
|
||||||
|
print("DEBUG: user session is \(self.userSession?.uid)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func login(withEmail email: String, password: String) {
|
func login(withEmail email: String, password: String) {
|
||||||
|
|||||||
@ -9,15 +9,15 @@ import SwiftUI
|
|||||||
import Kingfisher
|
import Kingfisher
|
||||||
|
|
||||||
struct TweetRowView: View {
|
struct TweetRowView: View {
|
||||||
@ObservedObject var viewModel: TweetVeiwModel
|
let tweet: Tweet
|
||||||
|
|
||||||
init(tweet: Tweet) {
|
init(tweet: Tweet) {
|
||||||
self.viewModel = TweetVeiwModel(tweet: tweet)
|
self.tweet = tweet
|
||||||
}
|
}
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
// profile image + user info + tweet
|
// profile image + user info + tweet
|
||||||
if let user = viewModel.tweet.user {
|
if let user = tweet.user {
|
||||||
HStack(alignment: .top, spacing: 12) {
|
HStack(alignment: .top, spacing: 12) {
|
||||||
// profile image
|
// profile image
|
||||||
KFImage(URL(string: user.profileImageUrl))
|
KFImage(URL(string: user.profileImageUrl))
|
||||||
@ -41,7 +41,7 @@ struct TweetRowView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tweet caption
|
// tweet caption
|
||||||
Text(viewModel.tweet.caption)
|
Text(tweet.caption)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
}
|
}
|
||||||
@ -64,11 +64,10 @@ struct TweetRowView: View {
|
|||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
Button {
|
Button {
|
||||||
viewModel.tweet.didLike ?? false ? viewModel.unlikeTweet() : viewModel.likeTweet()
|
// action here
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: viewModel.tweet.didLike ?? false ? "heart.fill" : "heart")
|
Image(systemName: "heart")
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(viewModel.tweet.didLike ?? false ? .red : .gray)
|
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
Button {
|
Button {
|
||||||
@ -1,38 +0,0 @@
|
|||||||
//
|
|
||||||
// TweetRowViewModel.swift
|
|
||||||
// dudu-tweet
|
|
||||||
//
|
|
||||||
// Created by ching on 2023/5/3.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class TweetVeiwModel: ObservableObject {
|
|
||||||
private let service = TweetService()
|
|
||||||
@Published var tweet: Tweet
|
|
||||||
|
|
||||||
init(tweet: Tweet) {
|
|
||||||
self.tweet = tweet
|
|
||||||
self.checkUserLikedTweet()
|
|
||||||
}
|
|
||||||
|
|
||||||
func likeTweet() {
|
|
||||||
service.likeTweet(tweet) { _ in
|
|
||||||
self.tweet.didLike = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func unlikeTweet() {
|
|
||||||
service.unlikeTweet(tweet) { _ in
|
|
||||||
self.tweet.didLike = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkUserLikedTweet() {
|
|
||||||
service.checkIfUserLikedTweet(tweet) { didLiked in
|
|
||||||
if didLiked {
|
|
||||||
self.tweet.didLike = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -18,14 +18,17 @@ struct ExploreView: View {
|
|||||||
LazyVStack {
|
LazyVStack {
|
||||||
ForEach(viewModel.searchableUsers) { user in
|
ForEach(viewModel.searchableUsers) { user in
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
ProfileView(user: user)
|
ProfileView(user: user)
|
||||||
} label: {
|
} label: {
|
||||||
UserRowView(user: user)
|
UserRowView(user: user)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.navigationTitle("Explore")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,6 +37,7 @@ struct FeedView: View {
|
|||||||
.fullScreenCover(isPresented: $showNewTweetView) {
|
.fullScreenCover(isPresented: $showNewTweetView) {
|
||||||
NewTweetView()
|
NewTweetView()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,54 +11,35 @@ struct MainTabView: View {
|
|||||||
@State private var selectedIndex: Int = 0
|
@State private var selectedIndex: Int = 0
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
TabView(selection: $selectedIndex) {
|
||||||
TabView(selection: $selectedIndex) {
|
FeedView()
|
||||||
FeedView()
|
.onTapGesture {
|
||||||
.onTapGesture {
|
self.selectedIndex = 0
|
||||||
self.selectedIndex = 0
|
}
|
||||||
}
|
.tabItem {
|
||||||
.tabItem {
|
Image(systemName: "house")
|
||||||
Image(systemName: "house")
|
}.tag(0)
|
||||||
}.tag(0)
|
ExploreView()
|
||||||
ExploreView()
|
.onTapGesture {
|
||||||
.onTapGesture {
|
self.selectedIndex = 1
|
||||||
self.selectedIndex = 1
|
}
|
||||||
}
|
.tabItem {
|
||||||
.tabItem {
|
Image(systemName: "magnifyingglass")
|
||||||
Image(systemName: "magnifyingglass")
|
}.tag(1)
|
||||||
}.tag(1)
|
NotificationsView()
|
||||||
NotificationsView()
|
.onTapGesture {
|
||||||
.onTapGesture {
|
self.selectedIndex = 2
|
||||||
self.selectedIndex = 2
|
}
|
||||||
}
|
.tabItem {
|
||||||
.tabItem {
|
Image(systemName: "bell")
|
||||||
Image(systemName: "bell")
|
}.tag(2)
|
||||||
}.tag(2)
|
MessagesView()
|
||||||
MessagesView()
|
.onTapGesture {
|
||||||
.onTapGesture {
|
self.selectedIndex = 3
|
||||||
self.selectedIndex = 3
|
}
|
||||||
}
|
.tabItem {
|
||||||
.tabItem {
|
Image(systemName: "envelope")
|
||||||
Image(systemName: "envelope")
|
}.tag(3)
|
||||||
}.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 ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
//
|
|
||||||
// ProfileViewModel.swift
|
|
||||||
// dudu-tweet
|
|
||||||
//
|
|
||||||
// Created by ching on 2023/5/3.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
|
|
||||||
class ProfileViewModel: ObservableObject {
|
|
||||||
@Published var tweets = [Tweet]()
|
|
||||||
@Published var likedTweets = [Tweet]()
|
|
||||||
private let service = TweetService()
|
|
||||||
private let userService = UserService()
|
|
||||||
let user: User
|
|
||||||
|
|
||||||
init(user: User) {
|
|
||||||
self.user = user
|
|
||||||
self.fetchUserTweet()
|
|
||||||
self.fetchLikedTweets()
|
|
||||||
}
|
|
||||||
|
|
||||||
var actionBarTitle: String {
|
|
||||||
user.isCurrentUser ? "Edit Profile" : "Follow"
|
|
||||||
}
|
|
||||||
|
|
||||||
func tweets(forFilter filter: TweetFilterViewModel) -> [Tweet] {
|
|
||||||
switch filter {
|
|
||||||
case .tweets:
|
|
||||||
return tweets
|
|
||||||
case .replies:
|
|
||||||
return tweets
|
|
||||||
case .likes:
|
|
||||||
return likedTweets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchLikedTweets() {
|
|
||||||
guard let uid = user.id else { return }
|
|
||||||
service.fetchLikedTweets(forUid: uid) { tweets in
|
|
||||||
self.likedTweets = tweets
|
|
||||||
|
|
||||||
for i in 0 ..< tweets.count {
|
|
||||||
let uid = tweets[i].uid
|
|
||||||
self.userService.fetchUser(withUid: uid) { user in
|
|
||||||
self.likedTweets[i].user = user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,12 +12,12 @@ struct ProfileView: View {
|
|||||||
@State private var selectedFilter: TweetFilterViewModel = .tweets
|
@State private var selectedFilter: TweetFilterViewModel = .tweets
|
||||||
// @Environment(\.presentationMode) var mode // 用来从explore 页面跳转过来之后跳转回去
|
// @Environment(\.presentationMode) var mode // 用来从explore 页面跳转过来之后跳转回去
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
@ObservedObject var viewModel: ProfileViewModel
|
private let user: User
|
||||||
|
|
||||||
@Namespace var animation
|
@Namespace var animation
|
||||||
|
|
||||||
init(user: User) {
|
init(user: User) {
|
||||||
self.viewModel = ProfileViewModel(user: user)
|
self.user = user
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -65,7 +65,7 @@ extension ProfileView {
|
|||||||
.offset(x: 16, y: -4)
|
.offset(x: 16, y: -4)
|
||||||
}
|
}
|
||||||
|
|
||||||
KFImage(URL(string: viewModel.user.profileImageUrl))
|
KFImage(URL(string: user.profileImageUrl))
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFill()
|
.scaledToFill()
|
||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
@ -87,7 +87,7 @@ extension ProfileView {
|
|||||||
Button {
|
Button {
|
||||||
// action here
|
// action here
|
||||||
} label: {
|
} label: {
|
||||||
Text(viewModel.actionBarTitle)
|
Text("Edit Profile")
|
||||||
.font(.subheadline).bold()
|
.font(.subheadline).bold()
|
||||||
.frame(width: 120, height: 32)
|
.frame(width: 120, height: 32)
|
||||||
.foregroundColor(.black)
|
.foregroundColor(.black)
|
||||||
@ -100,12 +100,12 @@ extension ProfileView {
|
|||||||
var userInfoDetails: some View {
|
var userInfoDetails: some View {
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
HStack {
|
HStack {
|
||||||
Text(viewModel.user.fullname)
|
Text(user.fullname)
|
||||||
.font(.title2).bold()
|
.font(.title2).bold()
|
||||||
Image(systemName: "checkmark.seal.fill")
|
Image(systemName: "checkmark.seal.fill")
|
||||||
.foregroundColor(Color(.systemBlue))
|
.foregroundColor(Color(.systemBlue))
|
||||||
}
|
}
|
||||||
Text("@\(viewModel.user.username)")
|
Text("@\(user.username)")
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
Text("一个普通人")
|
Text("一个普通人")
|
||||||
@ -165,8 +165,8 @@ extension ProfileView {
|
|||||||
var tweetsView: some View {
|
var tweetsView: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
LazyVStack {
|
LazyVStack {
|
||||||
ForEach(viewModel.tweets(forFilter: self.selectedFilter)) { tweet in
|
ForEach(0...9, id: \.self) { _ in
|
||||||
TweetRowView(tweet: tweet)
|
// TweetRowView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,5 +16,4 @@ struct Tweet: Identifiable, Decodable {
|
|||||||
var likes: Int
|
var likes: Int
|
||||||
|
|
||||||
var user: User?
|
var user: User?
|
||||||
var didLike: Bool? = false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import FirebaseFirestoreSwift
|
import FirebaseFirestoreSwift
|
||||||
import Firebase
|
|
||||||
|
|
||||||
struct User: Identifiable, Decodable {
|
struct User: Identifiable, Decodable {
|
||||||
@DocumentID var id: String?
|
@DocumentID var id: String?
|
||||||
@ -14,6 +13,4 @@ struct User: Identifiable, Decodable {
|
|||||||
let fullname: String
|
let fullname: String
|
||||||
let profileImageUrl: String
|
let profileImageUrl: String
|
||||||
let email: String
|
let email: String
|
||||||
|
|
||||||
var isCurrentUser: Bool { return Auth.auth().currentUser?.uid == self.id}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,84 +30,12 @@ struct TweetService {
|
|||||||
|
|
||||||
|
|
||||||
func fetchTweets(completion: @escaping([Tweet]) -> Void) {
|
func fetchTweets(completion: @escaping([Tweet]) -> Void) {
|
||||||
Firestore.firestore().collection("tweets")
|
Firestore.firestore().collection("tweets").getDocuments { snapshot, _ in
|
||||||
.order(by: "timestamp", descending: true)
|
|
||||||
.getDocuments { snapshot, _ in
|
|
||||||
guard let documents = snapshot?.documents else { return }
|
guard let documents = snapshot?.documents else { return }
|
||||||
|
|
||||||
let tweets = documents.compactMap({ try? $0.data(as: Tweet.self) })
|
let tweets = documents.compactMap({ try? $0.data(as: Tweet.self) })
|
||||||
completion(tweets)
|
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()}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func likeTweet(_ tweet: Tweet, completion: @escaping(Bool) -> Void) {
|
|
||||||
guard let uid = Auth.auth().currentUser?.uid else { return }
|
|
||||||
guard let tweetId = tweet.id else { return }
|
|
||||||
|
|
||||||
let userLikeRef = Firestore.firestore().collection("users").document(uid).collection("user-likes")
|
|
||||||
Firestore.firestore().collection("tweets").document(tweetId)
|
|
||||||
.updateData(["likes": tweet.likes + 1]) { _ in
|
|
||||||
userLikeRef.document(tweetId).setData([:]) { _ in
|
|
||||||
completion(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func unlikeTweet(_ tweet: Tweet, completion: @escaping(Bool) -> Void) {
|
|
||||||
guard let uid = Auth.auth().currentUser?.uid else { return }
|
|
||||||
guard let tweetId = tweet.id else { return }
|
|
||||||
guard tweet.likes > 0 else { return }
|
|
||||||
|
|
||||||
let userLikeRef = Firestore.firestore().collection("users").document(uid).collection("user-likes")
|
|
||||||
Firestore.firestore().collection("tweets").document(tweetId)
|
|
||||||
.updateData(["likes": tweet.likes - 1]) { _ in
|
|
||||||
userLikeRef.document(tweetId).delete() { _ in
|
|
||||||
completion(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkIfUserLikedTweet(_ tweet: Tweet, completion: @escaping(Bool) -> Void) {
|
|
||||||
guard let uid = Auth.auth().currentUser?.uid else { return }
|
|
||||||
guard let tweetId = tweet.id else { return }
|
|
||||||
print("DEBUG: checking likes for \(tweetId)")
|
|
||||||
Firestore.firestore().collection("users").document(uid).collection("user-likes")
|
|
||||||
.document(tweetId)
|
|
||||||
.getDocument { snapshot, _ in
|
|
||||||
guard let snapshot = snapshot else { return }
|
|
||||||
completion(snapshot.exists)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchLikedTweets(forUid uid: String, completion: @escaping([Tweet]) -> Void) {
|
|
||||||
var tweets = [Tweet]()
|
|
||||||
Firestore.firestore().collection("users").document(uid).collection("user-likes")
|
|
||||||
.getDocuments { snapshot, _ in
|
|
||||||
guard let documents = snapshot?.documents else { return }
|
|
||||||
documents.forEach { doc in
|
|
||||||
let tweetID = doc.documentID
|
|
||||||
Firestore.firestore().collection("tweets")
|
|
||||||
.document(tweetID)
|
|
||||||
.getDocument { snapshot, _ in
|
|
||||||
guard let tweet = try? snapshot?.data(as: Tweet.self) else { return }
|
|
||||||
tweets.append(tweet)
|
|
||||||
completion(tweets)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user