From 8eb1e17692d1e5d6a28bc11970b2124ac4159f2e Mon Sep 17 00:00:00 2001 From: cheykrym Date: Mon, 6 Oct 2025 06:13:23 +0300 Subject: [PATCH] fix ideas --- yobble.xcodeproj/project.pbxproj | 4 +- yobble/Resources/Localizable.xcstrings | 824 +++++++++++++++---------- yobble/Views/Tab/FeedbackTab.swift | 25 +- 3 files changed, 532 insertions(+), 321 deletions(-) diff --git a/yobble.xcodeproj/project.pbxproj b/yobble.xcodeproj/project.pbxproj index 6c725f5..ed42532 100644 --- a/yobble.xcodeproj/project.pbxproj +++ b/yobble.xcodeproj/project.pbxproj @@ -395,7 +395,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = yobble/yobble.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = V22H44W47J; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -435,7 +435,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = yobble/yobble.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = V22H44W47J; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; diff --git a/yobble/Resources/Localizable.xcstrings b/yobble/Resources/Localizable.xcstrings index 42d2edc..aee8888 100644 --- a/yobble/Resources/Localizable.xcstrings +++ b/yobble/Resources/Localizable.xcstrings @@ -1,443 +1,631 @@ { - "sourceLanguage": "ru", - "strings": { - "%lld": {}, - "@%@": { - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "@%@" + "sourceLanguage" : "ru", + "strings" : { + "(не работает) Отправить предложение" : { + + }, + "@%@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "@%@" } } } }, - "Chat ID:": {}, - "Companion ID:": {}, - "DEBUG UPDATE": { - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "DEBUG UPDATE" + "%lld" : { + + }, + "🌍" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "🌍" + } + } + } + }, + "Chat ID:" : { + + }, + "Companion ID:" : { + + }, + "DEBUG UPDATE" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEBUG UPDATE" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "DEBUG UPDATE" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEBUG UPDATE" } } } }, - "Fun Fest": { - "comment": "Fun Fest", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Fun Fest" + "Fun Fest" : { + "comment" : "Fun Fest", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fun Fest" } } } }, - "Hello, world!": { - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Hello, world!" + "Hello, world!" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hello, world!" } } } }, - "OK": { - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "OK" + "OK" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } } } }, - "Push-уведомления": {}, - "Yobble": { - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Yobble" + "profile_down_text_1" : { + + }, + "profile_down_text_2" : { + + }, + "profile_down_text_3" : { + + }, + "Push-уведомления" : { + + }, + "Yobble" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yobble" } } } }, - "profile_down_text_1": {}, - "profile_down_text_2": {}, - "profile_down_text_3": {}, - "Активные сессии": {}, - "Без звука (скоро)": {}, - "Безопасность": {}, - "Ваше предложение": {}, - "Вложение": {}, - "Войти": { - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Log in" + "Активные сессии" : { + + }, + "Без звука (скоро)" : { + + }, + "Безопасность" : { + + }, + "Ваше предложение" : { + + }, + "Вложение" : { + + }, + "Войти" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Log in" } } } }, - "Вы": {}, - "Вы предложили: %@": {}, - "Выйти из аккаунта": {}, - "Где найти сохранённые черновики?": { - "comment": "FAQ question: drafts" + "Вы" : { + }, - "Данные": {}, - "Двухфакторная аутентификация": {}, - "Добавить друзей": { - "comment": "Add friends", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Add friends" + "Вы предложили: %@" : { + + }, + "Выйти из аккаунта" : { + + }, + "Где найти сохранённые черновики?" : { + "comment" : "FAQ question: drafts" + }, + "Данные" : { + + }, + "Двухфакторная аутентификация" : { + + }, + "Добавить друзей" : { + "comment" : "Add friends", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add friends" } } } }, - "Другое": {}, - "Заглушка: Push-уведомления": {}, - "Заглушка: Активные сессии": {}, - "Заглушка: Двухфакторная аутентификация": {}, - "Заглушка: Другие настройки": {}, - "Заглушка: Обратная связь": {}, - "Заглушка: Сменить пароль": {}, - "Заглушка: Хранилище данных": {}, - "Загружаем чаты…": {}, - "Загрузка...": { - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Loading..." + "Другое" : { + + }, + "Заглушка: Push-уведомления" : { + + }, + "Заглушка: Активные сессии" : { + + }, + "Заглушка: Двухфакторная аутентификация" : { + + }, + "Заглушка: Другие настройки" : { + + }, + "Заглушка: Обратная связь" : { + + }, + "Заглушка: Сменить пароль" : { + + }, + "Заглушка: Хранилище данных" : { + + }, + "Загружаем чаты…" : { + + }, + "Загрузка..." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading..." } } } }, - "Заказы": { - "comment": "Orders", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Orders" + "Заказы" : { + "comment" : "Orders", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Orders" } } } }, - "Закрепить (скоро)": {}, - "Закрыть": { - "comment": "Закрыть", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Close" + "Закрепить (скоро)" : { + + }, + "Закрыть" : { + "comment" : "Закрыть", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Close" } } } }, - "Зарегистрироваться": { - "comment": "Зарегистрироваться", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Register" + "Зарегистрироваться" : { + "comment" : "Зарегистрироваться", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Register" } } } }, - "Здесь не будут чаты": {}, - "Идеи": {}, - "Избранные сообщения": {}, - "Инвайт-код (необязательно)": { - "comment": "Инвайт-код", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Invite code (optional)" + "Здесь не будут чаты" : { + + }, + "Идеи" : { + + }, + "Избранные сообщения" : { + + }, + "Инвайт-код (необязательно)" : { + "comment" : "Инвайт-код", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invite code (optional)" } } } }, - "История": { - "comment": "History", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "History" + "История" : { + "comment" : "History", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "History" } } } }, - "Как сбросить пароль?": { - "comment": "FAQ question: reset password" + "Как сбросить пароль?" : { + "comment" : "FAQ question: reset password" }, - "Как связаться с поддержкой?": { - "comment": "FAQ question: support" + "Как связаться с поддержкой?" : { + "comment" : "FAQ question: support" }, - "Какая вкладка вам нужна?": {}, - "Корзина": { - "comment": "Cart", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Cart" + "Какая вкладка вам нужна?" : { + + }, + "Корзина" : { + "comment" : "Cart", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cart" } } } }, - "Кошелёк": { - "comment": "Wallet", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Wallet" + "Кошелёк" : { + "comment" : "Wallet", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wallet" } } } }, - "Лента": {}, - "Лицо": {}, - "Логин": { - "comment": "Логин", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Login" + "Лента" : { + + }, + "Лицо" : { + + }, + "Логин" : { + "comment" : "Логин", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Login" } } } }, - "Логин должен быть от 3 до 32 символов (английские буквы, цифры, _)": { - "comment": "Логин должен быть от 3 до 32 символов (английские буквы, цифры, _)", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Username must be 3 to 32 characters (letters, digits, or _)" + "Логин должен быть от 3 до 32 символов (английские буквы, цифры, _)" : { + "comment" : "Логин должен быть от 3 до 32 символов (английские буквы, цифры, _)", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username must be 3 to 32 characters (letters, digits, or _)" } } } }, - "Логин уже занят.": {}, - "Мини-приложения": { - "comment": "Applets", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Applets" + "Логин уже занят." : { + + }, + "Мини-приложения" : { + "comment" : "Applets", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applets" } } } }, - "Мои загрузки": { - "comment": "My Downloads", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "My Downloads" + "Мои загрузки" : { + "comment" : "My Downloads", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "My Downloads" } } } }, - "Мои комментарии": { - "comment": "My Comments", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "My Comments" + "Мои комментарии" : { + "comment" : "My Comments", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "My Comments" } } } }, - "Мой профиль": {}, - "Мы планируем заменить вкладку. Поделитесь, что бы вы хотели видеть здесь чаще всего.": {}, - "Напишите нам через форму обратной связи в разделе \"Поддержка\".": { - "comment": "FAQ answer: support" + "Мой профиль" : { + }, - "Например: закладки, друзья, активность...": {}, - "Настройки": { - "comment": "Settings", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Settings" + "Мы планируем заменить вкладку. Поделитесь, что бы вы хотели видеть здесь чаще всего." : { + + }, + "Напишите нам через форму обратной связи в разделе \"Поддержка\"." : { + "comment" : "FAQ answer: support" + }, + "Например: закладки, друзья, активность..." : { + + }, + "Настройки" : { + "comment" : "Settings", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" } } } }, - "Не удалось загрузить список чатов.": {}, - "Не удалось загрузить чаты.": {}, - "Не удалось обработать данные чатов.": {}, - "Не удалось обработать ответ сервера.": {}, - "Не удалось сериализовать данные запроса.": {}, - "Неверный запрос (400).": {}, - "Неверный код приглашения.": {}, - "Неверный логин": { - "comment": "Неверный логин" + "Не удалось загрузить список чатов." : { + }, - "Неверный логин или пароль.": {}, - "Неверный пароль": { - "comment": "Неверный пароль" + "Не удалось загрузить чаты." : { + }, - "Неизвестная ошибка": {}, - "Неизвестная ошибка.": {}, - "Неизвестная ошибка. Попробуйте позже.": {}, - "Неизвестный": {}, - "Неизвестный пользователь": {}, - "Некорректный ответ от сервера.": {}, - "Нет аккаунта? Регистрация": { - "comment": "Регистрация" + "Не удалось обработать данные чатов." : { + }, - "Нет сообщений": {}, - "О приложении": {}, - "Обновить": {}, - "Обратная связь": {}, - "Описание": {}, - "Отображаемое имя": {}, - "Отправить предложение": {}, - "Отправляем...": {}, - "Ошибка авторизации": {}, - "Ошибка при деавторизации.": {}, - "Ошибка регистрация": { - "comment": "Ошибка" + "Не удалось обработать ответ сервера." : { + }, - "Ошибка сервера (%@).": {}, - "Ошибка сервера: %@": {}, - "Ошибка сети: %@": {}, - "Ошибка соединения с сервером.": {}, - "Пароли не совпадают": { - "comment": "Пароли не совпадают" + "Не удалось сериализовать данные запроса." : { + }, - "Пароль": { - "comment": "Пароль" + "Неверный запрос (400)." : { + }, - "Пароль должен быть от 8 до 128 символов": { - "comment": "Пароль должен быть от 6 до 32 символов" + "Неверный код приглашения." : { + }, - "Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям.": { - "comment": "FAQ answer: reset password" + "Неверный логин" : { + "comment" : "Неверный логин" }, - "Повторить": {}, - "Поддержка": {}, - "Подтверждение пароля": { - "comment": "Подтверждение пароля" + "Неверный логин или пароль." : { + }, - "Пока что у вас нет чатов": {}, - "Помощь": { - "comment": "Help Center", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Help" + "Неверный пароль" : { + "comment" : "Неверный пароль" + }, + "Неизвестная ошибка" : { + + }, + "Неизвестная ошибка." : { + + }, + "Неизвестная ошибка. Попробуйте позже." : { + + }, + "Неизвестный" : { + + }, + "Неизвестный пользователь" : { + + }, + "Некорректный ответ от сервера." : { + + }, + "Нет аккаунта? Регистрация" : { + "comment" : "Регистрация" + }, + "Нет сообщений" : { + + }, + "О приложении" : { + + }, + "Обновить" : { + + }, + "Обратная связь" : { + + }, + "Описание" : { + + }, + "Отображаемое имя" : { + + }, + "Отправляем..." : { + + }, + "Ошибка авторизации" : { + + }, + "Ошибка при деавторизации." : { + + }, + "Ошибка регистрация" : { + "comment" : "Ошибка" + }, + "Ошибка сервера (%@)." : { + + }, + "Ошибка сервера: %@" : { + + }, + "Ошибка сети: %@" : { + + }, + "Ошибка соединения с сервером." : { + + }, + "Пароли не совпадают" : { + "comment" : "Пароли не совпадают" + }, + "Пароль" : { + "comment" : "Пароль" + }, + "Пароль должен быть от 8 до 128 символов" : { + "comment" : "Пароль должен быть от 6 до 32 символов" + }, + "Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям." : { + "comment" : "FAQ answer: reset password" + }, + "Повторить" : { + + }, + "Поддержка" : { + + }, + "Подтверждение пароля" : { + "comment" : "Подтверждение пароля" + }, + "Пока что у вас нет чатов" : { + + }, + "Помощь" : { + "comment" : "Help Center", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Help" } } } }, - "Приглашение достигло лимита использования.": {}, - "Приглашение истекло.": {}, - "Приглашение не активно.": {}, - "Приложение": {}, - "Применить": {}, - "Произошла ошибка.": {}, - "Профиль": {}, - "Публичная информация": {}, - "Регистрация": { - "comment": "Регистрация" + "Приглашение достигло лимита использования." : { + }, - "Регистрация временно недоступна.": {}, - "Регистрация выполнена, но вход не удался.": {}, - "Регистрация запрещена.": {}, - "Регистрация и вход выполнены успешно.": {}, - "Редактировать профиль": {}, - "Сервер не отвечает. Попробуйте позже.": {}, - "Сессия истекла. Войдите снова.": {}, - "Скан": { - "comment": "Scan", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Scan" + "Приглашение истекло." : { + + }, + "Приглашение не активно." : { + + }, + "Приложение" : { + + }, + "Применить" : { + + }, + "Произошла ошибка." : { + + }, + "Профиль" : { + + }, + "Публичная информация" : { + + }, + "Регистрация" : { + "comment" : "Регистрация" + }, + "Регистрация временно недоступна." : { + + }, + "Регистрация выполнена, но вход не удался." : { + + }, + "Регистрация запрещена." : { + + }, + "Регистрация и вход выполнены успешно." : { + + }, + "Редактировать профиль" : { + + }, + "Сервер не отвечает. Попробуйте позже." : { + + }, + "Сессия истекла. Войдите снова." : { + + }, + "Скан" : { + "comment" : "Scan", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scan" } } } }, - "Слишком много запросов.": {}, - "Сменить пароль": {}, - "Сообщение": {}, - "Спасибо!": {}, - "Тёмная тема": {}, - "Уведомления": {}, - "Удалить чат (скоро)": {}, - "Центр авторов": { - "comment": "Creator Center", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Creator Center" + "Слишком много запросов." : { + + }, + "Сменить пароль" : { + + }, + "Сообщение" : { + + }, + "Спасибо!" : { + + }, + "Тёмная тема" : { + + }, + "Уведомления" : { + + }, + "Удалить чат (скоро)" : { + + }, + "Центр авторов" : { + "comment" : "Creator Center", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Creator Center" } } } }, - "Частые вопросы": { - "comment": "FAQ navigation title" + "Частые вопросы" : { + "comment" : "FAQ navigation title" }, - "Чат": {}, - "Чаты": {}, - "Черновики": { - "comment": "Drafts", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Drafts" + "Чат" : { + + }, + "Чаты" : { + + }, + "Черновики" : { + "comment" : "Drafts", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Drafts" } } } }, - "Черновики доступны в боковом меню в разделе Drafts.": { - "comment": "FAQ answer: drafts" + "Черновики доступны в боковом меню в разделе Drafts." : { + "comment" : "FAQ answer: drafts" }, - "Экран чата в разработке": {}, - "Язык": {}, - "🌍": { - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "🌍" - } - } - } + "Экран чата в разработке" : { + + }, + "Язык" : { + } }, - "version": "1.0" -} + "version" : "1.0" +} \ No newline at end of file diff --git a/yobble/Views/Tab/FeedbackTab.swift b/yobble/Views/Tab/FeedbackTab.swift index 705c3fb..09c9b6e 100644 --- a/yobble/Views/Tab/FeedbackTab.swift +++ b/yobble/Views/Tab/FeedbackTab.swift @@ -1,10 +1,14 @@ import SwiftUI +#if canImport(UIKit) +import UIKit +#endif struct FeedbackTab: View { @State private var suggestion: String = "" @State private var submittedSuggestion: String? = nil @State private var isSubmitting: Bool = false @State private var showSubmissionMessage: Bool = false + @FocusState private var isSuggestionFocused: Bool var body: some View { ScrollView { @@ -45,6 +49,7 @@ struct FeedbackTab: View { } ) .disableAutocorrection(true) + .focused($isSuggestionFocused) } Button(action: submitSuggestion) { @@ -55,7 +60,7 @@ struct FeedbackTab: View { } Text(isSubmitting ? NSLocalizedString("Отправляем...", comment: "") - : NSLocalizedString("Отправить предложение", comment: "")) + : NSLocalizedString("(не работает) Отправить предложение", comment: "")) .fontWeight(.semibold) } .frame(maxWidth: .infinity) @@ -88,6 +93,11 @@ struct FeedbackTab: View { .padding(.horizontal, 20) .padding(.vertical, 32) } + .simultaneousGesture( + TapGesture().onEnded { + dismissKeyboardIfNeeded() + } + ) } private var suggestionIsValid: Bool { @@ -98,6 +108,8 @@ struct FeedbackTab: View { guard suggestionIsValid else { return } let trimmed = suggestion.trimmingCharacters(in: .whitespacesAndNewlines) + dismissKeyboardIfNeeded() + isSubmitting = true DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { // имитируем сетевой вызов submittedSuggestion = trimmed @@ -110,6 +122,17 @@ struct FeedbackTab: View { } } +private extension FeedbackTab { + func dismissKeyboardIfNeeded() { + guard isSuggestionFocused else { return } + isSuggestionFocused = false + +#if canImport(UIKit) + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) +#endif + } +} + struct FeedbackTab_Previews: PreviewProvider { static var previews: some View { FeedbackTab()