Fix profile picker
This commit is contained in:
@@ -246,7 +246,13 @@ import SwiftUI
|
|||||||
#endif
|
#endif
|
||||||
ToolbarItemGroup(placement: .topBarTrailing) {
|
ToolbarItemGroup(placement: .topBarTrailing) {
|
||||||
if #available(iOS 16.0, tvOS 17.0, *) {
|
if #available(iOS 16.0, tvOS 17.0, *) {
|
||||||
cardManagementButton
|
#if os(iOS)
|
||||||
|
if !useLegacyTabView || coordinator.selection == .overview {
|
||||||
|
cardManagementButton
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
cardManagementButton
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
StartStopButton()
|
StartStopButton()
|
||||||
@@ -288,10 +294,14 @@ import SwiftUI
|
|||||||
|
|
||||||
@available(iOS 16.0, *) @ViewBuilder private var cardManagementButton: some View {
|
@available(iOS 16.0, *) @ViewBuilder private var cardManagementButton: some View {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
Button {
|
Menu {
|
||||||
showCardManagement = true
|
Button {
|
||||||
|
showCardManagement = true
|
||||||
|
} label: {
|
||||||
|
Label("Dashboard Items", systemImage: "square.grid.2x2")
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Label("Dashboard Items", systemImage: "square.grid.2x2")
|
Label("Others", systemImage: "line.3.horizontal.circle")
|
||||||
}
|
}
|
||||||
#elseif os(tvOS)
|
#elseif os(tvOS)
|
||||||
Button {
|
Button {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ struct ProfilePickerSheet: View {
|
|||||||
@Binding var profileList: [ProfilePreview]
|
@Binding var profileList: [ProfilePreview]
|
||||||
@Binding var selectedProfileID: Int64
|
@Binding var selectedProfileID: Int64
|
||||||
|
|
||||||
#if os(iOS) || os(tvOS)
|
#if os(iOS)
|
||||||
|
@State private var legacyEditMode: EditMode = .inactive
|
||||||
|
#elseif os(tvOS)
|
||||||
@State private var editMode: EditMode = .inactive
|
@State private var editMode: EditMode = .inactive
|
||||||
#else
|
#else
|
||||||
@State private var isEditing = false
|
@State private var isEditing = false
|
||||||
@@ -26,7 +28,9 @@ struct ProfilePickerSheet: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private var isEditingActive: Bool {
|
private var isEditingActive: Bool {
|
||||||
#if os(iOS) || os(tvOS)
|
#if os(iOS)
|
||||||
|
legacyEditMode.isEditing
|
||||||
|
#elseif os(tvOS)
|
||||||
editMode.isEditing
|
editMode.isEditing
|
||||||
#else
|
#else
|
||||||
isEditing
|
isEditing
|
||||||
@@ -34,65 +38,44 @@ struct ProfilePickerSheet: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
listContent
|
#if os(iOS)
|
||||||
#if os(iOS) || os(tvOS)
|
if #available(iOS 26, *) {
|
||||||
.environment(\.editMode, $editMode)
|
iOSBody
|
||||||
#endif
|
} else {
|
||||||
#if os(tvOS)
|
legacyIOSBody
|
||||||
.navigationDestination(item: $profileToEdit) { profile in
|
|
||||||
EditProfileView()
|
|
||||||
.environmentObject(profile)
|
|
||||||
.environmentObject(environments)
|
|
||||||
.toolbar {
|
|
||||||
ToolbarItemGroup(placement: .topBarLeading) {
|
|
||||||
BackButton()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elseif os(macOS)
|
|
||||||
.safeAreaInset(edge: .bottom) {
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
Divider()
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
Button("Cancel") {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
.keyboardShortcut(.escape, modifiers: [])
|
|
||||||
if isEditing {
|
|
||||||
Button("Done") {
|
|
||||||
withAnimation {
|
|
||||||
isEditing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.buttonStyle(.borderedProminent)
|
|
||||||
} else {
|
|
||||||
Button("Edit") {
|
|
||||||
withAnimation {
|
|
||||||
isEditing = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.buttonStyle(.borderedProminent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.background(Color(NSColor.controlBackgroundColor))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.sheet(item: $profileToEdit) { profile in
|
|
||||||
NavigationSheet {
|
|
||||||
EditProfileView()
|
|
||||||
.environmentObject(profile)
|
|
||||||
.environmentObject(environments)
|
|
||||||
}
|
|
||||||
.frame(minWidth: 500, minHeight: 400)
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
.toolbar {
|
nonIOSBody
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@available(iOS 26, *)
|
||||||
|
private var iOSBody: some View {
|
||||||
|
listContent
|
||||||
|
.toolbar {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
Button(editMode.isEditing ? "Done" : "Edit") {
|
EditButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(item: $profileToEdit) { profile in
|
||||||
|
NavigationSheet(title: "Edit Profile") {
|
||||||
|
EditProfileView()
|
||||||
|
.environmentObject(profile)
|
||||||
|
.environmentObject(environments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alert($alert)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var legacyIOSBody: some View {
|
||||||
|
legacyListContent
|
||||||
|
.environment(\.editMode, $legacyEditMode)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .primaryAction) {
|
||||||
|
Button(legacyEditMode.isEditing ? "Done" : "Edit") {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
editMode = editMode.isEditing ? .inactive : .active
|
legacyEditMode = legacyEditMode.isEditing ? .inactive : .active
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,9 +87,67 @@ struct ProfilePickerSheet: View {
|
|||||||
.environmentObject(environments)
|
.environmentObject(environments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
.alert($alert)
|
.alert($alert)
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !os(iOS)
|
||||||
|
private var nonIOSBody: some View {
|
||||||
|
listContent
|
||||||
|
#if os(tvOS)
|
||||||
|
.environment(\.editMode, $editMode)
|
||||||
|
.navigationDestination(item: $profileToEdit) { profile in
|
||||||
|
EditProfileView()
|
||||||
|
.environmentObject(profile)
|
||||||
|
.environmentObject(environments)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
BackButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elseif os(macOS)
|
||||||
|
.safeAreaInset(edge: .bottom) {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
Divider()
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Button("Cancel") {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
.keyboardShortcut(.escape, modifiers: [])
|
||||||
|
if isEditing {
|
||||||
|
Button("Done") {
|
||||||
|
withAnimation {
|
||||||
|
isEditing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
} else {
|
||||||
|
Button("Edit") {
|
||||||
|
withAnimation {
|
||||||
|
isEditing = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.background(Color(NSColor.controlBackgroundColor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(item: $profileToEdit) { profile in
|
||||||
|
NavigationSheet {
|
||||||
|
EditProfileView()
|
||||||
|
.environmentObject(profile)
|
||||||
|
.environmentObject(environments)
|
||||||
|
}
|
||||||
|
.frame(minWidth: 500, minHeight: 400)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
.alert($alert)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private var listContent: some View {
|
private var listContent: some View {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
@@ -150,7 +191,6 @@ struct ProfilePickerSheet: View {
|
|||||||
ProfilePickerRow(
|
ProfilePickerRow(
|
||||||
profile: profile,
|
profile: profile,
|
||||||
isSelected: profile.id == selectedProfileID,
|
isSelected: profile.id == selectedProfileID,
|
||||||
isEditing: isEditingActive,
|
|
||||||
alert: $alert,
|
alert: $alert,
|
||||||
onSelect: {
|
onSelect: {
|
||||||
selectedProfileID = profile.id
|
selectedProfileID = profile.id
|
||||||
@@ -167,12 +207,99 @@ struct ProfilePickerSheet: View {
|
|||||||
}
|
}
|
||||||
.onMove(perform: moveProfile)
|
.onMove(perform: moveProfile)
|
||||||
.onDelete(perform: deleteProfile)
|
.onDelete(perform: deleteProfile)
|
||||||
.moveDisabled(!isEditingActive)
|
|
||||||
.deleteDisabled(!isEditingActive)
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@ViewBuilder
|
||||||
|
private var legacyListContent: some View {
|
||||||
|
if legacyEditMode.isEditing {
|
||||||
|
legacyEditingList
|
||||||
|
} else {
|
||||||
|
legacyNormalList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var legacyEditingList: some View {
|
||||||
|
List {
|
||||||
|
ForEach(profileList, id: \.id) { profile in
|
||||||
|
LegacyProfilePickerRow(
|
||||||
|
profile: profile,
|
||||||
|
isSelected: profile.id == selectedProfileID,
|
||||||
|
alert: $alert,
|
||||||
|
onSelect: {
|
||||||
|
selectedProfileID = profile.id
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
onEdit: {
|
||||||
|
profileToEdit = profile.origin
|
||||||
|
},
|
||||||
|
onUpdate: {
|
||||||
|
await updateProfile(profile)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.environmentObject(environments)
|
||||||
|
}
|
||||||
|
.onMove(perform: legacyMoveProfile)
|
||||||
|
.onDelete(perform: legacyDeleteProfile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var legacyNormalList: some View {
|
||||||
|
List {
|
||||||
|
ForEach(profileList, id: \.id) { profile in
|
||||||
|
LegacyProfilePickerRow(
|
||||||
|
profile: profile,
|
||||||
|
isSelected: profile.id == selectedProfileID,
|
||||||
|
alert: $alert,
|
||||||
|
onSelect: {
|
||||||
|
selectedProfileID = profile.id
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
onEdit: {
|
||||||
|
profileToEdit = profile.origin
|
||||||
|
},
|
||||||
|
onUpdate: {
|
||||||
|
await updateProfile(profile)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.environmentObject(environments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func legacyMoveProfile(from source: IndexSet, to destination: Int) {
|
||||||
|
profileList.move(fromOffsets: source, toOffset: destination)
|
||||||
|
for (index, profile) in profileList.enumerated() {
|
||||||
|
profileList[index].order = UInt32(index)
|
||||||
|
profile.origin.order = UInt32(index)
|
||||||
|
}
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await ProfileManager.update(profileList.map(\.origin))
|
||||||
|
environments.profileUpdate.send()
|
||||||
|
} catch {
|
||||||
|
// Handle error silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func legacyDeleteProfile(at offsets: IndexSet) {
|
||||||
|
let profilesToDelete = offsets.map { profileList[$0].origin }
|
||||||
|
profileList.remove(atOffsets: offsets)
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
_ = try await ProfileManager.delete(profilesToDelete)
|
||||||
|
environments.emptyProfiles = profileList.isEmpty
|
||||||
|
environments.profileUpdate.send()
|
||||||
|
} catch {
|
||||||
|
// Handle error silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private func updateProfile(_ profile: ProfilePreview) async {
|
private func updateProfile(_ profile: ProfilePreview) async {
|
||||||
do {
|
do {
|
||||||
try await profile.origin.updateRemoteProfile()
|
try await profile.origin.updateRemoteProfile()
|
||||||
@@ -273,10 +400,15 @@ struct ProfilePickerSheet: View {
|
|||||||
|
|
||||||
private struct ProfilePickerRow: View {
|
private struct ProfilePickerRow: View {
|
||||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||||
|
#if os(iOS)
|
||||||
|
@Environment(\.editMode) private var editMode
|
||||||
|
#endif
|
||||||
|
|
||||||
let profile: ProfilePreview
|
let profile: ProfilePreview
|
||||||
let isSelected: Bool
|
let isSelected: Bool
|
||||||
let isEditing: Bool
|
#if os(tvOS) || os(macOS)
|
||||||
|
let isEditing: Bool
|
||||||
|
#endif
|
||||||
@Binding var alert: AlertState?
|
@Binding var alert: AlertState?
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
var focusedProfileID: FocusState<Int64?>.Binding
|
var focusedProfileID: FocusState<Int64?>.Binding
|
||||||
@@ -288,6 +420,12 @@ private struct ProfilePickerRow: View {
|
|||||||
let onDelete: () -> Void
|
let onDelete: () -> Void
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
private var isEditing: Bool {
|
||||||
|
editMode?.wrappedValue.isEditing ?? false
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
@State private var isUpdating = false
|
@State private var isUpdating = false
|
||||||
@State private var showQRCode = false
|
@State private var showQRCode = false
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
@@ -462,10 +600,12 @@ private struct ProfilePickerRow: View {
|
|||||||
}
|
}
|
||||||
.frame(width: 16)
|
.frame(width: 16)
|
||||||
#else
|
#else
|
||||||
Image(systemName: "checkmark")
|
if !isEditing {
|
||||||
.font(.system(size: 16, weight: .semibold))
|
Image(systemName: "checkmark")
|
||||||
.foregroundStyle(.tint)
|
.font(.system(size: 16, weight: .semibold))
|
||||||
.opacity(isSelected ? 1 : 0)
|
.foregroundStyle(.tint)
|
||||||
|
.opacity(isSelected ? 1 : 0)
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
@@ -697,3 +837,173 @@ private struct ProfilePickerRow: View {
|
|||||||
func updateNSView(_: NSView, context _: Context) {}
|
func updateNSView(_: NSView, context _: Context) {}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// MARK: - Legacy iOS ProfilePickerRow (iOS < 26)
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
private struct LegacyProfilePickerRow: View {
|
||||||
|
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||||
|
@Environment(\.editMode) private var editMode
|
||||||
|
|
||||||
|
let profile: ProfilePreview
|
||||||
|
let isSelected: Bool
|
||||||
|
@Binding var alert: AlertState?
|
||||||
|
let onSelect: () -> Void
|
||||||
|
let onEdit: () -> Void
|
||||||
|
let onUpdate: () async -> Void
|
||||||
|
|
||||||
|
private var isEditing: Bool {
|
||||||
|
editMode?.wrappedValue.isEditing ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
@State private var isUpdating = false
|
||||||
|
@State private var showQRCode = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
if isEditing {
|
||||||
|
editingBody
|
||||||
|
} else {
|
||||||
|
normalBody
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.transaction { $0.animation = nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
private var editingBody: some View {
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
Text(profile.name)
|
||||||
|
.font(.body)
|
||||||
|
.foregroundStyle(.primary)
|
||||||
|
profileInfo
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var normalBody: some View {
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
Button {
|
||||||
|
if !isUpdating {
|
||||||
|
onSelect()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
.font(.system(size: 16, weight: .semibold))
|
||||||
|
.foregroundStyle(.tint)
|
||||||
|
.opacity(isSelected ? 1 : 0)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
Text(profile.name)
|
||||||
|
.font(.body)
|
||||||
|
.foregroundStyle(.primary)
|
||||||
|
profileInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.disabled(isUpdating)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
rowMenu
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showQRCode) {
|
||||||
|
if let remoteURL = profile.remoteURL {
|
||||||
|
QRCodeSheet(profileName: profile.name, remoteURL: remoteURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var rowMenu: some View {
|
||||||
|
Menu {
|
||||||
|
Button {
|
||||||
|
onEdit()
|
||||||
|
} label: {
|
||||||
|
Label("Edit", systemImage: "pencil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile.type == .remote {
|
||||||
|
Button {
|
||||||
|
isUpdating = true
|
||||||
|
Task {
|
||||||
|
await onUpdate()
|
||||||
|
isUpdating = false
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Update", systemImage: "arrow.clockwise")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shareMenu
|
||||||
|
} label: {
|
||||||
|
Group {
|
||||||
|
if isUpdating {
|
||||||
|
ProgressView()
|
||||||
|
.scaleEffect(0.8)
|
||||||
|
} else {
|
||||||
|
Image(systemName: "ellipsis")
|
||||||
|
.font(.system(size: 16))
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(width: 32, height: 32)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var shareMenu: some View {
|
||||||
|
Menu {
|
||||||
|
ShareButtonCompat($alert) {
|
||||||
|
Label("Share File", systemImage: "doc")
|
||||||
|
} itemURL: {
|
||||||
|
try profile.origin.toContent().generateShareFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile.type == .remote {
|
||||||
|
Button {
|
||||||
|
showQRCode = true
|
||||||
|
} label: {
|
||||||
|
Label("Share URL as QR Code", systemImage: "qrcode")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShareButtonCompat($alert) {
|
||||||
|
Label("Share Content JSON File", systemImage: "curlybraces")
|
||||||
|
} itemURL: {
|
||||||
|
try profile.origin.read().generateShareFile(name: "\(profile.name).json")
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Share", systemImage: "square.and.arrow.up")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var profileInfo: some View {
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Image(systemName: profile.type == .remote ? "cloud.fill" : "doc.fill")
|
||||||
|
.font(.system(size: 12))
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
Text(profile.type == .remote ? "Remote" : "Local")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile.type == .remote, let lastUpdated = profile.lastUpdated {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Image(systemName: "clock.fill")
|
||||||
|
.font(.system(size: 12))
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
Text(lastUpdated.myFormat)
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user