SearchHeaderView.swift (4889B)
1 // 2 // SearchIconView.swift 3 // damus 4 // 5 // Created by William Casarin on 2023-07-12. 6 // 7 8 import SwiftUI 9 10 struct SearchHeaderView: View { 11 let state: DamusState 12 let described: DescribedSearch 13 @State var is_following: Bool 14 15 init(state: DamusState, described: DescribedSearch) { 16 self.state = state 17 self.described = described 18 19 let is_following = (described.is_hashtag.map { 20 ht in is_following_hashtag(contacts: state.contacts.event, hashtag: ht) 21 }) ?? false 22 23 self._is_following = State(wrappedValue: is_following) 24 } 25 26 var Icon: some View { 27 ZStack { 28 switch described { 29 case .hashtag: 30 SingleCharacterAvatar(character: "#") 31 case .unknown: 32 SystemIconAvatar(system_name: "magnifyingglass") 33 } 34 } 35 } 36 37 var SearchText: Text { 38 Text(described.description) 39 } 40 41 var body: some View { 42 HStack(alignment: .center, spacing: 30) { 43 Icon 44 45 VStack(alignment: .leading, spacing: 10.0) { 46 SearchText 47 .foregroundStyle(DamusLogoGradient.gradient) 48 .font(.title.bold()) 49 50 if state.is_privkey_user, case .hashtag(let ht) = described { 51 if is_following { 52 HashtagUnfollowButton(damus_state: state, hashtag: ht, is_following: $is_following) 53 } else { 54 HashtagFollowButton(damus_state: state, hashtag: ht, is_following: $is_following) 55 } 56 } 57 } 58 } 59 .onReceive(handle_notify(.followed)) { ref in 60 guard hashtag_matches_search(desc: self.described, ref: ref) else { return } 61 self.is_following = true 62 } 63 .onReceive(handle_notify(.unfollowed)) { ref in 64 guard hashtag_matches_search(desc: self.described, ref: ref) else { return } 65 self.is_following = false 66 } 67 } 68 } 69 70 struct SystemIconAvatar: View { 71 let system_name: String 72 73 var body: some View { 74 NonImageAvatar { 75 Image(systemName: system_name) 76 .font(.title.bold()) 77 } 78 } 79 } 80 81 struct SingleCharacterAvatar: View { 82 let character: String 83 84 var body: some View { 85 NonImageAvatar { 86 Text(character) 87 .font(.largeTitle.bold()) 88 .mask(Text(character) 89 .font(.largeTitle.bold())) 90 } 91 } 92 } 93 94 struct NonImageAvatar<Content: View>: View { 95 let content: Content 96 97 init(@ViewBuilder content: () -> Content) { 98 self.content = content() 99 } 100 101 var body: some View { 102 ZStack { 103 Circle() 104 .fill(DamusColors.lightBackgroundPink) 105 .frame(width: 54, height: 54) 106 107 content 108 .foregroundStyle(PinkGradient) 109 } 110 } 111 } 112 113 struct HashtagUnfollowButton: View { 114 let damus_state: DamusState 115 let hashtag: String 116 @Binding var is_following: Bool 117 118 var body: some View { 119 return Button(action: { unfollow(hashtag) }) { 120 Text("Unfollow hashtag", comment: "Button to unfollow a given hashtag.") 121 .font(.footnote.bold()) 122 } 123 .buttonStyle(GradientButtonStyle(padding: 10)) 124 } 125 126 func unfollow(_ hashtag: String) { 127 is_following = false 128 handle_unfollow(state: damus_state, unfollow: FollowRef.hashtag(hashtag)) 129 } 130 } 131 132 struct HashtagFollowButton: View { 133 let damus_state: DamusState 134 let hashtag: String 135 @Binding var is_following: Bool 136 137 var body: some View { 138 return Button(action: { follow(hashtag) }) { 139 Text("Follow hashtag", comment: "Button to follow a given hashtag.") 140 .font(.footnote.bold()) 141 } 142 .buttonStyle(GradientButtonStyle(padding: 10)) 143 } 144 145 func follow(_ hashtag: String) { 146 is_following = true 147 handle_follow(state: damus_state, follow: .hashtag(hashtag)) 148 } 149 } 150 151 func hashtag_matches_search(desc: DescribedSearch, ref: FollowRef) -> Bool { 152 guard case .hashtag(let follow_ht) = ref, 153 case .hashtag(let search_ht) = desc, 154 follow_ht == search_ht 155 else { 156 return false 157 } 158 159 return true 160 } 161 162 func is_following_hashtag(contacts: NostrEvent?, hashtag: String) -> Bool { 163 guard let contacts else { return false } 164 return is_already_following(contacts: contacts, follow: .hashtag(hashtag)) 165 } 166 167 168 struct SearchHeaderView_Previews: PreviewProvider { 169 static var previews: some View { 170 VStack(alignment: .leading) { 171 SearchHeaderView(state: test_damus_state, described: .hashtag("damus")) 172 173 SearchHeaderView(state: test_damus_state, described: .unknown) 174 } 175 } 176 }