Compare commits
	
		
			2 Commits
		
	
	
		
			d63391fadf
			...
			0359616c8f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0359616c8f | |||
| 02be9062d0 | 
@ -395,7 +395,7 @@
 | 
			
		||||
				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 | 
			
		||||
				CODE_SIGN_ENTITLEMENTS = yobble/yobble.entitlements;
 | 
			
		||||
				CODE_SIGN_STYLE = Automatic;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 5;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 6;
 | 
			
		||||
				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 = 5;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 6;
 | 
			
		||||
				DEVELOPMENT_TEAM = V22H44W47J;
 | 
			
		||||
				ENABLE_HARDENED_RUNTIME = YES;
 | 
			
		||||
				ENABLE_PREVIEWS = YES;
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "sourceLanguage" : "ru",
 | 
			
		||||
  "strings" : {
 | 
			
		||||
    "(не работает) Отправить предложение" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "@%@" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
@ -34,6 +31,17 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "%d символов" : {
 | 
			
		||||
      "comment" : "feedback: character count",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "%d characters"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "%lld" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
@ -203,8 +211,16 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Ваше предложение" : {
 | 
			
		||||
 | 
			
		||||
    "Ваш e-mail" : {
 | 
			
		||||
      "comment" : "feedback: email placeholder",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Your email"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Версия:" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
@ -289,8 +305,16 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Вы предложили: %@" : {
 | 
			
		||||
 | 
			
		||||
    "Выберите оценку — это поможет нам понять настроение." : {
 | 
			
		||||
      "comment" : "feedback: rating hint",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Select a rating — this helps us understand the vibe."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Выйти из аккаунта" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
@ -460,6 +484,17 @@
 | 
			
		||||
    "Здесь появится информация о собеседнике и существующих чатах." : {
 | 
			
		||||
      "comment" : "Search placeholder description"
 | 
			
		||||
    },
 | 
			
		||||
    "Идея" : {
 | 
			
		||||
      "comment" : "feedback category: idea",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Idea"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Избранные сообщения" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
@ -503,9 +538,6 @@
 | 
			
		||||
    },
 | 
			
		||||
    "Как связаться с поддержкой?" : {
 | 
			
		||||
      "comment" : "FAQ question: support"
 | 
			
		||||
    },
 | 
			
		||||
    "Какая вкладка вам нужна?" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Кастомная" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
@ -530,6 +562,17 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Контент" : {
 | 
			
		||||
      "comment" : "feedback category: content",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Content"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Конфиденциальность" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
@ -693,14 +736,107 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Мы планируем заменить вкладку. Поделитесь, что бы вы хотели видеть здесь чаще всего." : {
 | 
			
		||||
 | 
			
		||||
    "Мы используем адрес только для ответа на ваш запрос." : {
 | 
			
		||||
      "comment" : "feedback: email hint",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "We’ll only use your email to respond to your request."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Мы постараемся всё исправить. Напишите, что смутило." : {
 | 
			
		||||
      "comment" : "feedback: rating description 2",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "We’ll do our best to fix it. Tell us what felt off."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Мы свяжемся с вами по адресу %@, как только ответим." : {
 | 
			
		||||
      "comment" : "feedback: success email",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "We’ll contact you at %@ once we reply."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Мы читаем каждый отзыв и используем его, чтобы сделать Yobble полезнее для вас." : {
 | 
			
		||||
      "comment" : "feedback: header subtitle",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "We read every piece of feedback and use it to make Yobble better for you."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Напишите нам через форму обратной связи в разделе \"Поддержка\"." : {
 | 
			
		||||
      "comment" : "FAQ answer: support"
 | 
			
		||||
    },
 | 
			
		||||
    "Например: закладки, друзья, активность..." : {
 | 
			
		||||
 | 
			
		||||
    "Например: заметил неточную информацию в статье..." : {
 | 
			
		||||
      "comment" : "feedback placeholder: content",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "For example: I noticed inaccurate information in an article…"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Например: понравилась новая лента, потому что..." : {
 | 
			
		||||
      "comment" : "feedback placeholder: praise",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "For example: I liked the new feed because…"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Например: приложение вылетает, когда я открываю профиль..." : {
 | 
			
		||||
      "comment" : "feedback placeholder: bug",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "For example: the app crashes when I open a profile…"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Например: хотелось бы видеть подборку по интересам..." : {
 | 
			
		||||
      "comment" : "feedback placeholder: idea",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "For example: I’d love to see personalized recommendations…"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Насколько вам нравится Yobble?" : {
 | 
			
		||||
      "comment" : "feedback: rating title",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "How much do you like Yobble?"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Настройки" : {
 | 
			
		||||
      "comment" : "Settings",
 | 
			
		||||
@ -924,6 +1060,17 @@
 | 
			
		||||
    },
 | 
			
		||||
    "Необходимо авторизоваться заново." : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Неплохо, но можно лучше — что добавить?" : {
 | 
			
		||||
      "comment" : "feedback: rating description 3",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Not bad, but it could be better — what would you add?"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Нет аккаунта? Регистрация" : {
 | 
			
		||||
      "comment" : "Регистрация",
 | 
			
		||||
@ -970,6 +1117,28 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Нужно ли вам ответить?" : {
 | 
			
		||||
      "comment" : "feedback: contact title",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Do you need a response?"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "О каком контенте идёт речь?" : {
 | 
			
		||||
      "comment" : "feedback prompt: content",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "What content are you referring to?"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "О приложении" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
@ -991,6 +1160,7 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Обратная связь" : {
 | 
			
		||||
      "comment" : "feedback: navigation title",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
@ -1000,6 +1170,9 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Обратная связь (не работает)" : {
 | 
			
		||||
      "comment" : "feedback: navigation title"
 | 
			
		||||
    },
 | 
			
		||||
    "Ограничить таймер автоудаления (максимум)" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
@ -1012,12 +1185,64 @@
 | 
			
		||||
    },
 | 
			
		||||
    "Описание" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Опишите идею" : {
 | 
			
		||||
      "comment" : "feedback prompt: idea",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Describe your idea"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Отображаемое имя" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Отправить отзыв" : {
 | 
			
		||||
      "comment" : "feedback: submit button",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Submit Feedback"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Отправляем..." : {
 | 
			
		||||
 | 
			
		||||
      "comment" : "feedback: sending state",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Sending..."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Оценка %d" : {
 | 
			
		||||
      "comment" : "feedback: rating accessibility",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Rating %d"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Оценка: %d из 5" : {
 | 
			
		||||
      "comment" : "feedback: success rating",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Rating: %d out of 5"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Ошибка" : {
 | 
			
		||||
      "comment" : "Profile update error title",
 | 
			
		||||
@ -1200,6 +1425,28 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Поделитесь идеями, сообщите об ошибке или расскажите, что работает отлично." : {
 | 
			
		||||
      "comment" : "feedback: info detail",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Share ideas, report a bug, or tell us what works well."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Поделитесь, что понравилось" : {
 | 
			
		||||
      "comment" : "feedback category subtitle: praise",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Share what you liked"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Подтверждение пароля" : {
 | 
			
		||||
      "comment" : "Подтверждение пароля",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
@ -1211,6 +1458,17 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Пожалуйста, введите корректный e-mail." : {
 | 
			
		||||
      "comment" : "feedback: email error",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Please enter a valid email."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Поиск" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
@ -1257,6 +1515,17 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Получить ответ от команды" : {
 | 
			
		||||
      "comment" : "feedback: contact toggle",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Get a reply from the team"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Пользователь Системы 1" : {
 | 
			
		||||
      "comment" : "Тестовая подмена офф аккаунта",
 | 
			
		||||
      "extractionState" : "manual",
 | 
			
		||||
@ -1282,6 +1551,39 @@
 | 
			
		||||
    },
 | 
			
		||||
    "Попробуйте изменить запрос поиска." : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Похвала" : {
 | 
			
		||||
      "comment" : "feedback category: praise",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Praise"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Предложите, что добавить" : {
 | 
			
		||||
      "comment" : "feedback category subtitle: idea",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Suggest what to add"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Прекрасно! Расскажите, что понравилось больше всего." : {
 | 
			
		||||
      "comment" : "feedback: rating description 5",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Amazing! Tell us what you liked the most."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Приватные чаты" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
@ -1373,6 +1675,17 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Проблема" : {
 | 
			
		||||
      "comment" : "feedback category: bug",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Bug"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Проверьте данные и повторите попытку." : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
@ -1452,6 +1765,28 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Расскажите о своём опыте" : {
 | 
			
		||||
      "comment" : "feedback: header title",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Tell us about your experience"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Расскажите, что не работает" : {
 | 
			
		||||
      "comment" : "feedback category subtitle: bug",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Tell us what isn’t working"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Регистрация" : {
 | 
			
		||||
      "comment" : "Регистрация",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
@ -1635,6 +1970,17 @@
 | 
			
		||||
    },
 | 
			
		||||
    "Сообщение" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Сообщите о материалах" : {
 | 
			
		||||
      "comment" : "feedback category subtitle: content",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Report content issues"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Сохранить изменения" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
@ -1646,8 +1992,27 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Спасибо!" : {
 | 
			
		||||
 | 
			
		||||
    "Спасибо! Мы получили ваш отзыв" : {
 | 
			
		||||
      "comment" : "feedback: success title",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Thank you! We’ve received your feedback"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Спасибо! Что поможет нам добраться до пятёрки?" : {
 | 
			
		||||
      "comment" : "feedback: rating description 4",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Thanks! What would get us to a five?"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Старый пароль" : {
 | 
			
		||||
      "comment" : "Старый пароль",
 | 
			
		||||
@ -1670,6 +2035,17 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Тема: %@" : {
 | 
			
		||||
      "comment" : "feedback: success category",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Category: %@"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Тёмная" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
@ -1806,6 +2182,50 @@
 | 
			
		||||
    "Черновики доступны в боковом меню в разделе Drafts." : {
 | 
			
		||||
      "comment" : "FAQ answer: drafts"
 | 
			
		||||
    },
 | 
			
		||||
    "Что вам понравилось?" : {
 | 
			
		||||
      "comment" : "feedback prompt: praise",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "What did you like?"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Что вы хотите обсудить?" : {
 | 
			
		||||
      "comment" : "feedback: category title",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "What would you like to talk about?"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Что случилось?" : {
 | 
			
		||||
      "comment" : "feedback prompt: bug",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "What happened?"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Что-то пошло не так. Расскажите подробности ниже." : {
 | 
			
		||||
      "comment" : "feedback: rating description 1",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Something went wrong. Tell us more below."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Экран чата в разработке" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -5,127 +5,364 @@ import UIKit
 | 
			
		||||
 | 
			
		||||
struct FeedbackView: View {
 | 
			
		||||
    @State private var suggestion: String = ""
 | 
			
		||||
    @State private var submittedSuggestion: String? = nil
 | 
			
		||||
    @State private var submittedFeedback: SubmittedFeedback? = nil
 | 
			
		||||
    @State private var isSubmitting: Bool = false
 | 
			
		||||
    @State private var showSubmissionMessage: Bool = false
 | 
			
		||||
    @FocusState private var isSuggestionFocused: Bool
 | 
			
		||||
    @State private var feedbackCategory: FeedbackCategory = .idea
 | 
			
		||||
    @State private var rating: Int = 0
 | 
			
		||||
    @State private var wantsResponse: Bool = false
 | 
			
		||||
    @State private var contactEmail: String = ""
 | 
			
		||||
    @FocusState private var focusedField: Field?
 | 
			
		||||
 | 
			
		||||
    private let gridColumns = [GridItem(.adaptive(minimum: 120), spacing: 12)]
 | 
			
		||||
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        ScrollView {
 | 
			
		||||
            VStack(alignment: .leading, spacing: 20) {
 | 
			
		||||
                Text(NSLocalizedString("Какая вкладка вам нужна?", comment: ""))
 | 
			
		||||
                    .font(.title2)
 | 
			
		||||
                    .fontWeight(.semibold)
 | 
			
		||||
 | 
			
		||||
                Text(NSLocalizedString(
 | 
			
		||||
                    "Мы планируем заменить вкладку. Поделитесь, что бы вы хотели видеть здесь чаще всего.",
 | 
			
		||||
                    comment: ""
 | 
			
		||||
                ))
 | 
			
		||||
                .foregroundColor(.secondary)
 | 
			
		||||
 | 
			
		||||
                VStack(alignment: .leading, spacing: 12) {
 | 
			
		||||
                    Text(NSLocalizedString("Ваше предложение", comment: ""))
 | 
			
		||||
                        .font(.headline)
 | 
			
		||||
 | 
			
		||||
                    TextEditor(text: $suggestion)
 | 
			
		||||
                        .frame(minHeight: 120)
 | 
			
		||||
                        .padding(12)
 | 
			
		||||
                        .background(
 | 
			
		||||
                            RoundedRectangle(cornerRadius: 12)
 | 
			
		||||
                                .fill(Color(.systemGray6))
 | 
			
		||||
                        )
 | 
			
		||||
                        .overlay(
 | 
			
		||||
                            RoundedRectangle(cornerRadius: 12)
 | 
			
		||||
                                .stroke(Color(.systemGray4))
 | 
			
		||||
                        )
 | 
			
		||||
                        .overlay(
 | 
			
		||||
                            Group {
 | 
			
		||||
                                if suggestion.isEmpty {
 | 
			
		||||
                                    Text(NSLocalizedString("Например: закладки, друзья, активность...", comment: ""))
 | 
			
		||||
                                        .foregroundColor(.secondary)
 | 
			
		||||
                                        .padding(18)
 | 
			
		||||
                                        .allowsHitTesting(false)
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        )
 | 
			
		||||
                        .disableAutocorrection(true)
 | 
			
		||||
                        .focused($isSuggestionFocused)
 | 
			
		||||
                }
 | 
			
		||||
            VStack(alignment: .leading, spacing: 24) {
 | 
			
		||||
                headerSection
 | 
			
		||||
                infoSection
 | 
			
		||||
                categorySection
 | 
			
		||||
                ratingSection
 | 
			
		||||
                suggestionSection
 | 
			
		||||
                contactSection
 | 
			
		||||
 | 
			
		||||
                Button(action: submitSuggestion) {
 | 
			
		||||
                    HStack {
 | 
			
		||||
                    HStack(spacing: 10) {
 | 
			
		||||
                        if isSubmitting {
 | 
			
		||||
                            ProgressView()
 | 
			
		||||
                                .progressViewStyle(CircularProgressViewStyle())
 | 
			
		||||
                        }
 | 
			
		||||
                        Text(isSubmitting
 | 
			
		||||
                             ? NSLocalizedString("Отправляем...", comment: "")
 | 
			
		||||
                             : NSLocalizedString("(не работает) Отправить предложение", comment: ""))
 | 
			
		||||
                             ? NSLocalizedString("Отправляем...", comment: "feedback: sending state")
 | 
			
		||||
                             : NSLocalizedString("Отправить отзыв", comment: "feedback: submit button"))
 | 
			
		||||
                            .fontWeight(.semibold)
 | 
			
		||||
                    }
 | 
			
		||||
                    .frame(maxWidth: .infinity)
 | 
			
		||||
                    .padding()
 | 
			
		||||
                    .background(suggestionIsValid ? Color.accentColor : Color(.systemGray4))
 | 
			
		||||
                    .background(buttonBackgroundColor)
 | 
			
		||||
                    .foregroundColor(.white)
 | 
			
		||||
                    .cornerRadius(14)
 | 
			
		||||
                    .cornerRadius(16)
 | 
			
		||||
                }
 | 
			
		||||
                .disabled(!suggestionIsValid || isSubmitting)
 | 
			
		||||
                .disabled(!canSubmit)
 | 
			
		||||
 | 
			
		||||
                if let submittedSuggestion, showSubmissionMessage {
 | 
			
		||||
                    VStack(alignment: .leading, spacing: 8) {
 | 
			
		||||
                        Text(NSLocalizedString("Спасибо!", comment: ""))
 | 
			
		||||
                            .font(.headline)
 | 
			
		||||
                        Text(String(format: NSLocalizedString("Вы предложили: %@", comment: ""), submittedSuggestion))
 | 
			
		||||
                            .foregroundColor(.secondary)
 | 
			
		||||
                    }
 | 
			
		||||
                    .transition(.opacity)
 | 
			
		||||
                if let submission = submittedFeedback, showSubmissionMessage {
 | 
			
		||||
                    successSection(for: submission)
 | 
			
		||||
                        .transition(.move(edge: .top).combined(with: .opacity))
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Spacer(minLength: 24)
 | 
			
		||||
 | 
			
		||||
//                Text(NSLocalizedString(
 | 
			
		||||
//                    "Позже мы добавим отправку на сервер, чтобы собрать статистику, и расскажем о результатах в обновлениях.",
 | 
			
		||||
//                    comment: ""
 | 
			
		||||
//                ))
 | 
			
		||||
//                .font(.footnote)
 | 
			
		||||
//                .foregroundColor(.secondary)
 | 
			
		||||
                Spacer(minLength: 16)
 | 
			
		||||
            }
 | 
			
		||||
            .padding(.horizontal, 20)
 | 
			
		||||
            .padding(.vertical, 32)
 | 
			
		||||
            .padding(.horizontal, 20)
 | 
			
		||||
        }
 | 
			
		||||
        .background(Color(.systemGroupedBackground).ignoresSafeArea())
 | 
			
		||||
        .navigationTitle(NSLocalizedString("Обратная связь (не работает)", comment: "feedback: navigation title"))
 | 
			
		||||
        .navigationBarTitleDisplayMode(.inline)
 | 
			
		||||
        .simultaneousGesture(
 | 
			
		||||
            TapGesture().onEnded {
 | 
			
		||||
                dismissKeyboardIfNeeded()
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        .onChange(of: wantsResponse) { wants in
 | 
			
		||||
            if !wants {
 | 
			
		||||
                contactEmail = ""
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var headerSection: some View {
 | 
			
		||||
        VStack(alignment: .leading, spacing: 12) {
 | 
			
		||||
            Text(NSLocalizedString("Расскажите о своём опыте", comment: "feedback: header title"))
 | 
			
		||||
                .font(.title2)
 | 
			
		||||
                .fontWeight(.bold)
 | 
			
		||||
            Text(NSLocalizedString("Мы читаем каждый отзыв и используем его, чтобы сделать Yobble полезнее для вас.", comment: "feedback: header subtitle"))
 | 
			
		||||
                .foregroundColor(.secondary)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var infoSection: some View {
 | 
			
		||||
        VStack(alignment: .leading, spacing: 8) {
 | 
			
		||||
            Label {
 | 
			
		||||
                Text(NSLocalizedString("Поделитесь идеями, сообщите об ошибке или расскажите, что работает отлично.", comment: "feedback: info detail"))
 | 
			
		||||
            } icon: {
 | 
			
		||||
                Image(systemName: "bubble.left.and.bubble.right.fill")
 | 
			
		||||
                    .foregroundColor(.accentColor)
 | 
			
		||||
            }
 | 
			
		||||
            .font(.callout)
 | 
			
		||||
        }
 | 
			
		||||
        .padding()
 | 
			
		||||
        .frame(maxWidth: .infinity, alignment: .leading)
 | 
			
		||||
        .background(
 | 
			
		||||
            RoundedRectangle(cornerRadius: 16, style: .continuous)
 | 
			
		||||
                .fill(Color.accentColor.opacity(0.08))
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var categorySection: some View {
 | 
			
		||||
        VStack(alignment: .leading, spacing: 12) {
 | 
			
		||||
            sectionTitle(NSLocalizedString("Что вы хотите обсудить?", comment: "feedback: category title"))
 | 
			
		||||
            LazyVGrid(columns: gridColumns, alignment: .leading, spacing: 12) {
 | 
			
		||||
                ForEach(FeedbackCategory.allCases) { category in
 | 
			
		||||
                    categoryButton(for: category)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var ratingSection: some View {
 | 
			
		||||
        VStack(alignment: .leading, spacing: 12) {
 | 
			
		||||
            sectionTitle(NSLocalizedString("Насколько вам нравится Yobble?", comment: "feedback: rating title"))
 | 
			
		||||
            HStack(spacing: 8) {
 | 
			
		||||
                ForEach(1...5, id: \.self) { value in
 | 
			
		||||
                    Button {
 | 
			
		||||
                        rating = value
 | 
			
		||||
                    } label: {
 | 
			
		||||
                        Image(systemName: rating >= value ? "star.fill" : "star")
 | 
			
		||||
                            .foregroundColor(rating >= value ? Color.yellow : Color(.systemGray3))
 | 
			
		||||
                            .font(.title3)
 | 
			
		||||
                            .padding(.vertical, 4)
 | 
			
		||||
                    }
 | 
			
		||||
                    .buttonStyle(.plain)
 | 
			
		||||
                    .accessibilityLabel(Text(String(format: NSLocalizedString("Оценка %d", comment: "feedback: rating accessibility"), value)))
 | 
			
		||||
                    .accessibilityAddTraits(rating == value ? .isSelected : [])
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if rating > 0 {
 | 
			
		||||
                Text(ratingDescription(for: rating))
 | 
			
		||||
                    .font(.caption)
 | 
			
		||||
                    .foregroundColor(.secondary)
 | 
			
		||||
            } else {
 | 
			
		||||
                Text(NSLocalizedString("Выберите оценку — это поможет нам понять настроение.", comment: "feedback: rating hint"))
 | 
			
		||||
                    .font(.caption)
 | 
			
		||||
                    .foregroundColor(.secondary)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var suggestionSection: some View {
 | 
			
		||||
        VStack(alignment: .leading, spacing: 12) {
 | 
			
		||||
            sectionTitle(feedbackCategory.promptTitle)
 | 
			
		||||
 | 
			
		||||
            TextEditor(text: $suggestion)
 | 
			
		||||
                .frame(minHeight: 140)
 | 
			
		||||
                .padding(12)
 | 
			
		||||
                .background(
 | 
			
		||||
                    RoundedRectangle(cornerRadius: 14, style: .continuous)
 | 
			
		||||
                        .fill(Color(.systemBackground))
 | 
			
		||||
                )
 | 
			
		||||
                .overlay(
 | 
			
		||||
                    RoundedRectangle(cornerRadius: 14, style: .continuous)
 | 
			
		||||
                        .stroke(Color(.systemGray4), lineWidth: 1)
 | 
			
		||||
                )
 | 
			
		||||
                .overlay(
 | 
			
		||||
                    Group {
 | 
			
		||||
                        if suggestion.isEmpty {
 | 
			
		||||
                            Text(feedbackCategory.placeholder)
 | 
			
		||||
                                .foregroundColor(.secondary)
 | 
			
		||||
                                .padding(.horizontal, 18)
 | 
			
		||||
                                .padding(.vertical, 16)
 | 
			
		||||
                                .allowsHitTesting(false)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
                .focused($focusedField, equals: .suggestion)
 | 
			
		||||
                .autocorrectionDisabled(false)
 | 
			
		||||
 | 
			
		||||
            Text(String(format: NSLocalizedString("%d символов", comment: "feedback: character count"), suggestion.count))
 | 
			
		||||
                .font(.caption2)
 | 
			
		||||
                .foregroundColor(.secondary)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var contactSection: some View {
 | 
			
		||||
        VStack(alignment: .leading, spacing: 12) {
 | 
			
		||||
            sectionTitle(NSLocalizedString("Нужно ли вам ответить?", comment: "feedback: contact title"))
 | 
			
		||||
 | 
			
		||||
            Toggle(NSLocalizedString("Получить ответ от команды", comment: "feedback: contact toggle"), isOn: $wantsResponse)
 | 
			
		||||
                .toggleStyle(SwitchToggleStyle(tint: .accentColor))
 | 
			
		||||
 | 
			
		||||
            if wantsResponse {
 | 
			
		||||
                VStack(alignment: .leading, spacing: 6) {
 | 
			
		||||
                    TextField(NSLocalizedString("Ваш e-mail", comment: "feedback: email placeholder"), text: $contactEmail)
 | 
			
		||||
                        .textContentType(.emailAddress)
 | 
			
		||||
                        .textInputAutocapitalization(.never)
 | 
			
		||||
                        .keyboardType(.emailAddress)
 | 
			
		||||
                        .autocorrectionDisabled(true)
 | 
			
		||||
                        .padding(12)
 | 
			
		||||
                        .background(
 | 
			
		||||
                            RoundedRectangle(cornerRadius: 14, style: .continuous)
 | 
			
		||||
                                .fill(Color(.systemBackground))
 | 
			
		||||
                        )
 | 
			
		||||
                        .overlay(
 | 
			
		||||
                            RoundedRectangle(cornerRadius: 14, style: .continuous)
 | 
			
		||||
                                .stroke(emailBorderColor, lineWidth: 1)
 | 
			
		||||
                        )
 | 
			
		||||
                        .focused($focusedField, equals: .email)
 | 
			
		||||
 | 
			
		||||
                    if !contactEmail.isEmpty && !emailIsValid {
 | 
			
		||||
                        Text(NSLocalizedString("Пожалуйста, введите корректный e-mail.", comment: "feedback: email error"))
 | 
			
		||||
                            .font(.caption)
 | 
			
		||||
                            .foregroundColor(.red)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Text(NSLocalizedString("Мы используем адрес только для ответа на ваш запрос.", comment: "feedback: email hint"))
 | 
			
		||||
                            .font(.caption)
 | 
			
		||||
                            .foregroundColor(.secondary)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var canSubmit: Bool {
 | 
			
		||||
        suggestionIsValid && emailIsValid && !isSubmitting
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var suggestionIsValid: Bool {
 | 
			
		||||
        !suggestion.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var emailIsValid: Bool {
 | 
			
		||||
        guard wantsResponse else { return true }
 | 
			
		||||
        let trimmed = contactEmail.trimmingCharacters(in: .whitespacesAndNewlines)
 | 
			
		||||
        guard !trimmed.isEmpty else { return false }
 | 
			
		||||
        return trimmed.contains("@") && trimmed.contains(".")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var buttonBackgroundColor: Color {
 | 
			
		||||
        suggestionIsValid && emailIsValid ? Color.accentColor : Color(.systemGray4)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var emailBorderColor: Color {
 | 
			
		||||
        if contactEmail.isEmpty { return Color(.systemGray4) }
 | 
			
		||||
        return emailIsValid ? Color.accentColor : .red
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func sectionTitle(_ text: String) -> some View {
 | 
			
		||||
        Text(text)
 | 
			
		||||
            .font(.headline)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func categoryButton(for category: FeedbackCategory) -> some View {
 | 
			
		||||
        let isSelected = feedbackCategory == category
 | 
			
		||||
 | 
			
		||||
        return Button {
 | 
			
		||||
            feedbackCategory = category
 | 
			
		||||
        } label: {
 | 
			
		||||
            VStack(alignment: .leading, spacing: 6) {
 | 
			
		||||
                Image(systemName: category.iconName)
 | 
			
		||||
                    .font(.subheadline)
 | 
			
		||||
                    .foregroundColor(isSelected ? .accentColor : .secondary)
 | 
			
		||||
                Text(category.title)
 | 
			
		||||
                    .fontWeight(.semibold)
 | 
			
		||||
                    .foregroundColor(.primary)
 | 
			
		||||
                Text(category.subtitle)
 | 
			
		||||
                    .font(.caption)
 | 
			
		||||
                    .foregroundColor(.secondary)
 | 
			
		||||
            }
 | 
			
		||||
            .frame(maxWidth: .infinity, alignment: .leading)
 | 
			
		||||
            .padding(.vertical, 14)
 | 
			
		||||
            .padding(.horizontal, 16)
 | 
			
		||||
            .background(
 | 
			
		||||
                RoundedRectangle(cornerRadius: 16, style: .continuous)
 | 
			
		||||
                    .fill(isSelected ? Color.accentColor.opacity(0.12) : Color(.systemBackground))
 | 
			
		||||
            )
 | 
			
		||||
            .overlay(
 | 
			
		||||
                RoundedRectangle(cornerRadius: 16, style: .continuous)
 | 
			
		||||
                    .stroke(isSelected ? Color.accentColor : Color(.systemGray4), lineWidth: isSelected ? 2 : 1)
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        .buttonStyle(.plain)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func ratingDescription(for rating: Int) -> String {
 | 
			
		||||
        switch rating {
 | 
			
		||||
        case 1:
 | 
			
		||||
            return NSLocalizedString("Что-то пошло не так. Расскажите подробности ниже.", comment: "feedback: rating description 1")
 | 
			
		||||
        case 2:
 | 
			
		||||
            return NSLocalizedString("Мы постараемся всё исправить. Напишите, что смутило.", comment: "feedback: rating description 2")
 | 
			
		||||
        case 3:
 | 
			
		||||
            return NSLocalizedString("Неплохо, но можно лучше — что добавить?", comment: "feedback: rating description 3")
 | 
			
		||||
        case 4:
 | 
			
		||||
            return NSLocalizedString("Спасибо! Что поможет нам добраться до пятёрки?", comment: "feedback: rating description 4")
 | 
			
		||||
        case 5:
 | 
			
		||||
            return NSLocalizedString("Прекрасно! Расскажите, что понравилось больше всего.", comment: "feedback: rating description 5")
 | 
			
		||||
        default:
 | 
			
		||||
            return ""
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func submitSuggestion() {
 | 
			
		||||
        guard suggestionIsValid else { return }
 | 
			
		||||
        let trimmed = suggestion.trimmingCharacters(in: .whitespacesAndNewlines)
 | 
			
		||||
        guard canSubmit else { return }
 | 
			
		||||
 | 
			
		||||
        let trimmedSuggestion = suggestion.trimmingCharacters(in: .whitespacesAndNewlines)
 | 
			
		||||
        let trimmedEmail = contactEmail.trimmingCharacters(in: .whitespacesAndNewlines)
 | 
			
		||||
 | 
			
		||||
        dismissKeyboardIfNeeded()
 | 
			
		||||
 | 
			
		||||
        isSubmitting = true
 | 
			
		||||
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { // имитируем сетевой вызов
 | 
			
		||||
            submittedSuggestion = trimmed
 | 
			
		||||
        showSubmissionMessage = false
 | 
			
		||||
 | 
			
		||||
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
 | 
			
		||||
            submittedFeedback = SubmittedFeedback(
 | 
			
		||||
                suggestion: trimmedSuggestion,
 | 
			
		||||
                category: feedbackCategory,
 | 
			
		||||
                rating: rating,
 | 
			
		||||
                email: wantsResponse ? trimmedEmail : nil
 | 
			
		||||
            )
 | 
			
		||||
            suggestion = ""
 | 
			
		||||
            rating = 0
 | 
			
		||||
            if !wantsResponse {
 | 
			
		||||
                contactEmail = ""
 | 
			
		||||
            }
 | 
			
		||||
            withAnimation {
 | 
			
		||||
                showSubmissionMessage = true
 | 
			
		||||
            }
 | 
			
		||||
            isSubmitting = false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func successSection(for submission: SubmittedFeedback) -> some View {
 | 
			
		||||
        VStack(alignment: .leading, spacing: 10) {
 | 
			
		||||
            Label {
 | 
			
		||||
                Text(NSLocalizedString("Спасибо! Мы получили ваш отзыв", comment: "feedback: success title"))
 | 
			
		||||
            } icon: {
 | 
			
		||||
                Image(systemName: "checkmark.seal.fill")
 | 
			
		||||
                    .foregroundColor(.accentColor)
 | 
			
		||||
            }
 | 
			
		||||
            .font(.headline)
 | 
			
		||||
 | 
			
		||||
            Text(String(format: NSLocalizedString("Тема: %@", comment: "feedback: success category"), submission.category.title))
 | 
			
		||||
                .font(.subheadline)
 | 
			
		||||
 | 
			
		||||
            if submission.rating > 0 {
 | 
			
		||||
                Text(String(format: NSLocalizedString("Оценка: %d из 5", comment: "feedback: success rating"), submission.rating))
 | 
			
		||||
                    .font(.subheadline)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
private extension FeedbackView {
 | 
			
		||||
    func dismissKeyboardIfNeeded() {
 | 
			
		||||
        guard isSuggestionFocused else { return }
 | 
			
		||||
        isSuggestionFocused = false
 | 
			
		||||
            if !submission.suggestion.isEmpty {
 | 
			
		||||
                Text(submission.suggestion)
 | 
			
		||||
                    .font(.callout)
 | 
			
		||||
                    .foregroundColor(.secondary)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if let email = submission.email, !email.isEmpty {
 | 
			
		||||
                Text(String(format: NSLocalizedString("Мы свяжемся с вами по адресу %@, как только ответим.", comment: "feedback: success email"), email))
 | 
			
		||||
                    .font(.footnote)
 | 
			
		||||
                    .foregroundColor(.secondary)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .padding()
 | 
			
		||||
        .frame(maxWidth: .infinity, alignment: .leading)
 | 
			
		||||
        .background(
 | 
			
		||||
            RoundedRectangle(cornerRadius: 16, style: .continuous)
 | 
			
		||||
                .fill(Color.accentColor.opacity(0.12))
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func dismissKeyboardIfNeeded() {
 | 
			
		||||
        guard focusedField != nil else { return }
 | 
			
		||||
        focusedField = nil
 | 
			
		||||
 | 
			
		||||
#if canImport(UIKit)
 | 
			
		||||
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
 | 
			
		||||
@ -133,9 +370,99 @@ private extension FeedbackView {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private extension FeedbackView {
 | 
			
		||||
    enum Field: Hashable {
 | 
			
		||||
        case suggestion
 | 
			
		||||
        case email
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct SubmittedFeedback {
 | 
			
		||||
        let suggestion: String
 | 
			
		||||
        let category: FeedbackCategory
 | 
			
		||||
        let rating: Int
 | 
			
		||||
        let email: String?
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum FeedbackCategory: String, CaseIterable, Identifiable {
 | 
			
		||||
        case idea
 | 
			
		||||
        case bug
 | 
			
		||||
        case praise
 | 
			
		||||
        case content
 | 
			
		||||
 | 
			
		||||
        var id: String { rawValue }
 | 
			
		||||
 | 
			
		||||
        var title: String {
 | 
			
		||||
            switch self {
 | 
			
		||||
            case .idea:
 | 
			
		||||
                return NSLocalizedString("Идея", comment: "feedback category: idea")
 | 
			
		||||
            case .bug:
 | 
			
		||||
                return NSLocalizedString("Проблема", comment: "feedback category: bug")
 | 
			
		||||
            case .praise:
 | 
			
		||||
                return NSLocalizedString("Похвала", comment: "feedback category: praise")
 | 
			
		||||
            case .content:
 | 
			
		||||
                return NSLocalizedString("Контент", comment: "feedback category: content")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var subtitle: String {
 | 
			
		||||
            switch self {
 | 
			
		||||
            case .idea:
 | 
			
		||||
                return NSLocalizedString("Предложите, что добавить", comment: "feedback category subtitle: idea")
 | 
			
		||||
            case .bug:
 | 
			
		||||
                return NSLocalizedString("Расскажите, что не работает", comment: "feedback category subtitle: bug")
 | 
			
		||||
            case .praise:
 | 
			
		||||
                return NSLocalizedString("Поделитесь, что понравилось", comment: "feedback category subtitle: praise")
 | 
			
		||||
            case .content:
 | 
			
		||||
                return NSLocalizedString("Сообщите о материалах", comment: "feedback category subtitle: content")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var iconName: String {
 | 
			
		||||
            switch self {
 | 
			
		||||
            case .idea:
 | 
			
		||||
                return "lightbulb"
 | 
			
		||||
            case .bug:
 | 
			
		||||
                return "ant"
 | 
			
		||||
            case .praise:
 | 
			
		||||
                return "heart.fill"
 | 
			
		||||
            case .content:
 | 
			
		||||
                return "doc.richtext"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var promptTitle: String {
 | 
			
		||||
            switch self {
 | 
			
		||||
            case .idea:
 | 
			
		||||
                return NSLocalizedString("Опишите идею", comment: "feedback prompt: idea")
 | 
			
		||||
            case .bug:
 | 
			
		||||
                return NSLocalizedString("Что случилось?", comment: "feedback prompt: bug")
 | 
			
		||||
            case .praise:
 | 
			
		||||
                return NSLocalizedString("Что вам понравилось?", comment: "feedback prompt: praise")
 | 
			
		||||
            case .content:
 | 
			
		||||
                return NSLocalizedString("О каком контенте идёт речь?", comment: "feedback prompt: content")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var placeholder: String {
 | 
			
		||||
            switch self {
 | 
			
		||||
            case .idea:
 | 
			
		||||
                return NSLocalizedString("Например: хотелось бы видеть подборку по интересам...", comment: "feedback placeholder: idea")
 | 
			
		||||
            case .bug:
 | 
			
		||||
                return NSLocalizedString("Например: приложение вылетает, когда я открываю профиль...", comment: "feedback placeholder: bug")
 | 
			
		||||
            case .praise:
 | 
			
		||||
                return NSLocalizedString("Например: понравилась новая лента, потому что...", comment: "feedback placeholder: praise")
 | 
			
		||||
            case .content:
 | 
			
		||||
                return NSLocalizedString("Например: заметил неточную информацию в статье...", comment: "feedback placeholder: content")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct FeedbackView_Previews: PreviewProvider {
 | 
			
		||||
    static var previews: some View {
 | 
			
		||||
        NavigationView {
 | 
			
		||||
            FeedbackView()
 | 
			
		||||
                .environmentObject(ThemeManager())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user