diff --git a/yobble/Resources/Localizable.xcstrings b/yobble/Resources/Localizable.xcstrings index b224649..00bf942 100644 --- a/yobble/Resources/Localizable.xcstrings +++ b/yobble/Resources/Localizable.xcstrings @@ -134,17 +134,20 @@ "localizations" : { "en" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Yobble" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Йоббле" + "value" : "Йоббл" } } } + }, + "Автоудаление аккаунта" : { + }, "Активные сессии" : { "localizations" : { @@ -164,6 +167,15 @@ }, "Ваше предложение" : { + }, + "Видимость и контент" : { + + }, + "Видимость статуса 'был в сети'" : { + + }, + "Включить автоудаление аккаунта" : { + }, "Вложение" : { @@ -344,17 +356,6 @@ "Здесь появится информация о собеседнике и существующих чатах." : { "comment" : "Search placeholder description" }, - "Идеи" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ideas" - } - } - } - }, "Избранные сообщения" : { }, @@ -415,6 +416,16 @@ "Кликер в разработке" : { "comment" : "Concept tab placeholder title" }, + "Конфиденциальность" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacy" + } + } + } + }, "Концепт" : { "comment" : "Tab bar: concept clicker" }, @@ -439,6 +450,15 @@ } } } + }, + "Кто может звонить" : { + + }, + "Кто может приглашать в беседы" : { + + }, + "Кто может приглашать в паблики" : { + }, "Лента" : { "localizations" : { @@ -547,6 +567,9 @@ } } } + }, + "Настройки приватности" : { + }, "Не удалось выполнить поиск." : { "comment" : "Search error fallback\nSearch service decoding error" @@ -995,6 +1018,15 @@ } } } + }, + "Показывать био не-контактам" : { + + }, + "Показывать сторисы не-контактам" : { + + }, + "Показывать фото не-контактам" : { + }, "Пользователь Системы 1" : { "comment" : "Тестовая подмена офф аккаунта", @@ -1051,6 +1083,9 @@ } } } + }, + "Приглашения и звонки" : { + }, "Приложение" : { @@ -1064,6 +1099,12 @@ } } } + }, + "Принимать сообщения от незнакомцев" : { + + }, + "Принудительное автоудаление в ЛС" : { + }, "Проверьте данные и повторите попытку." : { "localizations" : { @@ -1088,11 +1129,33 @@ } } }, + "Профиль" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Profile" + } + } + } + }, "Профиль в разработке" : { "comment" : "Search placeholder title" + }, + "Профиль и поиск" : { + }, "Публичная информация" : { + }, + "Разрешить пересылку сообщений" : { + + }, + "Разрешить поиск профиля" : { + + }, + "Разрешить хранить чаты на сервере" : { + }, "Регистрация" : { "comment" : "Регистрация", @@ -1257,6 +1320,9 @@ }, "Сообщение" : { + }, + "Сохранить изменения" : { + }, "Спасибо!" : { @@ -1281,6 +1347,9 @@ } } } + }, + "Таймер автоудаления: %@" : { + }, "Тёмная" : { "localizations" : { @@ -1341,6 +1410,9 @@ } } } + }, + "Удалять аккаунт через %lld дн." : { + }, "Ура!" : { "localizations" : { @@ -1393,6 +1465,9 @@ } } } + }, + "Чаты и хранение" : { + }, "Черновики" : { "comment" : "Drafts", diff --git a/yobble/Views/Tab/Settings/EditPrivacyView.swift b/yobble/Views/Tab/Settings/EditPrivacyView.swift new file mode 100644 index 0000000..fbb959b --- /dev/null +++ b/yobble/Views/Tab/Settings/EditPrivacyView.swift @@ -0,0 +1,159 @@ +import SwiftUI + +struct EditPrivacyView: View { + @State private var profilePermissions = ProfilePermissionsResponse() + + private var privacyScopeOptions: [PrivacyScope] { PrivacyScope.allCases } + + private var forceAutoDeleteBinding: Binding { + Binding( + get: { profilePermissions.maxMessageAutoDeleteSeconds ?? 30 }, + set: { profilePermissions.maxMessageAutoDeleteSeconds = $0 } + ) + } + + private var autoDeleteAccountEnabled: Binding { + Binding( + get: { profilePermissions.autoDeleteAfterDays != nil }, + set: { newValue in + profilePermissions.autoDeleteAfterDays = newValue ? (profilePermissions.autoDeleteAfterDays ?? 30) : nil + } + ) + } + + private var autoDeleteAccountBinding: Binding { + 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) дн.") + } + } + } + + Button("Сохранить изменения") { + print("Параметры приватности: \(profilePermissions)") + } + .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 +} diff --git a/yobble/Views/Tab/Settings/SettingsView.swift b/yobble/Views/Tab/Settings/SettingsView.swift index 992fd57..858ab95 100644 --- a/yobble/Views/Tab/Settings/SettingsView.swift +++ b/yobble/Views/Tab/Settings/SettingsView.swift @@ -13,12 +13,16 @@ struct SettingsView: View { var body: some View { Form { -// // MARK: - Профиль -// Section(header: Text("Профиль")) { + // MARK: - Профиль + Section(header: Text(NSLocalizedString("Профиль", comment: ""))) { // NavigationLink(destination: EditProfileView()) { // Label("Мой профиль", systemImage: "person.crop.circle") // } -// } + + NavigationLink(destination: EditPrivacyView()) { + Label(NSLocalizedString("Конфиденциальность", comment: ""), systemImage: "lock.fill") + } + } // MARK: - Безопасность Section(header: Text(NSLocalizedString("Безопасность", comment: ""))) {