Compare commits

..

2 Commits

Author SHA1 Message Date
0dd8241958 aaaaaaa 2025-10-08 01:31:39 +03:00
e6a9948495 add privacy 2025-10-08 01:08:48 +03:00
3 changed files with 269 additions and 16 deletions

View File

@ -134,17 +134,20 @@
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "needs_review",
"value" : "Yobble" "value" : "Yobble"
} }
}, },
"ru" : { "ru" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "translated",
"value" : "Йоббле" "value" : "Йоббл"
} }
} }
} }
},
"Автоудаление аккаунта" : {
}, },
"Активные сессии" : { "Активные сессии" : {
"localizations" : { "localizations" : {
@ -164,6 +167,15 @@
}, },
"Ваше предложение" : { "Ваше предложение" : {
},
"Видимость и контент" : {
},
"Видимость статуса 'был в сети'" : {
},
"Включить автоудаление аккаунта" : {
}, },
"Вложение" : { "Вложение" : {
@ -344,17 +356,6 @@
"Здесь появится информация о собеседнике и существующих чатах." : { "Здесь появится информация о собеседнике и существующих чатах." : {
"comment" : "Search placeholder description" "comment" : "Search placeholder description"
}, },
"Идеи" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ideas"
}
}
}
},
"Избранные сообщения" : { "Избранные сообщения" : {
}, },
@ -415,6 +416,16 @@
"Кликер в разработке" : { "Кликер в разработке" : {
"comment" : "Concept tab placeholder title" "comment" : "Concept tab placeholder title"
}, },
"Конфиденциальность" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Privacy"
}
}
}
},
"Концепт" : { "Концепт" : {
"comment" : "Tab bar: concept clicker" "comment" : "Tab bar: concept clicker"
}, },
@ -439,6 +450,15 @@
} }
} }
} }
},
"Кто может звонить" : {
},
"Кто может приглашать в беседы" : {
},
"Кто может приглашать в паблики" : {
}, },
"Лента" : { "Лента" : {
"localizations" : { "localizations" : {
@ -547,6 +567,9 @@
} }
} }
} }
},
"Настройки приватности" : {
}, },
"Не удалось выполнить поиск." : { "Не удалось выполнить поиск." : {
"comment" : "Search error fallback\nSearch service decoding error" "comment" : "Search error fallback\nSearch service decoding error"
@ -995,6 +1018,15 @@
} }
} }
} }
},
"Показывать био не-контактам" : {
},
"Показывать сторисы не-контактам" : {
},
"Показывать фото не-контактам" : {
}, },
"Пользователь Системы 1" : { "Пользователь Системы 1" : {
"comment" : "Тестовая подмена офф аккаунта", "comment" : "Тестовая подмена офф аккаунта",
@ -1051,6 +1083,9 @@
} }
} }
} }
},
"Приглашения и звонки" : {
}, },
"Приложение" : { "Приложение" : {
@ -1064,6 +1099,12 @@
} }
} }
} }
},
"Принимать сообщения от незнакомцев" : {
},
"Принудительное автоудаление в ЛС (Приватный)" : {
}, },
"Проверьте данные и повторите попытку." : { "Проверьте данные и повторите попытку." : {
"localizations" : { "localizations" : {
@ -1088,11 +1129,33 @@
} }
} }
}, },
"Профиль" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Profile"
}
}
}
},
"Профиль в разработке" : { "Профиль в разработке" : {
"comment" : "Search placeholder title" "comment" : "Search placeholder title"
},
"Профиль и поиск" : {
}, },
"Публичная информация" : { "Публичная информация" : {
},
"Разрешить пересылку сообщений" : {
},
"Разрешить поиск профиля" : {
},
"Разрешить хранить чаты на сервере (Обычный)" : {
}, },
"Регистрация" : { "Регистрация" : {
"comment" : "Регистрация", "comment" : "Регистрация",
@ -1154,6 +1217,9 @@
} }
} }
} }
},
"Сбросить по умолчанию" : {
}, },
"Светлая" : { "Светлая" : {
"localizations" : { "localizations" : {
@ -1257,6 +1323,9 @@
}, },
"Сообщение" : { "Сообщение" : {
},
"Сохранить изменения" : {
}, },
"Спасибо!" : { "Спасибо!" : {
@ -1281,6 +1350,9 @@
} }
} }
} }
},
"Таймер автоудаления: %@" : {
}, },
"Тёмная" : { "Тёмная" : {
"localizations" : { "localizations" : {
@ -1341,6 +1413,9 @@
} }
} }
} }
},
"Удалять аккаунт через %lld дн." : {
}, },
"Ура!" : { "Ура!" : {
"localizations" : { "localizations" : {
@ -1393,6 +1468,9 @@
} }
} }
} }
},
"Чаты и хранение" : {
}, },
"Черновики" : { "Черновики" : {
"comment" : "Drafts", "comment" : "Drafts",

View File

@ -0,0 +1,171 @@
import SwiftUI
struct EditPrivacyView: View {
@State private var profilePermissions = ProfilePermissionsResponse()
private var privacyScopeOptions: [PrivacyScope] { PrivacyScope.allCases }
private var forceAutoDeleteBinding: Binding<Int> {
Binding(
get: { profilePermissions.maxMessageAutoDeleteSeconds ?? 30 },
set: { profilePermissions.maxMessageAutoDeleteSeconds = $0 }
)
}
private var autoDeleteAccountEnabled: Binding<Bool> {
Binding(
get: { profilePermissions.autoDeleteAfterDays != nil },
set: { newValue in
profilePermissions.autoDeleteAfterDays = newValue ? (profilePermissions.autoDeleteAfterDays ?? 30) : nil
}
)
}
private var autoDeleteAccountBinding: Binding<Int> {
Binding(
get: { profilePermissions.autoDeleteAfterDays ?? 30 },
set: { profilePermissions.autoDeleteAfterDays = min(max($0, 1), 365) }
)
}
var body: some View {
Form {
Section(header: Text("Профиль и поиск")) {
Toggle("Разрешить поиск профиля", isOn: $profilePermissions.isSearchable)
Toggle("Разрешить пересылку сообщений", isOn: $profilePermissions.allowMessageForwarding)
Toggle("Принимать сообщения от незнакомцев", isOn: $profilePermissions.allowMessagesFromNonContacts)
}
Section(header: Text("Видимость и контент")) {
Toggle("Показывать фото не-контактам", isOn: $profilePermissions.showProfilePhotoToNonContacts)
Toggle("Показывать био не-контактам", isOn: $profilePermissions.showBioToNonContacts)
Toggle("Показывать сторисы не-контактам", isOn: $profilePermissions.showStoriesToNonContacts)
Picker("Видимость статуса 'был в сети'", selection: $profilePermissions.lastSeenVisibility) {
ForEach(privacyScopeOptions) { scope in
Text(scope.title).tag(scope.rawValue)
}
}
.pickerStyle(.segmented)
}
Section(header: Text("Приглашения и звонки")) {
Picker("Кто может приглашать в паблики", selection: $profilePermissions.publicInvitePermission) {
ForEach(privacyScopeOptions) { scope in
Text(scope.title).tag(scope.rawValue)
}
}
.pickerStyle(.segmented)
Picker("Кто может приглашать в беседы", selection: $profilePermissions.groupInvitePermission) {
ForEach(privacyScopeOptions) { scope in
Text(scope.title).tag(scope.rawValue)
}
}
.pickerStyle(.segmented)
Picker("Кто может звонить", selection: $profilePermissions.callPermission) {
ForEach(privacyScopeOptions) { scope in
Text(scope.title).tag(scope.rawValue)
}
}
.pickerStyle(.segmented)
}
Section(header: Text("Чаты и хранение")) {
Toggle("Разрешить хранить чаты на сервере (Обычный)", isOn: $profilePermissions.allowServerChats)
Toggle("Принудительное автоудаление в ЛС (Приватный)", isOn: $profilePermissions.forceAutoDeleteMessagesInPrivate)
if profilePermissions.forceAutoDeleteMessagesInPrivate {
Stepper(value: forceAutoDeleteBinding, in: 5...86400, step: 5) {
Text("Таймер автоудаления: \(formattedAutoDeleteSeconds(forceAutoDeleteBinding.wrappedValue))")
}
}
}
Section(header: Text("Автоудаление аккаунта")) {
Toggle("Включить автоудаление аккаунта", isOn: autoDeleteAccountEnabled)
if autoDeleteAccountEnabled.wrappedValue {
Stepper(value: autoDeleteAccountBinding, in: 1...365) {
Text("Удалять аккаунт через \(autoDeleteAccountBinding.wrappedValue) дн.")
}
}
}
Section {
Button("Сохранить изменения") {
print("Параметры приватности: \(profilePermissions)")
}
.frame(maxWidth: .infinity, alignment: .center)
}
Section {
Button(role: .destructive) {
profilePermissions = ProfilePermissionsResponse()
print("Настройки приватности сброшены к значениям по умолчанию")
} label: {
Text("Сбросить по умолчанию")
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
.navigationTitle("Настройки приватности")
.onChange(of: profilePermissions.forceAutoDeleteMessagesInPrivate) { newValue in
if newValue {
profilePermissions.maxMessageAutoDeleteSeconds = profilePermissions.maxMessageAutoDeleteSeconds ?? 30
} else {
profilePermissions.maxMessageAutoDeleteSeconds = nil
}
}
}
private func formattedAutoDeleteSeconds(_ value: Int) -> String {
switch value {
case ..<60:
return "\(value) сек."
case 60..<3600:
let minutes = value / 60
return "\(minutes) мин."
default:
let hours = Double(value) / 3600.0
return String(format: "%.1f ч.", hours)
}
}
}
private enum PrivacyScope: Int, CaseIterable, Identifiable {
case everyone = 0
case contacts = 1
case nobody = 2
var id: Int { rawValue }
var title: String {
switch self {
case .everyone:
return "Все"
case .contacts:
return "Контакты"
case .nobody:
return "Никто"
}
}
}
struct ProfilePermissionsResponse: Codable {
var isSearchable: Bool = true
var allowMessageForwarding: Bool = true
var allowMessagesFromNonContacts: Bool = true
var showProfilePhotoToNonContacts: Bool = true
var lastSeenVisibility: Int = PrivacyScope.everyone.rawValue
var showBioToNonContacts: Bool = true
var showStoriesToNonContacts: Bool = true
var allowServerChats: Bool = true
var publicInvitePermission: Int = PrivacyScope.everyone.rawValue
var groupInvitePermission: Int = PrivacyScope.everyone.rawValue
var callPermission: Int = PrivacyScope.everyone.rawValue
var forceAutoDeleteMessagesInPrivate: Bool = false
var maxMessageAutoDeleteSeconds: Int? = nil
var autoDeleteAfterDays: Int? = nil
}

View File

@ -13,12 +13,16 @@ struct SettingsView: View {
var body: some View { var body: some View {
Form { Form {
// // MARK: - Профиль // MARK: - Профиль
// Section(header: Text("Профиль")) { Section(header: Text(NSLocalizedString("Профиль", comment: ""))) {
// NavigationLink(destination: EditProfileView()) { // NavigationLink(destination: EditProfileView()) {
// Label("Мой профиль", systemImage: "person.crop.circle") // Label("Мой профиль", systemImage: "person.crop.circle")
// } // }
// }
NavigationLink(destination: EditPrivacyView()) {
Label(NSLocalizedString("Конфиденциальность", comment: ""), systemImage: "lock.fill")
}
}
// MARK: - Безопасность // MARK: - Безопасность
Section(header: Text(NSLocalizedString("Безопасность", comment: ""))) { Section(header: Text(NSLocalizedString("Безопасность", comment: ""))) {