mirror of
git://jb55.com/damus
synced 2024-09-30 00:40:45 +00:00
ui: add follow hashtag ui on search view
This commit is contained in:
parent
bebaffd247
commit
17df2972d9
@ -144,6 +144,7 @@
|
|||||||
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */; };
|
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */; };
|
||||||
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */ = {isa = PBXBuildFile; productRef = 4C649880286E0EE300EAE2B3 /* secp256k1 */; };
|
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */ = {isa = PBXBuildFile; productRef = 4C649880286E0EE300EAE2B3 /* secp256k1 */; };
|
||||||
4C687C212A5F7ED00092C550 /* DamusBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C202A5F7ED00092C550 /* DamusBackground.swift */; };
|
4C687C212A5F7ED00092C550 /* DamusBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C202A5F7ED00092C550 /* DamusBackground.swift */; };
|
||||||
|
4C687C242A5FA86D0092C550 /* SearchHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C232A5FA86D0092C550 /* SearchHeaderView.swift */; };
|
||||||
4C687C272A6039500092C550 /* TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C262A6039500092C550 /* TestData.swift */; };
|
4C687C272A6039500092C550 /* TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C262A6039500092C550 /* TestData.swift */; };
|
||||||
4C73C5142A4437C10062CAC0 /* ZapUserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C5132A4437C10062CAC0 /* ZapUserView.swift */; };
|
4C73C5142A4437C10062CAC0 /* ZapUserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C5132A4437C10062CAC0 /* ZapUserView.swift */; };
|
||||||
4C75EFA427FA577B0006080F /* PostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA327FA577B0006080F /* PostView.swift */; };
|
4C75EFA427FA577B0006080F /* PostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA327FA577B0006080F /* PostView.swift */; };
|
||||||
@ -619,6 +620,7 @@
|
|||||||
4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesView.swift; sourceTree = "<group>"; };
|
4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesView.swift; sourceTree = "<group>"; };
|
||||||
4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesModel.swift; sourceTree = "<group>"; };
|
4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesModel.swift; sourceTree = "<group>"; };
|
||||||
4C687C202A5F7ED00092C550 /* DamusBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusBackground.swift; sourceTree = "<group>"; };
|
4C687C202A5F7ED00092C550 /* DamusBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusBackground.swift; sourceTree = "<group>"; };
|
||||||
|
4C687C232A5FA86D0092C550 /* SearchHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHeaderView.swift; sourceTree = "<group>"; };
|
||||||
4C687C262A6039500092C550 /* TestData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestData.swift; sourceTree = "<group>"; };
|
4C687C262A6039500092C550 /* TestData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestData.swift; sourceTree = "<group>"; };
|
||||||
4C73C5132A4437C10062CAC0 /* ZapUserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapUserView.swift; sourceTree = "<group>"; };
|
4C73C5132A4437C10062CAC0 /* ZapUserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapUserView.swift; sourceTree = "<group>"; };
|
||||||
4C75EFA327FA577B0006080F /* PostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostView.swift; sourceTree = "<group>"; };
|
4C75EFA327FA577B0006080F /* PostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostView.swift; sourceTree = "<group>"; };
|
||||||
@ -1099,6 +1101,14 @@
|
|||||||
path = Notifications;
|
path = Notifications;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4C687C2A2A6058450092C550 /* Search */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4C687C232A5FA86D0092C550 /* SearchHeaderView.swift */,
|
||||||
|
);
|
||||||
|
path = Search;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4C75EFA227FA576C0006080F /* Views */ = {
|
4C75EFA227FA576C0006080F /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -1404,6 +1414,7 @@
|
|||||||
4CE4F9DF285287A000C00DD9 /* Components */ = {
|
4CE4F9DF285287A000C00DD9 /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4C687C2A2A6058450092C550 /* Search */,
|
||||||
4C7D09702A0AEF4C00943473 /* Gradients */,
|
4C7D09702A0AEF4C00943473 /* Gradients */,
|
||||||
31D2E846295218AF006D67F8 /* Shimmer.swift */,
|
31D2E846295218AF006D67F8 /* Shimmer.swift */,
|
||||||
4CD7641A28A1641400B6928F /* EndBlock.swift */,
|
4CD7641A28A1641400B6928F /* EndBlock.swift */,
|
||||||
@ -1946,6 +1957,7 @@
|
|||||||
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
||||||
4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */,
|
4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */,
|
||||||
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */,
|
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */,
|
||||||
|
4C687C242A5FA86D0092C550 /* SearchHeaderView.swift in Sources */,
|
||||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */,
|
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */,
|
||||||
4C1A9A2329DDDB8100516EAC /* IconLabel.swift in Sources */,
|
4C1A9A2329DDDB8100516EAC /* IconLabel.swift in Sources */,
|
||||||
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
||||||
|
134
damus/Components/Search/SearchHeaderView.swift
Normal file
134
damus/Components/Search/SearchHeaderView.swift
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
//
|
||||||
|
// SearchIconView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-07-12.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SearchHeaderView: View {
|
||||||
|
let state: DamusState
|
||||||
|
let described: DescribedSearch
|
||||||
|
@State var is_following: Bool
|
||||||
|
|
||||||
|
init(state: DamusState, described: DescribedSearch) {
|
||||||
|
self.state = state
|
||||||
|
self.described = described
|
||||||
|
|
||||||
|
let is_following = (described.is_hashtag.map {
|
||||||
|
ht in is_following_hashtag(contacts: state.contacts.event, hashtag: ht)
|
||||||
|
}) ?? false
|
||||||
|
|
||||||
|
self._is_following = State(wrappedValue: is_following)
|
||||||
|
}
|
||||||
|
|
||||||
|
var Icon: some View {
|
||||||
|
ZStack {
|
||||||
|
Circle()
|
||||||
|
.fill(Color(red: 0xF8/255.0, green: 0xE7/255.0, blue: 0xF8/255.0))
|
||||||
|
.frame(width: 54, height: 54)
|
||||||
|
|
||||||
|
switch described {
|
||||||
|
case .hashtag:
|
||||||
|
Text("#")
|
||||||
|
.font(.largeTitle.bold())
|
||||||
|
.foregroundStyle(PinkGradient)
|
||||||
|
.mask(Text("#")
|
||||||
|
.font(.largeTitle.bold()))
|
||||||
|
|
||||||
|
case .unknown:
|
||||||
|
Image(systemName: "magnifyingglass")
|
||||||
|
.font(.title.bold())
|
||||||
|
.foregroundStyle(PinkGradient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var SearchText: Text {
|
||||||
|
switch described {
|
||||||
|
case .hashtag(let ht):
|
||||||
|
Text(verbatim: "#" + ht)
|
||||||
|
case .unknown:
|
||||||
|
Text("Search")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unfollow(_ hashtag: String) {
|
||||||
|
is_following = false
|
||||||
|
handle_unfollow(state: state, unfollow: .t(hashtag))
|
||||||
|
}
|
||||||
|
|
||||||
|
func follow(_ hashtag: String) {
|
||||||
|
is_following = true
|
||||||
|
handle_follow(state: state, follow: .t(hashtag))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FollowButton(_ ht: String) -> some View {
|
||||||
|
return Button(action: { follow(ht) }) {
|
||||||
|
Text("Follow hashtag")
|
||||||
|
.font(.footnote.bold())
|
||||||
|
}
|
||||||
|
.buttonStyle(GradientButtonStyle(padding: 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnfollowButton(_ ht: String) -> some View {
|
||||||
|
return Button(action: { unfollow(ht) }) {
|
||||||
|
Text("Unfollow hashtag")
|
||||||
|
.font(.footnote.bold())
|
||||||
|
}
|
||||||
|
.buttonStyle(GradientButtonStyle(padding: 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(alignment: .center, spacing: 30) {
|
||||||
|
Icon
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 10.0) {
|
||||||
|
SearchText
|
||||||
|
.foregroundStyle(DamusLogoGradient.gradient)
|
||||||
|
.font(.title.bold())
|
||||||
|
|
||||||
|
if state.is_privkey_user, case .hashtag(let ht) = described {
|
||||||
|
if is_following {
|
||||||
|
UnfollowButton(ht)
|
||||||
|
} else {
|
||||||
|
FollowButton(ht)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onReceive(handle_notify(.followed)) { notif in
|
||||||
|
let ref = notif.object as! ReferencedId
|
||||||
|
guard hashtag_matches_search(desc: self.described, ref: ref) else { return }
|
||||||
|
self.is_following = true
|
||||||
|
}
|
||||||
|
.onReceive(handle_notify(.unfollowed)) { notif in
|
||||||
|
let ref = notif.object as! ReferencedId
|
||||||
|
guard hashtag_matches_search(desc: self.described, ref: ref) else { return }
|
||||||
|
self.is_following = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashtag_matches_search(desc: DescribedSearch, ref: ReferencedId) -> Bool {
|
||||||
|
guard let ht = desc.is_hashtag, ref.key == "t" && ref.ref_id == ht
|
||||||
|
else { return false }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_following_hashtag(contacts: NostrEvent?, hashtag: String) -> Bool {
|
||||||
|
guard let contacts else { return false }
|
||||||
|
return is_already_following(contacts: contacts, follow: .t(hashtag))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct SearchHeaderView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
SearchHeaderView(state: test_damus_state(), described: .hashtag("damus"))
|
||||||
|
|
||||||
|
SearchHeaderView(state: test_damus_state(), described: .unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,31 +12,69 @@ struct SearchView: View {
|
|||||||
@ObservedObject var search: SearchModel
|
@ObservedObject var search: SearchModel
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
|
let height: CGFloat = 250.0
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TimelineView<AnyView>(events: search.events, loading: $search.loading, damus: appstate, show_friend_icon: true, filter: { _ in true })
|
TimelineView(events: search.events, loading: $search.loading, damus: appstate, show_friend_icon: true, filter: { _ in true }) {
|
||||||
.navigationBarTitle(describe_search(search.search))
|
ZStack(alignment: .leading) {
|
||||||
.onReceive(handle_notify(.switched_timeline)) { obj in
|
DamusBackground(maxHeight: height)
|
||||||
dismiss()
|
.mask(LinearGradient(gradient: Gradient(colors: [.black, .black, .black, .clear]), startPoint: .top, endPoint: .bottom))
|
||||||
}
|
SearchHeaderView(state: appstate, described: described_search)
|
||||||
.onAppear() {
|
.padding(.leading, 30)
|
||||||
search.subscribe()
|
.padding(.top, 100)
|
||||||
}
|
|
||||||
.onDisappear() {
|
|
||||||
search.unsubscribe()
|
|
||||||
}
|
|
||||||
.onReceive(handle_notify(.new_mutes)) { notif in
|
|
||||||
search.filter_muted()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.ignoresSafeArea()
|
||||||
|
.onReceive(handle_notify(.switched_timeline)) { obj in
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
.onAppear() {
|
||||||
|
search.subscribe()
|
||||||
|
}
|
||||||
|
.onDisappear() {
|
||||||
|
search.unsubscribe()
|
||||||
|
}
|
||||||
|
.onReceive(handle_notify(.new_mutes)) { notif in
|
||||||
|
search.filter_muted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var described_search: DescribedSearch {
|
||||||
|
return describe_search(search.search)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func describe_search(_ filter: NostrFilter) -> String {
|
enum DescribedSearch {
|
||||||
if let hashtags = filter.hashtag {
|
case hashtag(String)
|
||||||
if hashtags.count >= 1 {
|
case unknown
|
||||||
return "#" + hashtags[0]
|
|
||||||
|
var is_hashtag: String? {
|
||||||
|
switch self {
|
||||||
|
case .hashtag(let ht):
|
||||||
|
return ht
|
||||||
|
case .unknown:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "Search"
|
|
||||||
|
var description: String {
|
||||||
|
switch self {
|
||||||
|
case .hashtag(let s):
|
||||||
|
return "#" + s
|
||||||
|
case .unknown:
|
||||||
|
return "Search"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func describe_search(_ filter: NostrFilter) -> DescribedSearch {
|
||||||
|
if let hashtags = filter.hashtag {
|
||||||
|
if hashtags.count >= 1 {
|
||||||
|
return .hashtag(hashtags[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SearchView_Previews: PreviewProvider {
|
struct SearchView_Previews: PreviewProvider {
|
||||||
|
Loading…
Reference in New Issue
Block a user