mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-23 12:53:18 +00:00
ios: relay failure indication (#6665)
This commit is contained in:
1 parent
7ac901587a
commit
a4f3e21490
7 files changed
+202
-82
No files matched your search
@@ -379,6 +379,7 @@ final class ChatModel: ObservableObject {
|
||||
@Published var chatSubStatus: SubscriptionStatus?
|
||||
@Published var openAroundItemId: ChatItem.ID? = nil
|
||||
@Published var chatToTop: String?
|
||||
@Published var creatingChannelId: String?
|
||||
@Published var groupMembers: [GMember] = []
|
||||
@Published var groupMembersIndexes: Dictionary<Int64, Int> = [:] // groupMemberId to index in groupMembers list
|
||||
@Published var membersLoaded = false
|
||||
@@ -1241,13 +1242,19 @@ final class ChatModel: ObservableObject {
|
||||
updateGroup(groupInfo)
|
||||
return false
|
||||
}
|
||||
// update current chat
|
||||
if chatId == groupInfo.id {
|
||||
// update current chat or channel being created
|
||||
if chatId == groupInfo.id || creatingChannelId == groupInfo.id {
|
||||
if let i = groupMembersIndexes[member.groupMemberId] {
|
||||
let connStatusChanged = self.groupMembers[i].wrapped.activeConn?.connStatus != member.activeConn?.connStatus
|
||||
withAnimation(.default) {
|
||||
self.groupMembers[i].wrapped = member
|
||||
self.groupMembers[i].created = Date.now
|
||||
}
|
||||
// Updating wrapped on a reference-type GMember doesn't mutate the groupMembers array,
|
||||
// so ChatModel.objectWillChange doesn't fire automatically — notify views explicitly.
|
||||
if connStatusChanged {
|
||||
objectWillChange.send()
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
withAnimation {
|
||||
|
||||
@@ -386,9 +386,10 @@ struct ComposeView: View {
|
||||
if gInfo.membership.memberRole == .owner {
|
||||
let relays = channelRelaysModel.groupId == gInfo.groupId
|
||||
? channelRelaysModel.groupRelays : []
|
||||
let activeCount = relays.filter { $0.relayStatus == .rsActive }.count
|
||||
let failedCount = relays.filter { relayMemberConnFailed($0) != nil }.count
|
||||
let activeCount = relays.filter { $0.relayStatus == .rsActive && relayMemberConnFailed($0) == nil }.count
|
||||
if !relays.isEmpty && activeCount < relays.count {
|
||||
ownerChannelRelayBar(relays: relays, activeCount: activeCount)
|
||||
ownerChannelRelayBar(relays: relays, activeCount: activeCount, failedCount: failedCount)
|
||||
}
|
||||
} else {
|
||||
let hostnames = (chatModel.channelRelayHostnames[gInfo.groupId] ?? []).sorted()
|
||||
@@ -398,6 +399,8 @@ struct ComposeView: View {
|
||||
let showProgress = !gInfo.nextConnectPrepared || composeState.inProgress
|
||||
let connectedCount = relayMembers.filter { $0.wrapped.activeConn?.connStatus == .ready }.count
|
||||
let deletedCount = relayMembers.filter { $0.wrapped.activeConn?.connStatus == .deleted }.count
|
||||
let failedCount = relayMembers.filter { $0.wrapped.activeConn?.connFailedErr != nil }.count
|
||||
let errorCount = deletedCount + failedCount
|
||||
let resolvedCount = connectedCount + deletedCount
|
||||
let total = relayMembers.count > 0 ? relayMembers.count : hostnames.count
|
||||
if total > 0, !showProgress || resolvedCount < total {
|
||||
@@ -405,7 +408,7 @@ struct ComposeView: View {
|
||||
hostnames: hostnames,
|
||||
relayMembers: relayMembers,
|
||||
connectedCount: connectedCount,
|
||||
deletedCount: deletedCount,
|
||||
errorCount: errorCount,
|
||||
total: total,
|
||||
showProgress: showProgress
|
||||
)
|
||||
@@ -719,22 +722,35 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func ownerChannelRelayBar(relays: [GroupRelay], activeCount: Int) -> some View {
|
||||
private func ownerChannelRelayBar(relays: [GroupRelay], activeCount: Int, failedCount: Int) -> some View {
|
||||
let total = relays.count
|
||||
let sorted = relays.sorted { relayDisplayName($0) < relayDisplayName($1) }
|
||||
return VStack(spacing: 0) {
|
||||
relayBarHeader {
|
||||
if activeCount < total {
|
||||
if activeCount + failedCount < total {
|
||||
RelayProgressIndicator(active: activeCount, total: total)
|
||||
}
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays active", comment: "channel relay bar progress"), activeCount, total))
|
||||
if failedCount > 0 {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays active, %d failed", comment: "channel relay bar progress with errors"), activeCount, total, failedCount))
|
||||
} else {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays active", comment: "channel relay bar progress"), activeCount, total))
|
||||
}
|
||||
}
|
||||
if relayListExpanded {
|
||||
ForEach(sorted) { relay in
|
||||
relayBarDetailRow {
|
||||
Text(relayDisplayName(relay)).foregroundColor(theme.colors.secondary)
|
||||
Spacer()
|
||||
relayStatusIndicator(relay.relayStatus)
|
||||
let failedErr = relayMemberConnFailed(relay)
|
||||
if let err = failedErr {
|
||||
Button {
|
||||
showAlert(
|
||||
NSLocalizedString("Relay connection failed", comment: "alert title"),
|
||||
message: err
|
||||
)
|
||||
} label: {
|
||||
ownerRelayDetailRow(relay, connFailed: true)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
} else {
|
||||
ownerRelayDetailRow(relay, connFailed: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -743,25 +759,32 @@ struct ComposeView: View {
|
||||
.animation(nil, value: relayListExpanded)
|
||||
}
|
||||
|
||||
private func ownerRelayDetailRow(_ relay: GroupRelay, connFailed: Bool) -> some View {
|
||||
relayBarDetailRow {
|
||||
Text(relayDisplayName(relay)).foregroundColor(theme.colors.secondary)
|
||||
Spacer()
|
||||
relayStatusIndicator(relay.relayStatus, connFailed: connFailed)
|
||||
}
|
||||
}
|
||||
|
||||
private func subscriberChannelRelayBar(
|
||||
hostnames: [String],
|
||||
relayMembers: [GMember],
|
||||
connectedCount: Int,
|
||||
deletedCount: Int,
|
||||
errorCount: Int,
|
||||
total: Int,
|
||||
showProgress: Bool
|
||||
) -> some View {
|
||||
VStack(spacing: 0) {
|
||||
relayBarHeader {
|
||||
let activeTotal = total - deletedCount
|
||||
if showProgress && connectedCount < activeTotal {
|
||||
RelayProgressIndicator(active: connectedCount, total: activeTotal)
|
||||
if showProgress && connectedCount + errorCount < total {
|
||||
RelayProgressIndicator(active: connectedCount, total: total)
|
||||
}
|
||||
if showProgress {
|
||||
if deletedCount > 0 {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays connected, %d deleted", comment: "channel subscriber relay bar progress with deleted"), connectedCount, activeTotal, deletedCount))
|
||||
if errorCount > 0 {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays connected, %d errors", comment: "channel subscriber relay bar progress with errors"), connectedCount, total, errorCount))
|
||||
} else {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays connected", comment: "channel subscriber relay bar progress"), connectedCount, activeTotal))
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays connected", comment: "channel subscriber relay bar progress"), connectedCount, total))
|
||||
}
|
||||
} else {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d relays", comment: "channel relay bar"), total))
|
||||
@@ -780,16 +803,19 @@ struct ComposeView: View {
|
||||
ForEach(relayMembers) { member in
|
||||
let m = member.wrapped
|
||||
let host = m.relayLink.map { hostFromRelayLink($0) }
|
||||
relayBarDetailRow {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("via %@", comment: "relay hostname"), host ?? m.chatViewName))
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
Spacer()
|
||||
let status = relayConnStatus(m)
|
||||
Circle()
|
||||
.fill(status.color)
|
||||
.frame(width: 8, height: 8)
|
||||
Text(status.text)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
let failedErr = m.activeConn?.connFailedErr
|
||||
if let err = failedErr {
|
||||
Button {
|
||||
showAlert(
|
||||
NSLocalizedString("Relay connection failed", comment: "alert title"),
|
||||
message: err
|
||||
)
|
||||
} label: {
|
||||
subscriberRelayDetailRow(m, host: host, connFailed: true)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
} else {
|
||||
subscriberRelayDetailRow(m, host: host, connFailed: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -799,6 +825,24 @@ struct ComposeView: View {
|
||||
.animation(nil, value: relayListExpanded)
|
||||
}
|
||||
|
||||
private func subscriberRelayDetailRow(_ m: GroupMember, host: String?, connFailed: Bool) -> some View {
|
||||
relayBarDetailRow {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("via %@", comment: "relay hostname"), host ?? m.chatViewName))
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
Spacer()
|
||||
let status = relayConnStatus(m)
|
||||
Circle()
|
||||
.fill(status.color)
|
||||
.frame(width: 8, height: 8)
|
||||
Text(status.text)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
if connFailed {
|
||||
Image(systemName: "exclamationmark.circle")
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func relayBarHeader<Content: View>(@ViewBuilder content: () -> Content) -> some View {
|
||||
Button {
|
||||
withAnimation(nil) { relayListExpanded.toggle() }
|
||||
@@ -830,6 +874,11 @@ struct ComposeView: View {
|
||||
.padding(.vertical, 2)
|
||||
}
|
||||
|
||||
private func relayMemberConnFailed(_ relay: GroupRelay) -> String? {
|
||||
chatModel.groupMembers.first(where: { $0.wrapped.groupMemberId == relay.groupMemberId })?
|
||||
.wrapped.activeConn?.connFailedErr
|
||||
}
|
||||
|
||||
private func connectButtonView(_ label: LocalizedStringKey, icon: String, connect: @escaping () -> Void) -> some View {
|
||||
Button(action: connect) {
|
||||
ZStack(alignment: .trailing) {
|
||||
|
||||
@@ -17,21 +17,20 @@ struct ChannelRelaysView: View {
|
||||
@State private var groupRelays: [GroupRelay] = []
|
||||
|
||||
var body: some View {
|
||||
let isOwner = groupInfo.isOwner
|
||||
List {
|
||||
relaysList(showRelayStatus: isOwner)
|
||||
relaysList()
|
||||
}
|
||||
.onAppear {
|
||||
Task {
|
||||
await chatModel.loadGroupMembers(groupInfo)
|
||||
if isOwner {
|
||||
if groupInfo.isOwner {
|
||||
groupRelays = await apiGetGroupRelays(groupInfo.groupId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func relaysList(showRelayStatus: Bool) -> some View {
|
||||
@ViewBuilder private func relaysList() -> some View {
|
||||
let relayMembers = chatModel.groupMembers.filter { $0.wrapped.memberRole == .relay }
|
||||
if relayMembers.isEmpty {
|
||||
Section {
|
||||
@@ -51,7 +50,10 @@ struct ChannelRelaysView: View {
|
||||
)
|
||||
.navigationBarHidden(false)
|
||||
} label: {
|
||||
relayMemberRow(member.wrapped, relayStatus: showRelayStatus ? relayStatusForMember(member.wrapped) : nil)
|
||||
let statusText = groupInfo.isOwner
|
||||
? ownerRelayStatusText(member.wrapped)
|
||||
: subscriberRelayStatusText(member.wrapped)
|
||||
relayMemberRow(member.wrapped, statusText: statusText)
|
||||
}
|
||||
}
|
||||
} footer: {
|
||||
@@ -60,28 +62,7 @@ struct ChannelRelaysView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func relayStatusForMember(_ member: GroupMember) -> RelayStatus? {
|
||||
groupRelays.first(where: { $0.groupMemberId == member.groupMemberId })?.relayStatus
|
||||
}
|
||||
|
||||
private func relayMemberRow(_ member: GroupMember, relayStatus: RelayStatus?) -> some View {
|
||||
HStack {
|
||||
MemberProfileImage(member, size: 38)
|
||||
.padding(.trailing, 2)
|
||||
VStack(alignment: .leading) {
|
||||
Text(member.chatViewName)
|
||||
.foregroundColor(theme.colors.onBackground)
|
||||
.lineLimit(1)
|
||||
Text(relayStatus?.text ?? relayConnStatusText(member))
|
||||
.lineLimit(1)
|
||||
.font(.caption)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
private func relayConnStatusText(_ member: GroupMember) -> LocalizedStringKey {
|
||||
private func subscriberRelayStatusText(_ member: GroupMember) -> LocalizedStringKey {
|
||||
if member.activeConn?.connDisabled ?? false {
|
||||
"disabled"
|
||||
} else if member.activeConn?.connInactive ?? false {
|
||||
@@ -90,13 +71,43 @@ struct ChannelRelaysView: View {
|
||||
relayConnStatus(member).text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func ownerRelayStatusText(_ member: GroupMember) -> LocalizedStringKey {
|
||||
if case .failed = member.activeConn?.connStatus {
|
||||
"failed"
|
||||
} else if member.activeConn?.connDisabled ?? false {
|
||||
"disabled"
|
||||
} else if member.activeConn?.connInactive ?? false {
|
||||
"inactive"
|
||||
} else {
|
||||
groupRelays.first(where: { $0.groupMemberId == member.groupMemberId })?.relayStatus.text
|
||||
?? relayConnStatus(member).text
|
||||
}
|
||||
}
|
||||
|
||||
private func relayMemberRow(_ member: GroupMember, statusText: LocalizedStringKey) -> some View {
|
||||
HStack {
|
||||
MemberProfileImage(member, size: 38)
|
||||
.padding(.trailing, 2)
|
||||
VStack(alignment: .leading) {
|
||||
Text(member.chatViewName)
|
||||
.foregroundColor(theme.colors.onBackground)
|
||||
.lineLimit(1)
|
||||
Text(statusText)
|
||||
.lineLimit(1)
|
||||
.font(.caption)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func relayConnStatus(_ member: GroupMember) -> (text: LocalizedStringKey, color: Color) {
|
||||
switch member.activeConn?.connStatus {
|
||||
case .ready: ("connected", .green)
|
||||
case .deleted: ("deleted", .red)
|
||||
case .failed: ("failed", .red)
|
||||
default: ("connecting", .yellow)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +236,14 @@ struct GroupMemberInfoView: View {
|
||||
|
||||
if let connFailedErr = member.activeConn?.connFailedErr {
|
||||
Section {
|
||||
infoRow("Connection failed", connFailedErr)
|
||||
Text(connFailedErr)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
} header: {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "exclamationmark.triangle")
|
||||
.foregroundColor(.red)
|
||||
Text("Connection failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -181,6 +181,7 @@ struct AddChannelView: View {
|
||||
}
|
||||
await MainActor.run {
|
||||
m.updateGroup(gInfo)
|
||||
m.creatingChannelId = gInfo.id
|
||||
groupInfo = gInfo
|
||||
groupLink = gLink
|
||||
groupRelays = gRelays.sorted { relayDisplayName($0) < relayDisplayName($1) }
|
||||
@@ -218,7 +219,8 @@ struct AddChannelView: View {
|
||||
// MARK: - Step 2: Progress
|
||||
|
||||
private func progressStepView(_ gInfo: GroupInfo) -> some View {
|
||||
let activeCount = groupRelays.filter { $0.relayStatus == .rsActive }.count
|
||||
let failedCount = groupRelays.filter { relayConnFailed($0) != nil }.count
|
||||
let activeCount = groupRelays.filter { $0.relayStatus == .rsActive && relayConnFailed($0) == nil }.count
|
||||
let total = groupRelays.count
|
||||
return List {
|
||||
Group {
|
||||
@@ -238,10 +240,14 @@ struct AddChannelView: View {
|
||||
withAnimation { relayListExpanded.toggle() }
|
||||
} label: {
|
||||
HStack(spacing: 8) {
|
||||
if activeCount < total {
|
||||
if activeCount + failedCount < total {
|
||||
RelayProgressIndicator(active: activeCount, total: total)
|
||||
}
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays active", comment: "channel creation progress"), activeCount, total))
|
||||
if failedCount > 0 {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays active, %d failed", comment: "channel creation progress with errors"), activeCount, total, failedCount))
|
||||
} else {
|
||||
Text(String.localizedStringWithFormat(NSLocalizedString("%d/%d relays active", comment: "channel creation progress"), activeCount, total))
|
||||
}
|
||||
Spacer()
|
||||
Image(systemName: relayListExpanded ? "chevron.up" : "chevron.down")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
@@ -251,10 +257,19 @@ struct AddChannelView: View {
|
||||
|
||||
if relayListExpanded {
|
||||
ForEach(groupRelays) { relay in
|
||||
HStack {
|
||||
Text(relayDisplayName(relay))
|
||||
Spacer()
|
||||
relayStatusIndicator(relay.relayStatus)
|
||||
let failed = relayConnFailed(relay)
|
||||
if let err = failed {
|
||||
Button {
|
||||
showAlert(
|
||||
NSLocalizedString("Relay connection failed", comment: "alert title"),
|
||||
message: err
|
||||
)
|
||||
} label: {
|
||||
relayRow(relay, connFailed: true)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
} else {
|
||||
relayRow(relay, connFailed: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,13 +281,21 @@ struct AddChannelView: View {
|
||||
if activeCount >= total {
|
||||
showLinkStep = true
|
||||
} else if activeCount > 0 {
|
||||
let actions: [UIAlertAction] = if activeCount + failedCount < total {
|
||||
[
|
||||
UIAlertAction(title: NSLocalizedString("Proceed", comment: "alert action"), style: .default) { _ in showLinkStep = true },
|
||||
UIAlertAction(title: NSLocalizedString("Wait", comment: "alert action"), style: .cancel) { _ in }
|
||||
]
|
||||
} else {
|
||||
[
|
||||
UIAlertAction(title: NSLocalizedString("Proceed", comment: "alert action"), style: .default) { _ in showLinkStep = true },
|
||||
cancelAlertAction
|
||||
]
|
||||
}
|
||||
showAlert(
|
||||
NSLocalizedString("Not all relays connected", comment: "alert title"),
|
||||
message: String.localizedStringWithFormat(NSLocalizedString("Channel will start working with %d of %d relays. Proceed?", comment: "alert message"), activeCount, total),
|
||||
actions: {[
|
||||
UIAlertAction(title: NSLocalizedString("Proceed", comment: "alert action"), style: .default) { _ in showLinkStep = true },
|
||||
UIAlertAction(title: NSLocalizedString("Wait", comment: "alert action"), style: .cancel) { _ in }
|
||||
]}
|
||||
actions: { actions }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -289,13 +312,26 @@ struct AddChannelView: View {
|
||||
.onChange(of: channelRelaysModel.groupRelays) { relays in
|
||||
guard channelRelaysModel.groupId == gInfo.groupId else { return }
|
||||
groupRelays = relays.sorted { relayDisplayName($0) < relayDisplayName($1) }
|
||||
if relays.allSatisfy({ $0.relayStatus == .rsActive }) {
|
||||
if relays.allSatisfy({ $0.relayStatus == .rsActive && relayConnFailed($0) == nil }) {
|
||||
showLinkStep = true
|
||||
channelRelaysModel.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func relayConnFailed(_ relay: GroupRelay) -> String? {
|
||||
m.groupMembers.first(where: { $0.wrapped.groupMemberId == relay.groupMemberId })?
|
||||
.wrapped.activeConn?.connFailedErr
|
||||
}
|
||||
|
||||
private func relayRow(_ relay: GroupRelay, connFailed: Bool) -> some View {
|
||||
HStack {
|
||||
Text(relayDisplayName(relay))
|
||||
Spacer()
|
||||
relayStatusIndicator(relay.relayStatus, connFailed: connFailed)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Step 3: Link
|
||||
|
||||
private func linkStepView(_ gInfo: GroupInfo) -> some View {
|
||||
@@ -307,6 +343,7 @@ struct AddChannelView: View {
|
||||
creatingGroup: true,
|
||||
isChannel: true
|
||||
) {
|
||||
m.creatingChannelId = nil
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
dismissAllSheets(animated: true) {
|
||||
ItemsModel.shared.loadOpenChat(gInfo.id)
|
||||
@@ -317,6 +354,7 @@ struct AddChannelView: View {
|
||||
}
|
||||
|
||||
private func cancelChannelCreation(_ gInfo: GroupInfo) {
|
||||
m.creatingChannelId = nil
|
||||
channelRelaysModel.reset()
|
||||
dismissAllSheets(animated: true)
|
||||
Task {
|
||||
@@ -358,14 +396,21 @@ func relayDisplayName(_ relay: GroupRelay) -> String {
|
||||
return "relay \(relay.groupRelayId)"
|
||||
}
|
||||
|
||||
func relayStatusIndicator(_ status: RelayStatus) -> some View {
|
||||
HStack(spacing: 4) {
|
||||
func relayStatusIndicator(_ status: RelayStatus, connFailed: Bool = false) -> some View {
|
||||
let color: Color = connFailed ? .red : (status == .rsActive ? .green : .orange)
|
||||
let text: LocalizedStringKey = connFailed ? "failed" : status.text
|
||||
return HStack(spacing: 4) {
|
||||
Circle()
|
||||
.fill(status == .rsActive ? .green : status == .rsNew ? .red : .orange)
|
||||
.fill(color)
|
||||
.frame(width: 8, height: 8)
|
||||
Text(status.text)
|
||||
Text(text)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
if connFailed {
|
||||
Image(systemName: "exclamationmark.circle")
|
||||
.foregroundColor(.accentColor)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2571,10 +2571,10 @@ public struct GroupRelay: Identifiable, Decodable, Equatable, Hashable {
|
||||
extension RelayStatus {
|
||||
public var text: LocalizedStringKey {
|
||||
switch self {
|
||||
case .rsNew: "New"
|
||||
case .rsInvited: "Invited"
|
||||
case .rsAccepted: "Accepted"
|
||||
case .rsActive: "Active"
|
||||
case .rsNew: "new"
|
||||
case .rsInvited: "invited"
|
||||
case .rsAccepted: "accepted"
|
||||
case .rsActive: "active"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,7 +728,7 @@ updatePreparedUserAndHostMembers'
|
||||
db
|
||||
vr
|
||||
user
|
||||
gInfo@GroupInfo {groupId, groupProfile = gp, businessChat}
|
||||
gInfo@GroupInfo {groupId, membership, groupProfile = gp, businessChat}
|
||||
hostMember
|
||||
fromMember
|
||||
fromMemberProfile
|
||||
@@ -737,7 +737,9 @@ updatePreparedUserAndHostMembers'
|
||||
business
|
||||
membershipStatus = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
liftIO $ updateUserMember currentTs
|
||||
-- For channels, don't regress membership status if already connected via another relay
|
||||
unless (memberStatus membership == GSMemConnected) $
|
||||
liftIO $ updateUserMember currentTs
|
||||
hostMember' <- updateHostMember currentTs
|
||||
when (gp /= groupProfile) $
|
||||
void $ updateGroupProfile db user gInfo groupProfile
|
||||
@@ -747,8 +749,7 @@ updatePreparedUserAndHostMembers'
|
||||
pure (gInfo', hostMember')
|
||||
where
|
||||
updateUserMember currentTs = do
|
||||
let GroupInfo {membership} = gInfo
|
||||
MemberIdRole memberId memberRole = invitedMember
|
||||
let MemberIdRole memberId memberRole = invitedMember
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
|
||||
Reference in new issue
Block a user