Rename Friends of Friends to Trusted Network and add popover tips to DMs and Notifications toolbars on Trusted Network button
Changelog-Changed: Renamed Friends of Friends to Trusted Network Changelog-Added: Added popover tips to DMs and Notifications toolbars on Trusted Network button Signed-off-by: Terry Yiu <git@tyiu.xyz>
This commit is contained in:
@@ -9,10 +9,7 @@ import SwiftUI
|
||||
import AVKit
|
||||
import MediaPlayer
|
||||
import EmojiPicker
|
||||
|
||||
#if canImport(TipKit)
|
||||
import TipKit
|
||||
#endif
|
||||
|
||||
struct ZapSheet {
|
||||
let target: ZapTarget
|
||||
@@ -182,7 +179,7 @@ struct ContentView: View {
|
||||
NotificationsView(state: damus, notifications: home.notifications, subtitle: $menu_subtitle)
|
||||
|
||||
case .dms:
|
||||
DirectMessagesView(damus_state: damus_state!, model: damus_state!.dms, settings: damus_state!.settings)
|
||||
DirectMessagesView(damus_state: damus_state!, model: damus_state!.dms, settings: damus_state!.settings, subtitle: $menu_subtitle)
|
||||
}
|
||||
}
|
||||
.background(DamusColors.adaptableWhite)
|
||||
|
||||
@@ -35,9 +35,9 @@ enum FriendFilter: String, StringCodable {
|
||||
func description() -> String {
|
||||
switch self {
|
||||
case .all:
|
||||
return NSLocalizedString("All", comment: "Human-readable short description of the 'friends filter' when it is set to 'all'")
|
||||
return NSLocalizedString("All", comment: "Human-readable short description of the 'trusted network filter' when it is disabled, and therefore is showing all content.")
|
||||
case .friends_of_friends:
|
||||
return NSLocalizedString("Friends of friends", comment: "Human-readable short description of the 'friends filter' when it is set to 'friends-of-friends'")
|
||||
return NSLocalizedString("Trusted Network", comment: "Human-readable short description of the 'trusted network filter' when it is enabled, and therefore showing content from only the trusted network.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
//
|
||||
// FriendsButton.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-04-21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct FriendsButton: View {
|
||||
@Binding var filter: FriendFilter
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
switch self.filter {
|
||||
case .all:
|
||||
self.filter = .friends_of_friends
|
||||
case .friends_of_friends:
|
||||
self.filter = .all
|
||||
}
|
||||
}) {
|
||||
if filter == .friends_of_friends {
|
||||
LINEAR_GRADIENT
|
||||
.mask(Image("user-added")
|
||||
.resizable()
|
||||
).frame(width: 28, height: 28)
|
||||
} else {
|
||||
Image("user-added")
|
||||
.resizable()
|
||||
.frame(width: 28, height: 28)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
||||
struct FriendsButton_Previews: PreviewProvider {
|
||||
@State static var enabled: FriendFilter = .all
|
||||
|
||||
static var previews: some View {
|
||||
FriendsButton(filter: $enabled)
|
||||
}
|
||||
}
|
||||
54
damus/Views/Buttons/TrustedNetworkButton.swift
Normal file
54
damus/Views/Buttons/TrustedNetworkButton.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// TrustedNetworkButton.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-04-21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TrustedNetworkButton: View {
|
||||
@Binding var filter: FriendFilter
|
||||
var action: (@MainActor () -> Void)? = nil
|
||||
|
||||
var MainButton: some View {
|
||||
Button(action: {
|
||||
switch self.filter {
|
||||
case .all:
|
||||
self.filter = .friends_of_friends
|
||||
case .friends_of_friends:
|
||||
self.filter = .all
|
||||
}
|
||||
|
||||
if let action {
|
||||
action()
|
||||
}
|
||||
}) {
|
||||
if filter == .friends_of_friends {
|
||||
LINEAR_GRADIENT
|
||||
.mask(Image(systemName: "network.badge.shield.half.filled")
|
||||
.frame(width: 24, height: 24)
|
||||
)
|
||||
.scaledToFit()
|
||||
.frame(width: 24, height: 24)
|
||||
} else {
|
||||
Image(systemName: "network.slash")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
MainButton
|
||||
}
|
||||
}
|
||||
|
||||
struct TrustedNetworkButton_Previews: PreviewProvider {
|
||||
@State static var enabled: FriendFilter = .all
|
||||
|
||||
static var previews: some View {
|
||||
TrustedNetworkButton(filter: $enabled)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import TipKit
|
||||
|
||||
enum DMType: Hashable {
|
||||
case rando
|
||||
@@ -18,6 +19,7 @@ struct DirectMessagesView: View {
|
||||
@State var dm_type: DMType = .friend
|
||||
@ObservedObject var model: DirectMessagesModel
|
||||
@ObservedObject var settings: UserSettingsStore
|
||||
@Binding var subtitle: String?
|
||||
|
||||
func MainContent(requests: Bool) -> some View {
|
||||
ScrollView {
|
||||
@@ -72,7 +74,15 @@ struct DirectMessagesView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
let showTrustedButton = would_filter_non_friends_from_dms(contacts: damus_state.contacts, dms: self.model.dms)
|
||||
VStack(spacing: 0) {
|
||||
if #available(iOS 17, *), showTrustedButton {
|
||||
TipView(TrustedNetworkButtonTip.shared)
|
||||
.tipBackground(.clear)
|
||||
.tipViewStyle(TrustedNetworkButtonTipViewStyle())
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
CustomPicker(tabs: [
|
||||
(NSLocalizedString("DMs", comment: "Picker option for DM selector for seeing only DMs that have been responded to. DM is the English abbreviation for Direct Message."), DMType.friend),
|
||||
(NSLocalizedString("Requests", comment: "Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet"), DMType.rando),
|
||||
@@ -92,12 +102,22 @@ struct DirectMessagesView: View {
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
if would_filter_non_friends_from_dms(contacts: damus_state.contacts, dms: self.model.dms) {
|
||||
|
||||
FriendsButton(filter: $settings.friend_filter)
|
||||
if showTrustedButton {
|
||||
TrustedNetworkButton(filter: $settings.friend_filter) {
|
||||
if #available(iOS 17, *) {
|
||||
TrustedNetworkButtonTip.shared.invalidate(reason: .actionPerformed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
self.subtitle = settings.friend_filter.description()
|
||||
|
||||
}
|
||||
.onChange(of: settings.friend_filter) { val in
|
||||
self.subtitle = val.description()
|
||||
}
|
||||
.navigationTitle(NSLocalizedString("DMs", comment: "Navigation title for view of DMs, where DM is an English abbreviation for Direct Message."))
|
||||
}
|
||||
}
|
||||
@@ -115,6 +135,6 @@ func would_filter_non_friends_from_dms(contacts: Contacts, dms: [DirectMessageMo
|
||||
struct DirectMessagesView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let ds = test_damus_state
|
||||
DirectMessagesView(damus_state: ds, model: ds.dms, settings: ds.settings)
|
||||
DirectMessagesView(damus_state: ds, model: ds.dms, settings: ds.settings, subtitle: .constant(nil))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,7 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
#if canImport(TipKit)
|
||||
import TipKit
|
||||
#endif
|
||||
|
||||
class NotificationFilter: ObservableObject, Equatable {
|
||||
@Published var state: NotificationFilterState
|
||||
@@ -80,11 +77,10 @@ struct NotificationsView: View {
|
||||
@SceneStorage("NotificationsView.filter_state") var filter_state: NotificationFilterState = .all
|
||||
@Binding var subtitle: String?
|
||||
|
||||
@State var showTip: Bool = true
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var body: some View {
|
||||
let showTrustedButton = would_filter_non_friends_from_notifications(contacts: state.contacts, state: filter_state, items: self.notifications.notifications)
|
||||
TabView(selection: $filter_state) {
|
||||
NotificationTab(
|
||||
NotificationFilter(
|
||||
@@ -121,14 +117,19 @@ struct NotificationsView: View {
|
||||
Button(
|
||||
action: { state.nav.push(route: Route.NotificationSettings(settings: state.settings)) },
|
||||
label: {
|
||||
Image("settings")
|
||||
Image(systemName: "gearshape")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
)
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
if would_filter_non_friends_from_notifications(contacts: state.contacts, state: filter_state, items: self.notifications.notifications) {
|
||||
FriendsButton(filter: $filter.friend_filter)
|
||||
if showTrustedButton {
|
||||
TrustedNetworkButton(filter: $filter.friend_filter) {
|
||||
if #available(iOS 17, *) {
|
||||
TrustedNetworkButtonTip.shared.invalidate(reason: .actionPerformed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,6 +147,13 @@ struct NotificationsView: View {
|
||||
}
|
||||
.safeAreaInset(edge: .top, spacing: 0) {
|
||||
VStack(spacing: 0) {
|
||||
if #available(iOS 17, *), showTrustedButton {
|
||||
TipView(TrustedNetworkButtonTip.shared)
|
||||
.tipBackground(.clear)
|
||||
.tipViewStyle(TrustedNetworkButtonTipViewStyle())
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
CustomPicker(tabs: [
|
||||
(NSLocalizedString("All", comment: "Label for filter for all notifications."), NotificationFilterState.all),
|
||||
(NSLocalizedString("Zaps", comment: "Label for filter for zap notifications."), NotificationFilterState.zaps),
|
||||
|
||||
25
damus/Views/Tips/TrustedNetworkButtonTip.swift
Normal file
25
damus/Views/Tips/TrustedNetworkButtonTip.swift
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TrustedNetworkButtonTip.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Terry Yiu on 6/4/25.
|
||||
//
|
||||
|
||||
import TipKit
|
||||
|
||||
@available(iOS 17, *)
|
||||
struct TrustedNetworkButtonTip: Tip {
|
||||
static let shared = TrustedNetworkButtonTip()
|
||||
|
||||
var title: Text {
|
||||
Text("Toggle visibility of content from outside your trusted network", comment: "Title of tip that informs users what trusted network means and that they can toggle the visibility of content from outside their trusted network.")
|
||||
}
|
||||
|
||||
var message: Text? {
|
||||
Text("Your trusted network is comprised of profiles you follow and profiles that they follow.", comment: "Description of the tip that informs users what trusted network means.")
|
||||
}
|
||||
|
||||
var image: Image? {
|
||||
Image(systemName: "network.badge.shield.half.filled")
|
||||
}
|
||||
}
|
||||
79
damus/Views/Tips/TrustedNetworkButtonTipViewStyle.swift
Normal file
79
damus/Views/Tips/TrustedNetworkButtonTipViewStyle.swift
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// TrustedNetworkButtonTipViewStyle.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Terry Yiu on 6/7/25.
|
||||
//
|
||||
|
||||
import TipKit
|
||||
|
||||
// (tyiu): Apple's native popover tips have a lot of rendering and race condition issues --
|
||||
// text being rendered in the wrong locations or not at all, or the tip gets opened in full screen.
|
||||
//
|
||||
// Instead, we are introducing this custom popover tip view style to emulate a similar look and feel.
|
||||
// The main thing needed from this view style is really just an arrow on the top right corner
|
||||
// to point to the TrustedNetworkButton on the NotificationsView and DirectMessagesview.
|
||||
@available(iOS 17, *)
|
||||
struct TrustedNetworkButtonTipViewStyle: TipViewStyle {
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
VStack(spacing: 0) {
|
||||
// Arrow pointing up to the button (positioned at top right)
|
||||
HStack {
|
||||
Spacer()
|
||||
Triangle()
|
||||
.fill(Color(.secondarySystemBackground))
|
||||
.frame(width: 24, height: 14)
|
||||
}
|
||||
|
||||
HStack(alignment: .top, spacing: 12) {
|
||||
// Icon
|
||||
configuration.image
|
||||
.foregroundStyle(.tint)
|
||||
.font(.title2)
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
configuration.title
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
configuration.message
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: { configuration.tip.invalidate(reason: .tipClosed) }) {
|
||||
Image(systemName: "xmark")
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(Color(.tertiaryLabel))
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 14)
|
||||
.background(Color(.secondarySystemBackground))
|
||||
.clipShape(
|
||||
.rect(
|
||||
topLeadingRadius: 20,
|
||||
bottomLeadingRadius: 20,
|
||||
bottomTrailingRadius: 20,
|
||||
topTrailingRadius: 0
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom triangle shape for the popover arrow
|
||||
struct Triangle: Shape {
|
||||
func path(in rect: CGRect) -> Path {
|
||||
var path = Path()
|
||||
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
|
||||
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
|
||||
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
|
||||
path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))
|
||||
return path
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user