fix ideas

This commit is contained in:
cheykrym 2025-10-06 06:13:23 +03:00
parent c2647fa22e
commit 8eb1e17692
3 changed files with 532 additions and 321 deletions

View File

@ -395,7 +395,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = yobble/yobble.entitlements; CODE_SIGN_ENTITLEMENTS = yobble/yobble.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = V22H44W47J; DEVELOPMENT_TEAM = V22H44W47J;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -435,7 +435,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = yobble/yobble.entitlements; CODE_SIGN_ENTITLEMENTS = yobble/yobble.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = V22H44W47J; DEVELOPMENT_TEAM = V22H44W47J;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;

View File

@ -1,443 +1,631 @@
{ {
"sourceLanguage": "ru", "sourceLanguage" : "ru",
"strings": { "strings" : {
"%lld": {}, "(не работает) Отправить предложение" : {
"@%@": {
"localizations": { },
"en": { "@%@" : {
"stringUnit": { "localizations" : {
"state": "translated", "en" : {
"value": "@%@" "stringUnit" : {
"state" : "translated",
"value" : "@%@"
} }
} }
} }
}, },
"Chat ID:": {}, "%lld" : {
"Companion ID:": {},
"DEBUG UPDATE": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "DEBUG UPDATE"
}
}, },
"ru": { "🌍" : {
"stringUnit": { "localizations" : {
"state": "translated", "en" : {
"value": "DEBUG UPDATE" "stringUnit" : {
"state" : "translated",
"value" : "🌍"
} }
} }
} }
}, },
"Fun Fest": { "Chat ID:" : {
"comment": "Fun Fest",
"localizations": { },
"en": { "Companion ID:" : {
"stringUnit": {
"state": "translated", },
"value": "Fun Fest" "DEBUG UPDATE" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "DEBUG UPDATE"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "DEBUG UPDATE"
} }
} }
} }
}, },
"Hello, world!": { "Fun Fest" : {
"localizations": { "comment" : "Fun Fest",
"en": { "localizations" : {
"stringUnit": { "en" : {
"state": "translated", "stringUnit" : {
"value": "Hello, world!" "state" : "translated",
"value" : "Fun Fest"
} }
} }
} }
}, },
"OK": { "Hello, world!" : {
"localizations": { "localizations" : {
"en": { "en" : {
"stringUnit": { "stringUnit" : {
"state": "translated", "state" : "translated",
"value": "OK" "value" : "Hello, world!"
} }
} }
} }
}, },
"Push-уведомления": {}, "OK" : {
"Yobble": { "localizations" : {
"localizations": { "en" : {
"en": { "stringUnit" : {
"stringUnit": { "state" : "translated",
"state": "translated", "value" : "OK"
"value": "Yobble"
} }
} }
} }
}, },
"profile_down_text_1": {}, "profile_down_text_1" : {
"profile_down_text_2": {},
"profile_down_text_3": {}, },
"Активные сессии": {}, "profile_down_text_2" : {
"Без звука (скоро)": {},
"Безопасность": {}, },
"Ваше предложение": {}, "profile_down_text_3" : {
"Вложение": {},
"Войти": { },
"localizations": { "Push-уведомления" : {
"en": {
"stringUnit": { },
"state": "translated", "Yobble" : {
"value": "Log in" "localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Yobble"
} }
} }
} }
}, },
"Вы": {}, "Активные сессии" : {
"Вы предложили: %@": {},
"Выйти из аккаунта": {},
"Где найти сохранённые черновики?": {
"comment": "FAQ question: drafts"
}, },
"Данные": {}, "Без звука (скоро)" : {
"Двухфакторная аутентификация": {},
"Добавить друзей": { },
"comment": "Add friends", "Безопасность" : {
"localizations": {
"en": { },
"stringUnit": { "Ваше предложение" : {
"state": "translated",
"value": "Add friends" },
"Вложение" : {
},
"Войти" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Log in"
} }
} }
} }
}, },
"Другое": {}, "Вы" : {
"Заглушка: Push-уведомления": {},
"Заглушка: Активные сессии": {}, },
"Заглушка: Двухфакторная аутентификация": {}, "Вы предложили: %@" : {
"Заглушка: Другие настройки": {},
"Заглушка: Обратная связь": {}, },
"Заглушка: Сменить пароль": {}, "Выйти из аккаунта" : {
"Заглушка: Хранилище данных": {},
"Загружаем чаты…": {}, },
"Загрузка...": { "Где найти сохранённые черновики?" : {
"localizations": { "comment" : "FAQ question: drafts"
"en": { },
"stringUnit": { "Данные" : {
"state": "translated",
"value": "Loading..." },
"Двухфакторная аутентификация" : {
},
"Добавить друзей" : {
"comment" : "Add friends",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Add friends"
} }
} }
} }
}, },
"Заказы": { "Другое" : {
"comment": "Orders",
"localizations": { },
"en": { "Заглушка: Push-уведомления" : {
"stringUnit": {
"state": "translated", },
"value": "Orders" "Заглушка: Активные сессии" : {
},
"Заглушка: Двухфакторная аутентификация" : {
},
"Заглушка: Другие настройки" : {
},
"Заглушка: Обратная связь" : {
},
"Заглушка: Сменить пароль" : {
},
"Заглушка: Хранилище данных" : {
},
"Загружаем чаты…" : {
},
"Загрузка..." : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Loading..."
} }
} }
} }
}, },
"Закрепить (скоро)": {}, "Заказы" : {
"Закрыть": { "comment" : "Orders",
"comment": "Закрыть", "localizations" : {
"localizations": { "en" : {
"en": { "stringUnit" : {
"stringUnit": { "state" : "translated",
"state": "translated", "value" : "Orders"
"value": "Close"
} }
} }
} }
}, },
"Зарегистрироваться": { "Закрепить (скоро)" : {
"comment": "Зарегистрироваться",
"localizations": { },
"en": { "Закрыть" : {
"stringUnit": { "comment" : "Закрыть",
"state": "translated", "localizations" : {
"value": "Register" "en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Close"
} }
} }
} }
}, },
"Здесь не будут чаты": {}, "Зарегистрироваться" : {
"Идеи": {}, "comment" : "Зарегистрироваться",
"Избранные сообщения": {}, "localizations" : {
"Инвайт-код (необязательно)": { "en" : {
"comment": "Инвайт-код", "stringUnit" : {
"localizations": { "state" : "translated",
"en": { "value" : "Register"
"stringUnit": {
"state": "translated",
"value": "Invite code (optional)"
} }
} }
} }
}, },
"История": { "Здесь не будут чаты" : {
"comment": "History",
"localizations": { },
"en": { "Идеи" : {
"stringUnit": {
"state": "translated", },
"value": "History" "Избранные сообщения" : {
},
"Инвайт-код (необязательно)" : {
"comment" : "Инвайт-код",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Invite code (optional)"
} }
} }
} }
}, },
"Как сбросить пароль?": { "История" : {
"comment": "FAQ question: reset password" "comment" : "History",
}, "localizations" : {
"Как связаться с поддержкой?": { "en" : {
"comment": "FAQ question: support" "stringUnit" : {
}, "state" : "translated",
"Какая вкладка вам нужна?": {}, "value" : "History"
"Корзина": {
"comment": "Cart",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Cart"
} }
} }
} }
}, },
"Кошелёк": { "Как сбросить пароль?" : {
"comment": "Wallet", "comment" : "FAQ question: reset password"
"localizations": { },
"en": { "Как связаться с поддержкой?" : {
"stringUnit": { "comment" : "FAQ question: support"
"state": "translated", },
"value": "Wallet" "Какая вкладка вам нужна?" : {
},
"Корзина" : {
"comment" : "Cart",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Cart"
} }
} }
} }
}, },
"Лента": {}, "Кошелёк" : {
"Лицо": {}, "comment" : "Wallet",
"Логин": { "localizations" : {
"comment": "Логин", "en" : {
"localizations": { "stringUnit" : {
"en": { "state" : "translated",
"stringUnit": { "value" : "Wallet"
"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 _)" "Логин" : {
"comment" : "Логин",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Login"
} }
} }
} }
}, },
"Логин уже занят.": {}, "Логин должен быть от 3 до 32 символов (английские буквы, цифры, _)" : {
"Мини-приложения": { "comment" : "Логин должен быть от 3 до 32 символов (английские буквы, цифры, _)",
"comment": "Applets", "localizations" : {
"localizations": { "en" : {
"en": { "stringUnit" : {
"stringUnit": { "state" : "translated",
"state": "translated", "value" : "Username must be 3 to 32 characters (letters, digits, or _)"
"value": "Applets"
} }
} }
} }
}, },
"Мои загрузки": { "Логин уже занят." : {
"comment": "My Downloads",
"localizations": { },
"en": { "Мини-приложения" : {
"stringUnit": { "comment" : "Applets",
"state": "translated", "localizations" : {
"value": "My Downloads" "en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Applets"
} }
} }
} }
}, },
"Мои комментарии": { "Мои загрузки" : {
"comment": "My Comments", "comment" : "My Downloads",
"localizations": { "localizations" : {
"en": { "en" : {
"stringUnit": { "stringUnit" : {
"state": "translated", "state" : "translated",
"value": "My Comments" "value" : "My Downloads"
} }
} }
} }
}, },
"Мой профиль": {}, "Мои комментарии" : {
"Мы планируем заменить вкладку. Поделитесь, что бы вы хотели видеть здесь чаще всего.": {}, "comment" : "My Comments",
"Напишите нам через форму обратной связи в разделе \"Поддержка\".": { "localizations" : {
"comment": "FAQ answer: support" "en" : {
}, "stringUnit" : {
"Например: закладки, друзья, активность...": {}, "state" : "translated",
"Настройки": { "value" : "My Comments"
"comment": "Settings",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Settings"
} }
} }
} }
}, },
"Не удалось загрузить список чатов.": {}, "Мой профиль" : {
"Не удалось загрузить чаты.": {},
"Не удалось обработать данные чатов.": {},
"Не удалось обработать ответ сервера.": {},
"Не удалось сериализовать данные запроса.": {},
"Неверный запрос (400).": {},
"Неверный код приглашения.": {},
"Неверный логин": {
"comment": "Неверный логин"
}, },
"Неверный логин или пароль.": {}, "Мы планируем заменить вкладку. Поделитесь, что бы вы хотели видеть здесь чаще всего." : {
"Неверный пароль": {
"comment": "Неверный пароль"
}, },
"Неизвестная ошибка": {}, "Напишите нам через форму обратной связи в разделе \"Поддержка\"." : {
"Неизвестная ошибка.": {}, "comment" : "FAQ answer: support"
"Неизвестная ошибка. Попробуйте позже.": {},
"Неизвестный": {},
"Неизвестный пользователь": {},
"Некорректный ответ от сервера.": {},
"Нет аккаунта? Регистрация": {
"comment": "Регистрация"
}, },
"Нет сообщений": {}, "Например: закладки, друзья, активность..." : {
"О приложении": {},
"Обновить": {},
"Обратная связь": {},
"Описание": {},
"Отображаемое имя": {},
"Отправить предложение": {},
"Отправляем...": {},
"Ошибка авторизации": {},
"Ошибка при деавторизации.": {},
"Ошибка регистрация": {
"comment": "Ошибка"
}, },
"Ошибка сервера (%@).": {}, "Настройки" : {
"Ошибка сервера: %@": {}, "comment" : "Settings",
"Ошибка сети: %@": {}, "localizations" : {
"Ошибка соединения с сервером.": {}, "en" : {
"Пароли не совпадают": { "stringUnit" : {
"comment": "Пароли не совпадают" "state" : "translated",
}, "value" : "Settings"
"Пароль": {
"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", "Неверный запрос (400)." : {
"value": "Scan"
},
"Неверный код приглашения." : {
},
"Неверный логин" : {
"comment" : "Неверный логин"
},
"Неверный логин или пароль." : {
},
"Неверный пароль" : {
"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": "Creator Center", },
"localizations": { "Приложение" : {
"en": {
"stringUnit": { },
"state": "translated", "Применить" : {
"value": "Creator Center"
},
"Произошла ошибка." : {
},
"Профиль" : {
},
"Публичная информация" : {
},
"Регистрация" : {
"comment" : "Регистрация"
},
"Регистрация временно недоступна." : {
},
"Регистрация выполнена, но вход не удался." : {
},
"Регистрация запрещена." : {
},
"Регистрация и вход выполнены успешно." : {
},
"Редактировать профиль" : {
},
"Сервер не отвечает. Попробуйте позже." : {
},
"Сессия истекла. Войдите снова." : {
},
"Скан" : {
"comment" : "Scan",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Scan"
} }
} }
} }
}, },
"Частые вопросы": { "Слишком много запросов." : {
"comment": "FAQ navigation title"
}, },
"Чат": {}, "Сменить пароль" : {
"Чаты": {},
"Черновики": { },
"comment": "Drafts", "Сообщение" : {
"localizations": {
"en": { },
"stringUnit": { "Спасибо!" : {
"state": "translated",
"value": "Drafts" },
"Тёмная тема" : {
},
"Уведомления" : {
},
"Удалить чат (скоро)" : {
},
"Центр авторов" : {
"comment" : "Creator Center",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Creator Center"
} }
} }
} }
}, },
"Черновики доступны в боковом меню в разделе Drafts.": { астые вопросы" : {
"comment": "FAQ answer: drafts" "comment" : "FAQ navigation title"
}, },
"Экран чата в разработке": {}, "Чат" : {
"Язык": {},
"🌍": { },
"localizations": { "Чаты" : {
"en": {
"stringUnit": { },
"state": "translated", "Черновики" : {
"value": "🌍" "comment" : "Drafts",
} "localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Drafts"
} }
} }
} }
}, },
"version": "1.0" "Черновики доступны в боковом меню в разделе Drafts." : {
"comment" : "FAQ answer: drafts"
},
"Экран чата в разработке" : {
},
"Язык" : {
}
},
"version" : "1.0"
} }

View File

@ -1,10 +1,14 @@
import SwiftUI import SwiftUI
#if canImport(UIKit)
import UIKit
#endif
struct FeedbackTab: View { struct FeedbackTab: View {
@State private var suggestion: String = "" @State private var suggestion: String = ""
@State private var submittedSuggestion: String? = nil @State private var submittedSuggestion: String? = nil
@State private var isSubmitting: Bool = false @State private var isSubmitting: Bool = false
@State private var showSubmissionMessage: Bool = false @State private var showSubmissionMessage: Bool = false
@FocusState private var isSuggestionFocused: Bool
var body: some View { var body: some View {
ScrollView { ScrollView {
@ -45,6 +49,7 @@ struct FeedbackTab: View {
} }
) )
.disableAutocorrection(true) .disableAutocorrection(true)
.focused($isSuggestionFocused)
} }
Button(action: submitSuggestion) { Button(action: submitSuggestion) {
@ -55,7 +60,7 @@ struct FeedbackTab: View {
} }
Text(isSubmitting Text(isSubmitting
? NSLocalizedString("Отправляем...", comment: "") ? NSLocalizedString("Отправляем...", comment: "")
: NSLocalizedString("Отправить предложение", comment: "")) : NSLocalizedString("(не работает) Отправить предложение", comment: ""))
.fontWeight(.semibold) .fontWeight(.semibold)
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@ -88,6 +93,11 @@ struct FeedbackTab: View {
.padding(.horizontal, 20) .padding(.horizontal, 20)
.padding(.vertical, 32) .padding(.vertical, 32)
} }
.simultaneousGesture(
TapGesture().onEnded {
dismissKeyboardIfNeeded()
}
)
} }
private var suggestionIsValid: Bool { private var suggestionIsValid: Bool {
@ -98,6 +108,8 @@ struct FeedbackTab: View {
guard suggestionIsValid else { return } guard suggestionIsValid else { return }
let trimmed = suggestion.trimmingCharacters(in: .whitespacesAndNewlines) let trimmed = suggestion.trimmingCharacters(in: .whitespacesAndNewlines)
dismissKeyboardIfNeeded()
isSubmitting = true isSubmitting = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { // имитируем сетевой вызов DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { // имитируем сетевой вызов
submittedSuggestion = trimmed 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 { struct FeedbackTab_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
FeedbackTab() FeedbackTab()