Compare commits
	
		
			4 Commits
		
	
	
		
			5d74af5597
			...
			413e276abe
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 413e276abe | |||
| 47746690d6 | |||
| 020cd8893a | |||
| 102826ac8a | 
@ -187,7 +187,7 @@
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
			buildConfigurationList = 1A6D61C72E7CD03E00B9F736 /* Build configuration list for PBXProject "yobble" */;
 | 
			
		||||
			developmentRegion = en;
 | 
			
		||||
			developmentRegion = ru;
 | 
			
		||||
			hasScannedForEncodings = 0;
 | 
			
		||||
			knownRegions = (
 | 
			
		||||
				en,
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,8 @@ struct TopBarView: View {
 | 
			
		||||
    @Binding var selectedAccount: String
 | 
			
		||||
//    @Binding var sheetType: ProfileTab.SheetType?
 | 
			
		||||
    var accounts: [String]
 | 
			
		||||
    var viewModel: LoginViewModel
 | 
			
		||||
//    var viewModel: LoginViewModel
 | 
			
		||||
    @ObservedObject var viewModel: LoginViewModel
 | 
			
		||||
    
 | 
			
		||||
    // Привязка для управления боковым меню
 | 
			
		||||
    @Binding var isSideMenuPresented: Bool
 | 
			
		||||
@ -45,7 +46,7 @@ struct TopBarView: View {
 | 
			
		||||
                    Spacer()
 | 
			
		||||
                    Button(action: { }) {
 | 
			
		||||
                        HStack(spacing: 4) {
 | 
			
		||||
                            Text(selectedAccount)
 | 
			
		||||
                            Text("@\(viewModel.username)")
 | 
			
		||||
                                .font(.headline)
 | 
			
		||||
                                .foregroundColor(.primary)
 | 
			
		||||
                            Image(systemName: "chevron.down")
 | 
			
		||||
 | 
			
		||||
@ -104,7 +104,6 @@ class AuthService {
 | 
			
		||||
                    // Сохраняем токены в Keychain
 | 
			
		||||
                    KeychainService.shared.save(loginResponse.access_token, forKey: "access_token", service: username)
 | 
			
		||||
                    KeychainService.shared.save(loginResponse.refresh_token, forKey: "refresh_token", service: username)
 | 
			
		||||
                    print("loginResponse.user_id \(loginResponse.user_id)")
 | 
			
		||||
                    KeychainService.shared.save(loginResponse.user_id, forKey: "userId", service: username)
 | 
			
		||||
                    UserDefaults.standard.set(username, forKey: "currentUser")
 | 
			
		||||
                    
 | 
			
		||||
 | 
			
		||||
@ -1,45 +1,56 @@
 | 
			
		||||
{
 | 
			
		||||
  "sourceLanguage" : "en",
 | 
			
		||||
  "sourceLanguage" : "ru",
 | 
			
		||||
  "strings" : {
 | 
			
		||||
    "@yourusername" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "🌍" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "CATEGORY" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Hello, world!" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "loading_placeholder" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "LoginView_button_login" : {
 | 
			
		||||
      "extractionState" : "stale",
 | 
			
		||||
    "@%@" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "ебите меня четверо"
 | 
			
		||||
            "value" : "@%@"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "ok" : {
 | 
			
		||||
      "extractionState" : "stale",
 | 
			
		||||
    "🌍" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "ru" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "55454545"
 | 
			
		||||
            "value" : "🌍"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Fun Fest" : {
 | 
			
		||||
      "comment" : "Fun Fest",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Fun Fest"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Hello, world!" : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Hello, world!"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "OK" : {
 | 
			
		||||
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "OK"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "profile_down_text_1" : {
 | 
			
		||||
 | 
			
		||||
@ -52,15 +63,16 @@
 | 
			
		||||
    },
 | 
			
		||||
    "Push-уведомления" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "SERVICES" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Yobble" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Your Name" : {
 | 
			
		||||
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Yobble"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Активные сессии" : {
 | 
			
		||||
 | 
			
		||||
@ -69,16 +81,37 @@
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Войти" : {
 | 
			
		||||
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Log in"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Выйти из аккаунта" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Где найти сохранённые черновики?" : {
 | 
			
		||||
      "comment" : "FAQ question: drafts"
 | 
			
		||||
    },
 | 
			
		||||
    "Данные" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Двухфакторная аутентификация" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Добавить друзей" : {
 | 
			
		||||
      "comment" : "Add friends",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Add friends"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Другое" : {
 | 
			
		||||
 | 
			
		||||
@ -104,14 +137,48 @@
 | 
			
		||||
    "Заглушка: Хранилище данных" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Заглушка: Частые вопросы" : {
 | 
			
		||||
 | 
			
		||||
    "Загрузка..." : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Loading..."
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Заказы" : {
 | 
			
		||||
      "comment" : "Orders",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Orders"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Закрыть" : {
 | 
			
		||||
      "comment" : "Закрыть"
 | 
			
		||||
      "comment" : "Закрыть",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Close"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Зарегистрироваться" : {
 | 
			
		||||
      "comment" : "Зарегистрироваться"
 | 
			
		||||
      "comment" : "Зарегистрироваться",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Register"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Здесь будут чаты" : {
 | 
			
		||||
 | 
			
		||||
@ -120,22 +187,135 @@
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Инвайт-код (необязательно)" : {
 | 
			
		||||
      "comment" : "Инвайт-код"
 | 
			
		||||
      "comment" : "Инвайт-код",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Invite code (optional)"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "История" : {
 | 
			
		||||
      "comment" : "History",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "History"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Как сбросить пароль?" : {
 | 
			
		||||
      "comment" : "FAQ question: reset password"
 | 
			
		||||
    },
 | 
			
		||||
    "Как связаться с поддержкой?" : {
 | 
			
		||||
      "comment" : "FAQ question: support"
 | 
			
		||||
    },
 | 
			
		||||
    "Корзина" : {
 | 
			
		||||
      "comment" : "Cart",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Cart"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Кошелёк" : {
 | 
			
		||||
      "comment" : "Wallet",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Wallet"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Лента" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Лицо" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Логин" : {
 | 
			
		||||
      "comment" : "Логин"
 | 
			
		||||
      "comment" : "Логин",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Login"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Логин должен быть от 3 до 32 символов (английские буквы, цифры, _)" : {
 | 
			
		||||
      "comment" : "Логин должен быть от 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" : "My Downloads",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "My Downloads"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Мои комментарии" : {
 | 
			
		||||
      "comment" : "My Comments",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "My Comments"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Мой профиль" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Напишите нам через форму обратной связи в разделе \"Поддержка\"." : {
 | 
			
		||||
      "comment" : "FAQ answer: support"
 | 
			
		||||
    },
 | 
			
		||||
    "Настройки" : {
 | 
			
		||||
 | 
			
		||||
      "comment" : "Settings",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Settings"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Не удалось обработать ответ сервера." : {
 | 
			
		||||
 | 
			
		||||
@ -206,12 +386,29 @@
 | 
			
		||||
    "Пароль должен быть от 8 до 128 символов" : {
 | 
			
		||||
      "comment" : "Пароль должен быть от 6 до 32 символов"
 | 
			
		||||
    },
 | 
			
		||||
    "Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям." : {
 | 
			
		||||
      "comment" : "FAQ answer: reset password"
 | 
			
		||||
    },
 | 
			
		||||
    "Поддержка" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Подтверждение пароля" : {
 | 
			
		||||
      "comment" : "Подтверждение пароля"
 | 
			
		||||
    },
 | 
			
		||||
    "Поиск" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Помощь" : {
 | 
			
		||||
      "comment" : "Help Center",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Help Center"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Приглашение достигло лимита использования." : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
@ -256,6 +453,17 @@
 | 
			
		||||
    },
 | 
			
		||||
    "Сервер не отвечает. Попробуйте позже." : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Скан" : {
 | 
			
		||||
      "comment" : "Scan",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Scan"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Слишком много запросов." : {
 | 
			
		||||
 | 
			
		||||
@ -268,9 +476,37 @@
 | 
			
		||||
    },
 | 
			
		||||
    "Уведомления" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Центр авторов" : {
 | 
			
		||||
      "comment" : "Creator Center",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Creator Center"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Частые вопросы" : {
 | 
			
		||||
      "comment" : "FAQ navigation title"
 | 
			
		||||
    },
 | 
			
		||||
    "Чаты" : {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    "Черновики" : {
 | 
			
		||||
      "comment" : "Drafts",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
            "state" : "translated",
 | 
			
		||||
            "value" : "Drafts"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Черновики доступны в боковом меню в разделе Drafts." : {
 | 
			
		||||
      "comment" : "FAQ answer: drafts"
 | 
			
		||||
    },
 | 
			
		||||
    "Язык" : {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ class LoginViewModel: ObservableObject {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init() {
 | 
			
		||||
        loadStoredUser()
 | 
			
		||||
//        loadStoredUser()
 | 
			
		||||
 | 
			
		||||
        // Запускаем автологин
 | 
			
		||||
        autoLogin()
 | 
			
		||||
@ -113,6 +113,6 @@ class LoginViewModel: ObservableObject {
 | 
			
		||||
        username = defaults.string(forKey: DefaultsKeys.currentUser) ?? ""
 | 
			
		||||
        userId = KeychainService.shared.get(forKey: DefaultsKeys.userId, service: username) ?? ""
 | 
			
		||||
        
 | 
			
		||||
        print("username: \(username) | userId: \(userId)")
 | 
			
		||||
        if AppConfig.DEBUG{ print("username: \(username) | userId: \(userId)")}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,12 @@ struct LoginView: View {
 | 
			
		||||
    @Environment(\.colorScheme) private var colorScheme
 | 
			
		||||
    
 | 
			
		||||
    @State private var isShowingRegistration = false
 | 
			
		||||
    @FocusState private var focusedField: Field?
 | 
			
		||||
 | 
			
		||||
    private enum Field: Hashable {
 | 
			
		||||
        case username
 | 
			
		||||
        case password
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var isUsernameValid: Bool {
 | 
			
		||||
        let pattern = "^[A-Za-z0-9_]{3,32}$"
 | 
			
		||||
@ -29,7 +35,7 @@ struct LoginView: View {
 | 
			
		||||
            Color.clear // чтобы поймать тап
 | 
			
		||||
                .contentShape(Rectangle())
 | 
			
		||||
                .onTapGesture {
 | 
			
		||||
                    hideKeyboard()
 | 
			
		||||
                    focusedField = nil
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            VStack {
 | 
			
		||||
@ -46,8 +52,8 @@ struct LoginView: View {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .onTapGesture {
 | 
			
		||||
                hideKeyboard()
 | 
			
		||||
            }
 | 
			
		||||
                    focusedField = nil
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Spacer()
 | 
			
		||||
 | 
			
		||||
@ -57,6 +63,7 @@ struct LoginView: View {
 | 
			
		||||
                    .cornerRadius(8)
 | 
			
		||||
                    .autocapitalization(.none)
 | 
			
		||||
                    .disableAutocorrection(true)
 | 
			
		||||
                    .focused($focusedField, equals: .username)
 | 
			
		||||
                    .onChange(of: viewModel.username) { newValue in
 | 
			
		||||
                        if newValue.count > 32 {
 | 
			
		||||
                            viewModel.username = String(newValue.prefix(32))
 | 
			
		||||
@ -76,6 +83,7 @@ struct LoginView: View {
 | 
			
		||||
                    .background(Color(.secondarySystemBackground))
 | 
			
		||||
                    .cornerRadius(8)
 | 
			
		||||
                    .autocapitalization(.none)
 | 
			
		||||
                    .focused($focusedField, equals: .password)
 | 
			
		||||
                    .onChange(of: viewModel.password) { newValue in
 | 
			
		||||
                        if newValue.count > 32 {
 | 
			
		||||
                            viewModel.password = String(newValue.prefix(32))
 | 
			
		||||
@ -140,8 +148,8 @@ struct LoginView: View {
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            .onTapGesture {
 | 
			
		||||
            hideKeyboard()
 | 
			
		||||
        }
 | 
			
		||||
                focusedField = nil
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
@ -169,10 +177,6 @@ struct LoginView: View {
 | 
			
		||||
        UIApplication.shared.open(url)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private func hideKeyboard() {
 | 
			
		||||
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct LoginView_Previews: PreviewProvider {
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,15 @@ struct RegistrationView: View {
 | 
			
		||||
    @State private var showError: Bool = false
 | 
			
		||||
    @State private var errorMessage: String = ""
 | 
			
		||||
 | 
			
		||||
    @FocusState private var focusedField: Field?
 | 
			
		||||
 | 
			
		||||
    private enum Field: Hashable {
 | 
			
		||||
        case username
 | 
			
		||||
        case password
 | 
			
		||||
        case confirmPassword
 | 
			
		||||
        case invite
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var isUsernameValid: Bool {
 | 
			
		||||
        let pattern = "^[A-Za-z0-9_]{3,32}$"
 | 
			
		||||
        return username.range(of: pattern, options: .regularExpression) != nil
 | 
			
		||||
@ -45,7 +54,7 @@ struct RegistrationView: View {
 | 
			
		||||
                ZStack(alignment: .top) {
 | 
			
		||||
                    Color.clear
 | 
			
		||||
                        .contentShape(Rectangle())
 | 
			
		||||
                        .onTapGesture { hideKeyboard() }
 | 
			
		||||
                        .onTapGesture { focusedField = nil }
 | 
			
		||||
 | 
			
		||||
                    VStack(alignment: .leading, spacing: 16) {
 | 
			
		||||
                        Group {
 | 
			
		||||
@ -53,6 +62,7 @@ struct RegistrationView: View {
 | 
			
		||||
                                TextField(NSLocalizedString("Логин", comment: "Логин"), text: $username)
 | 
			
		||||
                                    .autocapitalization(.none)
 | 
			
		||||
                                    .disableAutocorrection(true)
 | 
			
		||||
                                    .focused($focusedField, equals: .username)
 | 
			
		||||
                                Spacer()
 | 
			
		||||
                                if !username.isEmpty {
 | 
			
		||||
                                    Image(systemName: isUsernameValid ? "checkmark.circle" : "xmark.circle")
 | 
			
		||||
@ -79,6 +89,7 @@ struct RegistrationView: View {
 | 
			
		||||
                            HStack {
 | 
			
		||||
                                SecureField(NSLocalizedString("Пароль", comment: "Пароль"), text: $password)
 | 
			
		||||
                                    .autocapitalization(.none)
 | 
			
		||||
                                    .focused($focusedField, equals: .password)
 | 
			
		||||
                                Spacer()
 | 
			
		||||
                                if !password.isEmpty {
 | 
			
		||||
                                    Image(systemName: isPasswordValid ? "checkmark.circle" : "xmark.circle")
 | 
			
		||||
@ -104,6 +115,7 @@ struct RegistrationView: View {
 | 
			
		||||
                            HStack {
 | 
			
		||||
                                SecureField(NSLocalizedString("Подтверждение пароля", comment: "Подтверждение пароля"), text: $confirmPassword)
 | 
			
		||||
                                    .autocapitalization(.none)
 | 
			
		||||
                                    .focused($focusedField, equals: .confirmPassword)
 | 
			
		||||
                                Spacer()
 | 
			
		||||
                                if !confirmPassword.isEmpty {
 | 
			
		||||
                                    Image(systemName: isConfirmPasswordValid ? "checkmark.circle" : "xmark.circle")
 | 
			
		||||
@ -132,6 +144,7 @@ struct RegistrationView: View {
 | 
			
		||||
                                .cornerRadius(8)
 | 
			
		||||
                                .autocapitalization(.none)
 | 
			
		||||
                                .disableAutocorrection(true)
 | 
			
		||||
                                .focused($focusedField, equals: .invite)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        Button(action: registerUser) {
 | 
			
		||||
@ -189,14 +202,10 @@ struct RegistrationView: View {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func dismissSheet() {
 | 
			
		||||
        hideKeyboard()
 | 
			
		||||
        focusedField = nil
 | 
			
		||||
        isPresented = false
 | 
			
		||||
        presentationMode.wrappedValue.dismiss()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func hideKeyboard() {
 | 
			
		||||
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ struct SplashScreenView: View {
 | 
			
		||||
            ProgressView()
 | 
			
		||||
                .progressViewStyle(CircularProgressViewStyle())
 | 
			
		||||
                .scaleEffect(1.5)
 | 
			
		||||
            Text(NSLocalizedString("loading_placeholder", comment: ""))
 | 
			
		||||
            Text(NSLocalizedString("Загрузка...", comment: ""))
 | 
			
		||||
                .padding(.top, 10)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -7,12 +7,12 @@ struct CustomTabBar: View {
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        HStack {
 | 
			
		||||
            // Tab 1: Feed
 | 
			
		||||
            TabBarButton(systemName: "list.bullet.rectangle", text: "Лента", isSelected: selectedTab == 0) {
 | 
			
		||||
            TabBarButton(systemName: "list.bullet.rectangle", text: NSLocalizedString("Лента", comment: ""), isSelected: selectedTab == 0) {
 | 
			
		||||
                selectedTab = 0
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Tab 2: Search
 | 
			
		||||
            TabBarButton(systemName: "magnifyingglass", text: "Поиск", isSelected: selectedTab == 1) {
 | 
			
		||||
            TabBarButton(systemName: "magnifyingglass", text: NSLocalizedString("Поиск", comment: ""), isSelected: selectedTab == 1) {
 | 
			
		||||
                selectedTab = 1
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -22,12 +22,12 @@ struct CustomTabBar: View {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Tab 3: Chats
 | 
			
		||||
            TabBarButton(systemName: "bubble.left.and.bubble.right.fill", text: "Чаты", isSelected: selectedTab == 2) {
 | 
			
		||||
            TabBarButton(systemName: "bubble.left.and.bubble.right.fill", text: NSLocalizedString("Чаты", comment: ""), isSelected: selectedTab == 2) {
 | 
			
		||||
                selectedTab = 2
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Tab 4: Profile
 | 
			
		||||
            TabBarButton(systemName: "person.crop.square", text: "Лицо", isSelected: selectedTab == 3) {
 | 
			
		||||
            TabBarButton(systemName: "person.crop.square", text: NSLocalizedString("Лицо", comment: ""), isSelected: selectedTab == 3) {
 | 
			
		||||
                selectedTab = 3
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@ struct MainView: View {
 | 
			
		||||
                    .allowsHitTesting(menuOffset > 0)
 | 
			
		||||
 | 
			
		||||
                // Боковое меню
 | 
			
		||||
                SideMenuView(isPresented: $isSideMenuPresented)
 | 
			
		||||
                SideMenuView(viewModel: viewModel, isPresented: $isSideMenuPresented)
 | 
			
		||||
                    .frame(width: menuWidth)
 | 
			
		||||
                    .offset(x: -menuWidth + menuOffset) // Новая логика смещения
 | 
			
		||||
                    .ignoresSafeArea(edges: .vertical)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										47
									
								
								yobble/Views/Tab/Settings/FAQView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								yobble/Views/Tab/Settings/FAQView.swift
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
import SwiftUI
 | 
			
		||||
 | 
			
		||||
struct FAQView: View {
 | 
			
		||||
    private struct FAQItem: Identifiable {
 | 
			
		||||
        let id = UUID()
 | 
			
		||||
        let question: String
 | 
			
		||||
        let answer: String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private let faqItems: [FAQItem] = [
 | 
			
		||||
        FAQItem(
 | 
			
		||||
            question: NSLocalizedString("Как сбросить пароль?", comment: "FAQ question: reset password"),
 | 
			
		||||
            answer: NSLocalizedString("Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям.", comment: "FAQ answer: reset password")
 | 
			
		||||
        ),
 | 
			
		||||
        FAQItem(
 | 
			
		||||
            question: NSLocalizedString("Где найти сохранённые черновики?", comment: "FAQ question: drafts"),
 | 
			
		||||
            answer: NSLocalizedString("Черновики доступны в боковом меню в разделе Drafts.", comment: "FAQ answer: drafts")
 | 
			
		||||
        ),
 | 
			
		||||
        FAQItem(
 | 
			
		||||
            question: NSLocalizedString("Как связаться с поддержкой?", comment: "FAQ question: support"),
 | 
			
		||||
            answer: NSLocalizedString("Напишите нам через форму обратной связи в разделе \"Поддержка\".", comment: "FAQ answer: support")
 | 
			
		||||
        )
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        List(faqItems) { item in
 | 
			
		||||
            VStack(alignment: .leading, spacing: 6) {
 | 
			
		||||
                Text(item.question)
 | 
			
		||||
                    .font(.headline)
 | 
			
		||||
                Text(item.answer)
 | 
			
		||||
                    .font(.subheadline)
 | 
			
		||||
                    .foregroundColor(.secondary)
 | 
			
		||||
            }
 | 
			
		||||
            .padding(.vertical, 6)
 | 
			
		||||
        }
 | 
			
		||||
        .listStyle(.insetGrouped)
 | 
			
		||||
        .navigationTitle(NSLocalizedString("Частые вопросы", comment: "FAQ navigation title"))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct FAQView_Previews: PreviewProvider {
 | 
			
		||||
    static var previews: some View {
 | 
			
		||||
        NavigationView {
 | 
			
		||||
            FAQView()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -54,7 +54,7 @@ struct SettingsView: View {
 | 
			
		||||
 | 
			
		||||
            // MARK: - Поддержка
 | 
			
		||||
            Section(header: Text("Поддержка")) {
 | 
			
		||||
                NavigationLink(destination: Text("Заглушка: Частые вопросы")) {
 | 
			
		||||
                NavigationLink(destination: FAQView()) {
 | 
			
		||||
                    Label("Частые вопросы", systemImage: "questionmark.circle")
 | 
			
		||||
                }
 | 
			
		||||
                NavigationLink(destination: Text("Заглушка: Обратная связь")) {
 | 
			
		||||
 | 
			
		||||
@ -53,8 +53,10 @@ struct SideMenuFooterButton: View {
 | 
			
		||||
                Text(title)
 | 
			
		||||
                    .font(.caption2)
 | 
			
		||||
            }
 | 
			
		||||
            .frame(maxWidth: .infinity)
 | 
			
		||||
            .foregroundColor(.primary)
 | 
			
		||||
        }
 | 
			
		||||
        .frame(maxWidth: .infinity)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -62,24 +64,18 @@ struct SideMenuFooterButton: View {
 | 
			
		||||
// --- MAIN VIEW ---
 | 
			
		||||
 | 
			
		||||
struct SideMenuView: View {
 | 
			
		||||
    @ObservedObject var viewModel: LoginViewModel
 | 
			
		||||
    @EnvironmentObject var themeManager: ThemeManager
 | 
			
		||||
    @Environment(\.colorScheme) var colorScheme
 | 
			
		||||
    @Binding var isPresented: Bool
 | 
			
		||||
    @State private var isAccountListExpanded = false
 | 
			
		||||
    @State private var navigateToSettings = false
 | 
			
		||||
    @State private var navigateToFAQ = false
 | 
			
		||||
 | 
			
		||||
    // Adjustable paddings
 | 
			
		||||
    private let topPadding: CGFloat = 66
 | 
			
		||||
    private let bottomPadding: CGFloat = 34
 | 
			
		||||
    
 | 
			
		||||
    // Dummy account data
 | 
			
		||||
    private let accounts: [Account] = [
 | 
			
		||||
        Account(name: "Your Name", username: "@yourusername", isCurrent: true),
 | 
			
		||||
        Account(name: "Second Account", username: "@second", isCurrent: false),
 | 
			
		||||
        Account(name: "Another One", username: "@another", isCurrent: false),
 | 
			
		||||
        Account(name: "Test User", username: "@test", isCurrent: false),
 | 
			
		||||
        Account(name: "Creative Profile", username: "@creative", isCurrent: false)
 | 
			
		||||
    ]
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    private var themeToggleButton: some View {
 | 
			
		||||
        Button(action: {
 | 
			
		||||
            themeManager.toggleTheme(from: colorScheme)
 | 
			
		||||
@ -106,124 +102,58 @@ struct SideMenuView: View {
 | 
			
		||||
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        VStack(alignment: .leading, spacing: 0) {
 | 
			
		||||
            NavigationLink(
 | 
			
		||||
                destination: SettingsView(viewModel: viewModel),
 | 
			
		||||
                isActive: $navigateToSettings
 | 
			
		||||
            ) {
 | 
			
		||||
                EmptyView()
 | 
			
		||||
            }
 | 
			
		||||
            NavigationLink(
 | 
			
		||||
                destination: FAQView(),
 | 
			
		||||
                isActive: $navigateToFAQ
 | 
			
		||||
            ) {
 | 
			
		||||
                EmptyView()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ScrollView(showsIndicators: false) {
 | 
			
		||||
                VStack(alignment: .leading, spacing: 0) { // Parent VStack
 | 
			
		||||
                    
 | 
			
		||||
                    // --- Header ---
 | 
			
		||||
                    HStack(alignment: .top) {
 | 
			
		||||
                        Button(action: {  }) {
 | 
			
		||||
                            Image(systemName: "person.circle.fill")
 | 
			
		||||
                                .resizable()
 | 
			
		||||
                                .frame(width: 60, height: 60)
 | 
			
		||||
                                .foregroundColor(.gray)
 | 
			
		||||
                        }
 | 
			
		||||
                        Spacer()
 | 
			
		||||
                        themeToggleButton
 | 
			
		||||
                    }
 | 
			
		||||
                    .padding(.horizontal, 20)
 | 
			
		||||
                    .padding(.top, topPadding)
 | 
			
		||||
                    .padding(.bottom, 10)
 | 
			
		||||
                    
 | 
			
		||||
                    // --- Header Button ---
 | 
			
		||||
                    Button(action: {
 | 
			
		||||
                        withAnimation(.spring()) {
 | 
			
		||||
                            isAccountListExpanded.toggle()
 | 
			
		||||
                        }
 | 
			
		||||
                    }) {
 | 
			
		||||
                        HStack {
 | 
			
		||||
                            VStack(alignment: .leading) {
 | 
			
		||||
                                Text("Your Name")
 | 
			
		||||
                                    .font(.title3).bold()
 | 
			
		||||
                                Text("@yourusername")
 | 
			
		||||
                                    .font(.footnote)
 | 
			
		||||
                            }
 | 
			
		||||
                            .foregroundColor(.primary)
 | 
			
		||||
                            
 | 
			
		||||
                            Spacer()
 | 
			
		||||
                            
 | 
			
		||||
                            Image(systemName: isAccountListExpanded ? "chevron.up" : "chevron.down")
 | 
			
		||||
                                .font(.headline)
 | 
			
		||||
                                .foregroundColor(.secondary)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .padding(.horizontal, 20)
 | 
			
		||||
                    .padding(.bottom, 10)
 | 
			
		||||
 | 
			
		||||
                    // --- Collapsible Account List in a clipped container ---
 | 
			
		||||
                    VStack {
 | 
			
		||||
                        if isAccountListExpanded {
 | 
			
		||||
                            VStack(alignment: .leading, spacing: 15) {
 | 
			
		||||
                                ForEach(accounts) { account in
 | 
			
		||||
                                    HStack {
 | 
			
		||||
                                        Button(action: {  }) {
 | 
			
		||||
                                            ZStack {
 | 
			
		||||
                                                Image(systemName: "person.circle.fill")
 | 
			
		||||
                                                    .resizable()
 | 
			
		||||
                                                    .frame(width: 32, height: 32) // Smaller icon
 | 
			
		||||
                                                    .foregroundColor(.secondary)
 | 
			
		||||
                                                
 | 
			
		||||
                                                if account.isCurrent {
 | 
			
		||||
                                                    Image(systemName: "checkmark.circle.fill")
 | 
			
		||||
                                                        .foregroundColor(.blue)
 | 
			
		||||
                                                        .background(Circle().fill(Color(UIColor.systemBackground)))
 | 
			
		||||
                                                        .font(.body) // Smaller checkmark
 | 
			
		||||
                                                        .offset(x: 11, y: 11) // Adjusted offset
 | 
			
		||||
                                                }
 | 
			
		||||
                                            }
 | 
			
		||||
                                            
 | 
			
		||||
                                            VStack(alignment: .leading) {
 | 
			
		||||
                                                Text(account.name).font(.footnote).bold() // Smaller text
 | 
			
		||||
                                                Text(account.username).font(.caption2)      // Smaller text
 | 
			
		||||
                                            }
 | 
			
		||||
                                            .foregroundColor(.primary)
 | 
			
		||||
                                        }
 | 
			
		||||
                                                                            
 | 
			
		||||
                                        Spacer()
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            .padding(.horizontal, 20)
 | 
			
		||||
                            .padding(.vertical, 10)
 | 
			
		||||
                            .transition(.slideAndFade)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .clipped()
 | 
			
		||||
                    
 | 
			
		||||
                    // Menu Items
 | 
			
		||||
                    VStack(alignment: .leading, spacing: 20) {
 | 
			
		||||
                        // Section 1
 | 
			
		||||
                        VStack(alignment: .leading, spacing: 7) {
 | 
			
		||||
                            SideMenuButton(icon: "person.2.fill", title: "People You May Like", action: {})
 | 
			
		||||
                            SideMenuButton(icon: "star.fill", title: "Fun Fest", action: {})
 | 
			
		||||
                            SideMenuButton(icon: "lightbulb.fill", title: "Creator Center", action: {})
 | 
			
		||||
                            SideMenuButton(icon: "person.2.fill", title: NSLocalizedString("Добавить друзей", comment: "Add friends"), action: {})
 | 
			
		||||
                            SideMenuButton(icon: "star.fill", title: NSLocalizedString("Fun Fest", comment: "Fun Fest"), action: {})
 | 
			
		||||
                            SideMenuButton(icon: "lightbulb.fill", title: NSLocalizedString("Центр авторов", comment: "Creator Center"), action: {})
 | 
			
		||||
                        }
 | 
			
		||||
                        .padding(.top, topPadding)
 | 
			
		||||
 | 
			
		||||
                        Divider()
 | 
			
		||||
 | 
			
		||||
                        // Section 2
 | 
			
		||||
                        VStack(alignment: .leading, spacing: 7) {
 | 
			
		||||
                            Text("CATEGORY").font(.caption2).foregroundColor(.secondary)
 | 
			
		||||
                            SideMenuButton(icon: "doc.text", title: "Drafts", action: {})
 | 
			
		||||
                            SideMenuButton(icon: "bubble.left", title: "My Comments", action: {})
 | 
			
		||||
                            SideMenuButton(icon: "clock", title: "History", action: {})
 | 
			
		||||
                            SideMenuButton(icon: "arrow.down.circle", title: "My Downloads", action: {})
 | 
			
		||||
//                            Text("CATEGORY").font(.caption2).foregroundColor(.secondary)
 | 
			
		||||
                            SideMenuButton(icon: "doc.text", title: NSLocalizedString("Черновики", comment: "Drafts"), action: {})
 | 
			
		||||
                            SideMenuButton(icon: "bubble.left", title: NSLocalizedString("Мои комментарии", comment: "My Comments"), action: {})
 | 
			
		||||
                            SideMenuButton(icon: "clock", title: NSLocalizedString("История", comment: "History"), action: {})
 | 
			
		||||
                            SideMenuButton(icon: "arrow.down.circle", title: NSLocalizedString("Мои загрузки", comment: "My Downloads"), action: {})
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        Divider()
 | 
			
		||||
 | 
			
		||||
                        // Section 3
 | 
			
		||||
                        VStack(alignment: .leading, spacing: 7) {
 | 
			
		||||
                            Text("SERVICES").font(.caption2).foregroundColor(.secondary)
 | 
			
		||||
                            SideMenuButton(icon: "shippingbox", title: "Orders", action: {})
 | 
			
		||||
                            SideMenuButton(icon: "cart", title: "Cart", action: {})
 | 
			
		||||
                            SideMenuButton(icon: "wallet.pass", title: "Wallet", action: {})
 | 
			
		||||
//                            Text("SERVICES").font(.caption2).foregroundColor(.secondary)
 | 
			
		||||
                            SideMenuButton(icon: "shippingbox", title: NSLocalizedString("Заказы", comment: "Orders"), action: {})
 | 
			
		||||
                            SideMenuButton(icon: "cart", title: NSLocalizedString("Корзина", comment: "Cart"), action: {})
 | 
			
		||||
                            SideMenuButton(icon: "wallet.pass", title: NSLocalizedString("Кошелёк", comment: "Wallet"), action: {})
 | 
			
		||||
                        }
 | 
			
		||||
                        
 | 
			
		||||
                        Divider()
 | 
			
		||||
                        
 | 
			
		||||
                        // Section 4
 | 
			
		||||
                        VStack(alignment: .leading, spacing: 15) {
 | 
			
		||||
                            SideMenuButton(icon: "square.grid.2x2", title: "Applets", action: {})
 | 
			
		||||
                            SideMenuButton(icon: "square.grid.2x2", title: NSLocalizedString("Мини-приложения", comment: "Applets"), action: {})
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .padding()
 | 
			
		||||
@ -235,14 +165,35 @@ struct SideMenuView: View {
 | 
			
		||||
            Spacer()
 | 
			
		||||
 | 
			
		||||
            // Footer
 | 
			
		||||
            HStack(spacing: 20) {
 | 
			
		||||
                Spacer()
 | 
			
		||||
                SideMenuFooterButton(icon: "qrcode.viewfinder", title: "Scan", action: {})
 | 
			
		||||
                SideMenuFooterButton(icon: "questionmark.circle", title: "Help Center", action: {})
 | 
			
		||||
                SideMenuFooterButton(icon: "gear", title: "Settings", action: {})
 | 
			
		||||
                Spacer()
 | 
			
		||||
            HStack(spacing: 0) {
 | 
			
		||||
                SideMenuFooterButton(
 | 
			
		||||
                    icon: "qrcode.viewfinder",
 | 
			
		||||
                    title: NSLocalizedString("Скан", comment: "Scan"),
 | 
			
		||||
                    action: {}
 | 
			
		||||
                )
 | 
			
		||||
                Spacer(minLength: 40)
 | 
			
		||||
                SideMenuFooterButton(
 | 
			
		||||
                    icon: "questionmark.circle",
 | 
			
		||||
                    title: NSLocalizedString("Помощь", comment: "Help Center")
 | 
			
		||||
                ) {
 | 
			
		||||
                    withAnimation(.easeInOut) {
 | 
			
		||||
                        isPresented = false
 | 
			
		||||
                    }
 | 
			
		||||
                    navigateToFAQ = true
 | 
			
		||||
                }
 | 
			
		||||
                Spacer(minLength: 40)
 | 
			
		||||
                SideMenuFooterButton(
 | 
			
		||||
                    icon: "gear",
 | 
			
		||||
                    title: NSLocalizedString("Настройки", comment: "Settings")
 | 
			
		||||
                ) {
 | 
			
		||||
                    withAnimation(.easeInOut) {
 | 
			
		||||
                        isPresented = false
 | 
			
		||||
                    }
 | 
			
		||||
                    navigateToSettings = true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .padding()
 | 
			
		||||
            .padding(.horizontal, 28)
 | 
			
		||||
            .padding(.vertical)
 | 
			
		||||
            .padding(.bottom, bottomPadding)
 | 
			
		||||
        }
 | 
			
		||||
        .background(Color(UIColor.systemBackground))
 | 
			
		||||
@ -253,7 +204,10 @@ struct SideMenuView: View {
 | 
			
		||||
 | 
			
		||||
struct SideMenuView_Previews: PreviewProvider {
 | 
			
		||||
    static var previews: some View {
 | 
			
		||||
        SideMenuView(isPresented: .constant(true))
 | 
			
		||||
            .environmentObject(ThemeManager())
 | 
			
		||||
        let mockViewModel = LoginViewModel()
 | 
			
		||||
        NavigationView {
 | 
			
		||||
            SideMenuView(viewModel: mockViewModel, isPresented: .constant(true))
 | 
			
		||||
                .environmentObject(ThemeManager())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user