Compare commits

..

7 Commits

Author SHA1 Message Date
cheykrym
bb8c9a2b91 home page 2025-07-16 07:31:34 +03:00
cheykrym
a477ce2f78 add post scroll 2025-07-16 07:24:32 +03:00
cheykrym
cca9dd3ce3 post update 2025-07-16 07:14:30 +03:00
cheykrym
38de1cc204 profile patch 2025-07-16 07:01:03 +03:00
cheykrym
71b19f15ba profile deleted swipe 2025-07-16 06:58:33 +03:00
cheykrym
ff155b23e5 profile fix 2025-07-16 06:52:35 +03:00
cheykrym
2e821a0075 profile patch 2025-07-16 06:40:07 +03:00
25 changed files with 1008 additions and 319 deletions

45
Shared/Models/Post.swift Normal file
View File

@ -0,0 +1,45 @@
import Foundation
struct PostIDWrapper: Identifiable {
let id: UUID
}
struct Post: Identifiable, Codable {
let id: UUID // Уникальный идентификатор поста
let title: String? // Название поста
let description: String? // Описание поста
let media: [MediaItem] // Массив медиафайлов
let mediaOrder: [UUID]? // Порядок отображения медиа
let thumbnailID: UUID? // Превью для видео
let duration: Double? // Длительность видео/аудио
let createdAt: Date // Дата создания
let updatedAt: Date // Дата обновления
let views: Int // Просмотры
let likes: Int // Лайки
let saves: Int // В сохранённом
let commentsCount: Int // Кол-во комментариев
let isLikedByCurrentUser: Bool // Лайк текущим юзером
let isSavedByCurrentUser: Bool // Сохранено текущим юзером
let authorID: String // Автор
let authorUsername: String // Имя пользователя автора
let hashtags: [String]? // Хэштеги
let location: String? // Гео
let languageCode: [String]? // Язык
let accessLevel: AccessLevel // Доступ (публичный и т.п.)
}
enum MediaType: String, Codable {
case photo
case video
}
struct MediaItem: Identifiable, Codable {
let id: UUID
let type: MediaType
}
enum AccessLevel: String, Codable {
case `public`
case friends
case archive
}

View File

@ -0,0 +1,144 @@
import Foundation
class PostService {
static let shared = PostService()
private var posts: [Post] = []
init() {
generateMockPosts()
}
private func generateMockPosts() {
guard posts.isEmpty else { return }
let sampleTitles = [
"Обзор TikTok UI",
"Мой первый ролик",
"Котик в кадре",
"SwiftUI мастер-класс",
"Анимации и переходы",
"Съёмка с дрона",
"Урок по дизайну",
"Как сделать свайпы",
"Лучший UI 2025",
"Мой первый ролик",
"Котик в кадре",
"SwiftUI мастер-класс",
"Анимации и переходы",
"Съёмка с дрона",
"Урок по дизайну",
"Как сделать свайпы",
"Лучший UI 2025",
"Мой первый ролик",
"Котик в кадре",
"SwiftUI мастер-класс",
"Анимации и переходы",
"Съёмка с дрона",
"Урок по дизайну",
"Как сделать свайпы",
"Лучший UI 2025",
"Мой первый ролик",
"Котик в кадре",
"SwiftUI мастер-класс",
"Анимации и переходы",
"Съёмка с дрона",
"Урок по дизайну",
"Как сделать свайпы",
"Лучший UI 2025",
"Мой первый ролик",
"Котик в кадре",
"SwiftUI мастер-класс",
"Анимации и переходы",
"Съёмка с дрона",
"Урок по дизайну",
"Как сделать свайпы",
"Лучший UI 2025",
"Завершаем проект"
]
let sampleDescriptions = [
"Первый тестовый пост с видео",
"Фейковый контент для ленты",
"Видео с котиком 🐱",
"Интерфейс в стиле TikTok",
"Просто тестирую отображение",
"Код и UI — любовь",
"Видео в реальном времени",
"Интересный UX пример",
"Анимации, переходы, свайпы",
"Фейковый контент для ленты",
"Видео с котиком 🐱",
"Интерфейс в стиле TikTok",
"Просто тестирую отображение",
"Код и UI — любовь",
"Видео в реальном времени",
"Интересный UX пример",
"Анимации, переходы, свайпы",
"Фейковый контент для ленты",
"Видео с котиком 🐱",
"Интерфейс в стиле TikTok",
"Просто тестирую отображение",
"Код и UI — любовь",
"Видео в реальном времени",
"Интересный UX пример",
"Анимации, переходы, свайпы",
"Фейковый контент для ленты",
"Видео с котиком 🐱",
"Интерфейс в стиле TikTok",
"Просто тестирую отображение",
"Код и UI — любовь",
"Видео в реальном времени",
"Интересный UX пример",
"Анимации, переходы, свайпы",
"Вот это да, пост работает!"
]
for i in 0..<32 {
let mediaID = UUID()
let thumbID = Bool.random() ? UUID() : nil
let postID = UUID()
let authorId = "user_\(Int.random(in: 1...5))"
let post = Post(
id: postID,
title: sampleTitles[i],
description: sampleDescriptions[i],
media: [
MediaItem(id: mediaID, type: .video)
],
mediaOrder: [mediaID],
thumbnailID: thumbID,
duration: Double.random(in: 15.0...180.0),
createdAt: Date(),
updatedAt: Date(),
views: Int.random(in: 1_000...10_000),
likes: Int.random(in: 100...2_000),
saves: Int.random(in: 10...500),
commentsCount: Int.random(in: 0...100),
isLikedByCurrentUser: Bool.random(),
isSavedByCurrentUser: Bool.random(),
authorID: authorId,
authorUsername: "username_\(authorId.split(separator: "_").last ?? "")",
hashtags: ["#тест", "#видео", "#swiftui", "#ui"].shuffled().prefix(2).map { $0 },
location: Bool.random() ? "Москва" : nil,
languageCode: Bool.random() ? ["ru", "en"] : ["ru"],
accessLevel: [.public, .friends, .archive].randomElement()!
)
posts.append(post)
}
}
func fetchAllPosts(completion: @escaping ([Post]) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
completion(self.posts)
}
}
func fetchPost(by id: UUID, completion: @escaping (Post?) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let result = self.posts.first { $0.id == id }
completion(result)
}
}
}

View File

@ -0,0 +1,33 @@
import SwiftUI
struct HomeTab: View {
@State private var posts: [Post] = []
@State private var isLoading = true
var body: some View {
NavigationView {
VStack {
if isLoading {
ProgressView("Загрузка ленты...")
} else {
ScrollView {
LazyVStack(spacing: 24) {
ForEach(posts) { post in
PostDetailView(post: post)
}
}
}
}
}
.navigationTitle("Лента")
.onAppear {
if posts.isEmpty {
PostService.shared.fetchAllPosts { fetchedPosts in
self.posts = fetchedPosts
self.isLoading = false
}
}
}
}
}
}

View File

@ -0,0 +1,25 @@
// AccountSwitchSheet.swift
import SwiftUI
struct AccountShareSheet: View {
@Binding var isPresented: Bool
@Binding var selectedAccount: String
let accounts: [String]
var body: some View {
VStack(spacing: 24) {
Text("Типо qr code")
.font(.title2)
.bold()
.padding(.top, 32)
Spacer()
Button("Закрыть") {
isPresented = false
}
.padding()
}
.padding()
}
}

View File

@ -0,0 +1,13 @@
import SwiftUI
struct FollowersView: View {
let followers: [String]
var body: some View {
List(followers, id: \.self) { user in
Text(user)
}
.navigationTitle("Подписчики")
.navigationBarTitleDisplayMode(.inline)
}
}

View File

@ -0,0 +1,13 @@
import SwiftUI
struct FollowingView: View {
let following: [String]
var body: some View {
List(following, id: \.self) { user in
Text(user)
}
.navigationTitle("Подписки")
.navigationBarTitleDisplayMode(.inline)
}
}

View File

@ -0,0 +1,85 @@
import SwiftUI
struct PostDetailView: View {
let post: Post
var body: some View {
VStack(alignment: .leading, spacing: 0) {
// Шапка поста
HStack {
Image(systemName: "person.crop.circle.fill")
.resizable()
.frame(width: 36, height: 36)
.foregroundColor(.gray)
Text(post.authorUsername)
.font(.headline)
Spacer()
Button(action: {}) {
Image(systemName: "ellipsis")
.foregroundColor(.primary)
}
}
.padding(.horizontal)
.padding(.vertical, 8)
// Изображение поста
Rectangle()
.fill(Color.gray.opacity(0.3))
.aspectRatio(1, contentMode: .fit)
.frame(maxWidth: .infinity)
// Панель действий
HStack(spacing: 16) {
Button(action: {}) {
Image(systemName: "heart")
.font(.system(size: 24))
}
Button(action: {}) {
Image(systemName: "message")
.font(.system(size: 24))
}
Button(action: {}) {
Image(systemName: "paperplane")
.font(.system(size: 24))
}
Spacer()
Button(action: {}) {
Image(systemName: "square.and.arrow.down")
.font(.system(size: 24))
}
Button(action: {}) {
Image(systemName: "bookmark")
.font(.system(size: 24))
}
}
.foregroundColor(.primary)
.padding(.horizontal)
.padding(.top, 8)
// Лайки и описание
VStack(alignment: .leading, spacing: 4) {
Text("\(post.likes) likes")
.font(.headline)
Text(post.description ?? "")
.font(.subheadline)
}
.padding(.horizontal)
.padding(.top, 8)
// Комментарии
VStack(alignment: .leading, spacing: 4) {
Text("View all \(post.commentsCount) comments")
.font(.caption)
.foregroundColor(.secondary)
.padding(.top, 4)
Text("2 hours ago")
.font(.caption2)
.foregroundColor(.secondary)
.padding(.top, 4)
}
.padding(.horizontal)
}
}
}

View File

@ -0,0 +1,23 @@
import SwiftUI
struct PostFeedView: View {
let posts: [Post]
let selectedPostID: UUID
var body: some View {
ScrollViewReader { proxy in
ScrollView {
LazyVStack(spacing: 24) {
ForEach(posts) { post in
PostDetailView(post: post)
.id(post.id)
}
}
}
.navigationBarTitle("Feed", displayMode: .inline)
.onAppear {
proxy.scrollTo(selectedPostID, anchor: .center)
}
}
}
}

View File

@ -0,0 +1,112 @@
import SwiftUI
struct ProfileContentGrid: View {
let isContentLoaded: Bool
let selectedTabIndex: Int
let searchQuery: String
let selectedCategory: String
let allPosts: [Post]
let isLoading: Bool
var body: some View {
VStack(alignment: .leading, spacing: 0) {
if isLoading {
VStack {
ProgressView("Загрузка...")
.padding()
Spacer()
}
.frame(maxWidth: .infinity, minHeight: 300) // 👈 гарантированная высота
} else if filteredPosts.isEmpty {
VStack {
Text("Нет постов")
.foregroundColor(.secondary)
.padding()
}
.frame(maxWidth: .infinity, minHeight: 300)
} else {
LazyVGrid(columns: Array(repeating: .init(.flexible()), count: 3), spacing: 2) {
ForEach(filteredPosts) { post in
NavigationLink(destination: PostFeedView(posts: filteredPosts, selectedPostID: post.id)) {
Rectangle()
.fill(Color.gray.opacity(0.2))
.aspectRatio(1, contentMode: .fit)
.overlay(
ZStack {
VStack {
HStack {
Text("3 года назад") // Можно заменить на `post.createdAt`
.font(.caption2)
.foregroundColor(.white)
.padding(4)
.background(Color.black.opacity(0.6))
.cornerRadius(4)
Spacer()
}
Spacer()
}
.padding(4)
VStack {
Spacer()
HStack {
HStack(spacing: 4) {
Image(systemName: "play.fill")
.font(.caption2)
Text("\(post.views)")
.font(.caption2)
}
.foregroundColor(.white)
.padding(4)
.background(Color.black.opacity(0.6))
.cornerRadius(4)
Spacer()
}
.padding(4)
}
}
)
}
.buttonStyle(PlainButtonStyle())
}
}
.padding(.horizontal)
}
}
.frame(maxWidth: .infinity, alignment: .topLeading)
}
private var filteredPosts: [Post] {
var result: [Post]
switch selectedTabIndex {
case 1:
result = allPosts.filter { $0.accessLevel == .archive }
case 2:
result = allPosts.filter { $0.isSavedByCurrentUser }
case 3:
result = allPosts.filter { $0.isLikedByCurrentUser }
default:
result = allPosts
}
// 🔍 Поиск по названию или описанию
if !searchQuery.isEmpty {
result = result.filter {
($0.title?.localizedCaseInsensitiveContains(searchQuery) ?? false) ||
($0.description?.localizedCaseInsensitiveContains(searchQuery) ?? false)
}
}
// 🏷 Фильтрация по категории (если не "#все")
if selectedCategory != "#все" {
result = result.filter {
$0.hashtags?.contains(where: { $0 == selectedCategory }) ?? false
}
}
return result
}
}

View File

@ -0,0 +1,148 @@
import SwiftUI
struct ProfileContentTabbedGrid: View {
let isContentLoaded: Bool
@State private var selectedTabIndex = 0
@State private var selectedCategory = "#все"
@State private var searchQuery = ""
@State private var selectedSort = "По дате"
@State private var allPosts: [Post] = []
@State private var isLoading = true
var body: some View {
VStack(spacing: 0) {
// Вкладки профиля
HStack(spacing: 32) {
menuTab(index: 0)
tabButton(index: 1, systemIcon: "lock")
tabButton(index: 2, systemIcon: "bookmark")
tabButton(index: 3, systemIcon: "heart")
}
.padding(.vertical, 2)
// Фильтры
HStack {
if selectedTabIndex == 0 {
Menu {
Button("#все") { selectedCategory = "#все" }
Button("#влог") { selectedCategory = "#влог" }
Button("#игры") { selectedCategory = "#игры" }
} label: {
Label(selectedCategory, systemImage: "tag")
.font(.subheadline)
.padding(8)
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
}
TextField("Поиск", text: $searchQuery)
.padding(.horizontal, 10)
.padding(.vertical, 6)
.background(Color.gray.opacity(0.15))
.cornerRadius(8)
.font(.subheadline)
Button {
// Создать пост
} label: {
Label("Создать", systemImage: "plus")
.font(.subheadline)
.padding(8)
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
}
} else {
TextField("Поиск", text: $searchQuery)
.padding(.horizontal, 10)
.padding(.vertical, 6)
.background(Color.gray.opacity(0.15))
.cornerRadius(8)
.font(.subheadline)
}
}
.padding(.horizontal)
.padding(.vertical, 12)
// Контент с табами
ProfileContentGrid(
isContentLoaded: isContentLoaded,
selectedTabIndex: selectedTabIndex,
searchQuery: searchQuery,
selectedCategory: selectedCategory,
allPosts: allPosts,
isLoading: isLoading
)
}
.onAppear {
if allPosts.isEmpty {
isLoading = true
PostService.shared.fetchAllPosts { result in
self.allPosts = result
self.isLoading = false
}
}
}
}
// MARK: - Вкладка с меню
@ViewBuilder
func menuTab(index: Int) -> some View {
if selectedTabIndex == index {
Menu {
Button("По дате") { selectedSort = "По дате" }
Button("По популярности") { selectedSort = "По популярности" }
} label: {
VStack(spacing: 4) {
HStack(spacing: 4) {
Image(systemName: "rectangle.grid.3x2")
.font(.system(size: 18, weight: .medium))
.foregroundColor(.primary)
Image(systemName: "chevron.down")
.font(.system(size: 10))
.foregroundColor(.gray)
}
Rectangle()
.frame(height: 2)
.foregroundColor(.primary)
.padding(.horizontal)
}
.frame(maxWidth: .infinity)
}
} else {
Button {
selectedTabIndex = index
} label: {
VStack(spacing: 4) {
HStack(spacing: 4) {
Image(systemName: "rectangle.grid.3x2")
.font(.system(size: 18, weight: .medium))
.foregroundColor(.gray)
}
Rectangle()
.frame(height: 2)
.foregroundColor(.clear)
.padding(.horizontal)
}
.frame(maxWidth: .infinity)
}
}
}
// MARK: - Остальные вкладки
@ViewBuilder
func tabButton(index: Int, systemIcon: String) -> some View {
VStack(spacing: 4) {
Image(systemName: systemIcon)
.font(.system(size: 18, weight: .medium))
.foregroundColor(selectedTabIndex == index ? .primary : .gray)
Rectangle()
.frame(height: 2)
.foregroundColor(selectedTabIndex == index ? .primary : .clear)
.padding(.horizontal)
}
.frame(maxWidth: .infinity)
.onTapGesture {
selectedTabIndex = index
}
}
}

View File

@ -0,0 +1,147 @@
import SwiftUI
struct ProfileTab: View {
@ObservedObject var viewModel: LoginViewModel
// @State private var selectedTabIndex = 0
// @State private var selectedSort = "По дате"
// @State private var selectedCategory = "#все"
// @State private var searchQuery = ""
// @State private var searchQueryArchive = ""
// @State private var searchQuerySaved = ""
// @State private var searchQueryLiked = ""
@State private var isContentLoaded = true
@State private var accounts = ["@user1", "@user2", "@user3"]
@State private var selectedAccount = "@user1"
let followers = ["@alice", "@bob", "@charlie"]
let following = ["@dev", "@design", "@ios"]
@State private var sheetType: SheetType? = nil
enum SheetType: Identifiable {
case accountShare
var id: Int {
switch self {
case .accountShare: return 1
}
}
}
var body: some View {
NavigationView {
if !isContentLoaded{
SplashScreenView()
} else {
GeometryReader { geometry in
VStack(spacing: 0) {
ScrollView {
// Шапка профиля (занимает ~50% экрана)
header
.frame(minHeight: geometry.size.height * 0.5)
.frame(maxWidth: .infinity)
.background(Color(.systemBackground))
// Скролл с контентом
ProfileContentTabbedGrid(isContentLoaded: isContentLoaded)
}
.frame(maxWidth: .infinity)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
Button(action: {
sheetType = .accountShare
}) {
HStack(spacing: 4) {
Text("custom_user_name")
.font(.headline)
.foregroundColor(.primary)
Image(systemName: "chevron.down")
.font(.subheadline)
.foregroundColor(.gray)
}
}
}
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(destination: SettingsView(viewModel: viewModel)) {
Image(systemName: "wrench")
.imageScale(.large)
}
}
}
.sheet(item: $sheetType) { type in
switch type {
case .accountShare:
AccountShareSheet(
isPresented: Binding(
get: { sheetType != nil },
set: { if !$0 { sheetType = nil } }
),
selectedAccount: $selectedAccount,
accounts: accounts
)
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
// .onAppear {
// if !isContentLoaded {
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// isContentLoaded = true
// }
// }
// }
}
// MARK: - Шапка профиля
private var header: some View {
VStack(spacing: 12) {
// Аватар и имя
VStack(spacing: 6) {
Image(systemName: "person.crop.circle.fill")
.resizable()
.frame(width: 72, height: 72)
.foregroundColor(.gray)
Text(selectedAccount)
.font(.headline)
}
.padding(.top, 16)
Text("iOS разработчик, делаю интерфейсы, иногда снимаю блоги и делюсь кодом. Всегда рад новым подписчикам и интересным проектам.")
.font(.subheadline)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal)
// Статистика
HStack(spacing: 32) {
statView("24", "Посты")
NavigationLink(destination: FollowersView(followers: followers)) {
statView("1.2k", "Подписчики")
}
NavigationLink(destination: FollowingView(following: following)) {
statView("156", "Подписки")
}
}
}
.padding(.horizontal)
}
// MARK: - Статистика
func statView(_ value: String, _ label: String) -> some View {
VStack {
Text(value)
.font(.headline)
Text(label)
.font(.caption)
}
}
}

View File

@ -0,0 +1,8 @@
import SwiftUI
struct AppPreferencesView: View {
var body: some View {
Text("Настройки приложения")
.navigationTitle("Приложение")
}
}

View File

@ -0,0 +1,24 @@
import SwiftUI
struct EditProfileView: View {
@State private var displayName = ""
@State private var description = ""
var body: some View {
Form {
Section(header: Text("Публичная информация")) {
TextField("Отображаемое имя", text: $displayName)
TextField("Описание", text: $description)
}
Button(action: {
// Действие для сохранения профиля
print("DisplayName: \(displayName)")
print("Description: \(description)")
}) {
Text("Применить")
}
}
.navigationTitle("Редактировать профиль")
}
}

View File

@ -0,0 +1,8 @@
import SwiftUI
struct SecuritySettingsView: View {
var body: some View {
Text("Настройки безопасности")
.navigationTitle("Безопасность")
}
}

View File

@ -0,0 +1,98 @@
import SwiftUI
struct SettingsView: View {
@ObservedObject var viewModel: LoginViewModel
@AppStorage("isDarkMode") private var isDarkMode: Bool = true
var body: some View {
Form {
// MARK: - Профиль
Section(header: Text("Профиль")) {
NavigationLink(destination: EditProfileView()) {
Label("Мой профиль", systemImage: "person.crop.circle")
}
}
// MARK: - Безопасность
Section(header: Text("Безопасность")) {
NavigationLink(destination: Text("Заглушка: Сменить пароль")) {
Label("Сменить пароль", systemImage: "key")
}
NavigationLink(destination: Text("Заглушка: Двухфакторная аутентификация")) {
Label("Двухфакторная аутентификация", systemImage: "lock.shield")
}
NavigationLink(destination: Text("Заглушка: Активные сессии")) {
Label("Активные сессии", systemImage: "iphone")
}
}
// MARK: - Приложение
Section(header: Text("Приложение")) {
Button(action: openLanguageSettings) {
Label("Язык", systemImage: "globe")
}
Toggle(isOn: $isDarkMode) {
Label("Тёмная тема", systemImage: "moon.fill")
}
NavigationLink(destination: Text("Заглушка: Хранилище данных")) {
Label("Данные", systemImage: "externaldrive")
}
NavigationLink(destination: Text("Заглушка: Другие настройки")) {
Label("Другое", systemImage: "ellipsis.circle")
}
}
// MARK: - Уведомления
Section(header: Text("Уведомления")) {
NavigationLink(destination: Text("Заглушка: Push-уведомления")) {
Label("Push-уведомления", systemImage: "bell")
}
}
// MARK: - Поддержка
Section(header: Text("Поддержка")) {
NavigationLink(destination: Text("Заглушка: Частые вопросы")) {
Label("Частые вопросы", systemImage: "questionmark.circle")
}
NavigationLink(destination: Text("Заглушка: Обратная связь")) {
Label("Обратная связь", systemImage: "paperplane")
}
}
// MARK: - О приложении
Section(header: Text("О приложении")) {
VStack(alignment: .leading, spacing: 6) {
Text(AppInfo.text_1)
Text(AppInfo.text_2)
Text(AppInfo.text_3)
}
.font(.footnote)
.foregroundColor(.gray)
.padding(.vertical, 4)
}
// MARK: - Выход
Section {
Button(action: {
viewModel.logoutCurrentUser()
}) {
HStack {
Image(systemName: "arrow.backward.square")
Text("Выйти из аккаунта")
}
.foregroundColor(.red)
}
}
}
.navigationTitle("Настройки")
}
private func openLanguageSettings() {
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
UIApplication.shared.open(url)
}
}

View File

@ -1,16 +0,0 @@
import SwiftUI
struct HomeTab: View {
var body: some View {
NavigationView {
VStack {
Text("Домой")
.font(.largeTitle)
.bold()
.padding()
Spacer()
}
.navigationTitle("Домой")
}
}
}

View File

@ -1,297 +0,0 @@
import SwiftUI
struct ProfileTab: View {
@ObservedObject var viewModel: LoginViewModel
@State private var showingAccountSwitch = false
@State private var selectedTabIndex = 0
@State private var selectedSort = "По дате"
@State private var selectedCategory = "#все"
@State private var searchQuery = ""
@State private var searchQueryArchive = ""
@State private var searchQuerySaved = ""
@State private var searchQueryLiked = ""
var body: some View {
NavigationView {
GeometryReader { geometry in
VStack(spacing: 0) {
ScrollView {
VStack(spacing: 12) {
// Аватар и имя
VStack(spacing: 6) {
Image(systemName: "person.crop.circle.fill")
.resizable()
.frame(width: 72, height: 72)
.foregroundColor(.gray)
Text("@username")
.font(.headline)
}
.padding(.top, 16)
// Статистика
HStack(spacing: 32) {
statView("24", "Посты")
statView("1.2k", "Подписчики")
statView("156", "Подписки")
}
// Вкладки профиля
HStack(spacing: 32) {
menuTab(index: 0)
tabButton(index: 1, systemIcon: "lock")
tabButton(index: 2, systemIcon: "bookmark")
tabButton(index: 3, systemIcon: "heart")
}
.padding(.vertical, 12)
}
.padding(.horizontal)
.frame(width: geometry.size.width)
if selectedTabIndex == 0 {
HStack {
Menu {
// Здесь список категорий
Button("#все") { selectedCategory = "#все" }
Button("#влог") { selectedCategory = "#влог" }
Button("#игры") { selectedCategory = "#игры" }
Button("#путешествия") { selectedCategory = "#путешествия" }
} label: {
Label(selectedCategory, systemImage: "tag")
.font(.subheadline)
.padding(8)
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
}
// Поиск
TextField("Поиск", text: $searchQuery)
.padding(.horizontal, 10)
.padding(.vertical, 6)
.background(Color.gray.opacity(0.15))
.cornerRadius(8)
.font(.subheadline)
Button(action: {
// Создание поста
}) {
Label("Создать", systemImage: "plus")
.font(.subheadline)
.padding(8)
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
}
}
.padding(.horizontal)
} else if selectedTabIndex == 1 {
HStack {
// Поиск
TextField("Поиск", text: $searchQueryArchive)
.padding(.horizontal, 10)
.padding(.vertical, 6)
.background(Color.gray.opacity(0.15))
.cornerRadius(8)
.font(.subheadline)
}
.padding(.horizontal)
} else if selectedTabIndex == 2 {
HStack {
// Поиск
TextField("Поиск", text: $searchQuerySaved)
.padding(.horizontal, 10)
.padding(.vertical, 6)
.background(Color.gray.opacity(0.15))
.cornerRadius(8)
.font(.subheadline)
}
.padding(.horizontal)
} else if selectedTabIndex == 3 {
HStack {
// Поиск
TextField("Поиск", text: $searchQueryLiked)
.padding(.horizontal, 10)
.padding(.vertical, 6)
.background(Color.gray.opacity(0.15))
.cornerRadius(8)
.font(.subheadline)
}
.padding(.horizontal)
}
// Контентная часть
TabView(selection: $selectedTabIndex) {
ForEach(0..<4) { index in
LazyVGrid(columns: Array(repeating: .init(.flexible()), count: 3), spacing: 2) {
ForEach(0..<36) { _ in
Rectangle()
.fill(contentColor(for: index))
.aspectRatio(1, contentMode: .fit)
.overlay(
ZStack {
// Верхний левый угол дата
VStack {
HStack {
Text("3 года назад")
.font(.caption2)
.foregroundColor(.white)
.padding(4)
.background(Color.black.opacity(0.6))
.cornerRadius(4)
Spacer()
}
Spacer()
}
.padding(4)
// Нижний левый угол просмотры
VStack {
Spacer()
HStack {
HStack(spacing: 4) {
Image(systemName: "play.fill")
.font(.caption2)
Text("1.2k")
.font(.caption2)
}
.foregroundColor(.white)
.padding(4)
.background(Color.black.opacity(0.6))
.cornerRadius(4)
Spacer()
}
.padding(4)
}
}
)
}
}
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
// .frame(height: geometry.size.width * 1.2) // Динамическая высота
}
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
Button(action: { showingAccountSwitch.toggle() }) {
HStack(spacing: 4) {
Text("custom_user_name")
.font(.headline)
.foregroundColor(.primary)
Image(systemName: "chevron.down")
.font(.subheadline)
.foregroundColor(.gray)
}
}
}
// Левый верх ручка (редактирование)
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
// действие редактирования профиля
}) {
Image(systemName: "paintbrush")
.imageScale(.large)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
// перейти к настройкам
}) {
Image(systemName: "wrench")
.imageScale(.large)
}
}
}
.sheet(isPresented: $showingAccountSwitch) {
VStack {
Text("Выбор аккаунта")
.font(.title)
.padding()
Button("Закрыть") {
showingAccountSwitch = false
}
.padding()
}
}
}
}
// MARK: - Статистика
func statView(_ value: String, _ label: String) -> some View {
VStack {
Text(value)
.font(.headline)
Text(label)
.font(.caption)
}
}
// MARK: - Вкладка с меню
@ViewBuilder
func menuTab(index: Int) -> some View {
Menu {
if selectedTabIndex == index {
Button("По дате") { selectedSort = "По дате" }
Button("По популярности") { selectedSort = "По популярности" }
}
} label: {
VStack(spacing: 4) {
HStack(spacing: 4) {
Image(systemName: "rectangle.grid.3x2")
.font(.system(size: 18, weight: .medium))
.foregroundColor(.primary)
// Показываем стрелку вниз только если вкладка активна
if selectedTabIndex == index {
Image(systemName: "chevron.down")
.font(.system(size: 10))
.foregroundColor(.gray)
}
}
Rectangle()
.frame(height: 2)
.foregroundColor(selectedTabIndex == index ? .primary : .clear)
}
.frame(maxWidth: .infinity)
}
.onTapGesture {
selectedTabIndex = index
}
}
// MARK: - Остальные вкладки
@ViewBuilder
func tabButton(index: Int, systemIcon: String) -> some View {
VStack(spacing: 4) {
Image(systemName: systemIcon)
.font(.system(size: 18, weight: .medium))
.foregroundColor(selectedTabIndex == index ? .primary : .gray)
Rectangle()
.frame(height: 2)
.foregroundColor(selectedTabIndex == index ? .primary : .clear)
}
.frame(maxWidth: .infinity)
.onTapGesture {
selectedTabIndex = index
}
}
// MARK: - Цвет контента
func contentColor(for tab: Int) -> Color {
switch tab {
case 0: return Color.gray.opacity(0.3)
case 1: return Color.blue.opacity(0.3)
case 2: return Color.green.opacity(0.3)
case 3: return Color.red.opacity(0.3)
default: return Color.gray.opacity(0.3)
}
}
}

View File

@ -10,3 +10,9 @@ struct AppConfig {
static let APP_BUILD = "freestore" static let APP_BUILD = "freestore"
static let APP_VERSION = "0.1" static let APP_VERSION = "0.1"
} }
struct AppInfo {
static let text_1 = "\(NSLocalizedString("profile_down_text_1", comment: "")) yobble"
static let text_2 = "\(NSLocalizedString("profile_down_text_2", comment: "")) 0.1test"
static let text_3 = "\(NSLocalizedString("profile_down_text_3", comment: ""))2025"
}

View File

@ -27,6 +27,19 @@
1A7940E72DF7B5E5002569DA /* SplashScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940E62DF7B5E5002569DA /* SplashScreenView.swift */; }; 1A7940E72DF7B5E5002569DA /* SplashScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940E62DF7B5E5002569DA /* SplashScreenView.swift */; };
1A7940F02DF7B7A3002569DA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1A7940F22DF7B7A3002569DA /* Localizable.strings */; }; 1A7940F02DF7B7A3002569DA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1A7940F22DF7B7A3002569DA /* Localizable.strings */; };
1A79410C2DF7C81D002569DA /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79410B2DF7C81D002569DA /* RegistrationView.swift */; }; 1A79410C2DF7C81D002569DA /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79410B2DF7C81D002569DA /* RegistrationView.swift */; };
1AB4F8CD2E22E341002B6E40 /* AccountShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB4F8CC2E22E341002B6E40 /* AccountShareSheet.swift */; };
1AB4F8F32E22EC9F002B6E40 /* FollowersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB4F8F22E22EC9F002B6E40 /* FollowersView.swift */; };
1AB4F8F72E22ECAC002B6E40 /* FollowingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB4F8F62E22ECAC002B6E40 /* FollowingView.swift */; };
1ACE60F82E22F3DC00B37AC5 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE60F72E22F3DC00B37AC5 /* SettingsView.swift */; };
1ACE61012E22F55C00B37AC5 /* EditProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61002E22F55C00B37AC5 /* EditProfileView.swift */; };
1ACE61052E22F56800B37AC5 /* SecuritySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61042E22F56800B37AC5 /* SecuritySettingsView.swift */; };
1ACE61092E22F57100B37AC5 /* AppPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61082E22F57100B37AC5 /* AppPreferencesView.swift */; };
1ACE61152E22FE2000B37AC5 /* PostDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61142E22FE2000B37AC5 /* PostDetailView.swift */; };
1ACE61192E22FF1400B37AC5 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61182E22FF1400B37AC5 /* Post.swift */; };
1ACE61212E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61202E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift */; };
1AD757CD2E27608C0069C1FD /* PostFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD757CC2E27608C0069C1FD /* PostFeedView.swift */; };
1AE587052E23264800254F06 /* PostService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE587042E23264800254F06 /* PostService.swift */; };
1AE587252E23337000254F06 /* ProfileContentGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE587242E23337000254F06 /* ProfileContentGrid.swift */; };
1AEE5EAB2E21A83200A3DCA3 /* HomeTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AEE5EAA2E21A83200A3DCA3 /* HomeTab.swift */; }; 1AEE5EAB2E21A83200A3DCA3 /* HomeTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AEE5EAA2E21A83200A3DCA3 /* HomeTab.swift */; };
1AEE5EAF2E21A84500A3DCA3 /* PublicsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AEE5EAE2E21A84500A3DCA3 /* PublicsTab.swift */; }; 1AEE5EAF2E21A84500A3DCA3 /* PublicsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AEE5EAE2E21A84500A3DCA3 /* PublicsTab.swift */; };
1AEE5EB32E21A85800A3DCA3 /* SearchTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AEE5EB22E21A85800A3DCA3 /* SearchTab.swift */; }; 1AEE5EB32E21A85800A3DCA3 /* SearchTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AEE5EB22E21A85800A3DCA3 /* SearchTab.swift */; };
@ -57,6 +70,19 @@
1A7940F12DF7B7A3002569DA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; 1A7940F12DF7B7A3002569DA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
1A7940F72DF7B7EC002569DA /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; }; 1A7940F72DF7B7EC002569DA /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
1A79410B2DF7C81D002569DA /* RegistrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationView.swift; sourceTree = "<group>"; }; 1A79410B2DF7C81D002569DA /* RegistrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationView.swift; sourceTree = "<group>"; };
1AB4F8CC2E22E341002B6E40 /* AccountShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountShareSheet.swift; sourceTree = "<group>"; };
1AB4F8F22E22EC9F002B6E40 /* FollowersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersView.swift; sourceTree = "<group>"; };
1AB4F8F62E22ECAC002B6E40 /* FollowingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingView.swift; sourceTree = "<group>"; };
1ACE60F72E22F3DC00B37AC5 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
1ACE61002E22F55C00B37AC5 /* EditProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileView.swift; sourceTree = "<group>"; };
1ACE61042E22F56800B37AC5 /* SecuritySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecuritySettingsView.swift; sourceTree = "<group>"; };
1ACE61082E22F57100B37AC5 /* AppPreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPreferencesView.swift; sourceTree = "<group>"; };
1ACE61142E22FE2000B37AC5 /* PostDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDetailView.swift; sourceTree = "<group>"; };
1ACE61182E22FF1400B37AC5 /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = "<group>"; };
1ACE61202E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileContentTabbedGrid.swift; sourceTree = "<group>"; };
1AD757CC2E27608C0069C1FD /* PostFeedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostFeedView.swift; sourceTree = "<group>"; };
1AE587042E23264800254F06 /* PostService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostService.swift; sourceTree = "<group>"; };
1AE587242E23337000254F06 /* ProfileContentGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileContentGrid.swift; sourceTree = "<group>"; };
1AEE5EAA2E21A83200A3DCA3 /* HomeTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTab.swift; sourceTree = "<group>"; }; 1AEE5EAA2E21A83200A3DCA3 /* HomeTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTab.swift; sourceTree = "<group>"; };
1AEE5EAE2E21A84500A3DCA3 /* PublicsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicsTab.swift; sourceTree = "<group>"; }; 1AEE5EAE2E21A84500A3DCA3 /* PublicsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicsTab.swift; sourceTree = "<group>"; };
1AEE5EB22E21A85800A3DCA3 /* SearchTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTab.swift; sourceTree = "<group>"; }; 1AEE5EB22E21A85800A3DCA3 /* SearchTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTab.swift; sourceTree = "<group>"; };
@ -135,7 +161,7 @@
1A79409D2DF77DB5002569DA /* Views */ = { 1A79409D2DF77DB5002569DA /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1AEE5EA92E21A4FC00A3DCA3 /* tab */, 1AEE5EA92E21A4FC00A3DCA3 /* Tab */,
1AEE5EA62E2194CD00A3DCA3 /* contacts */, 1AEE5EA62E2194CD00A3DCA3 /* contacts */,
1AEE5EA52E21947B00A3DCA3 /* login */, 1AEE5EA52E21947B00A3DCA3 /* login */,
1A79407B2DF77BC2002569DA /* ContentView.swift */, 1A79407B2DF77BC2002569DA /* ContentView.swift */,
@ -156,6 +182,7 @@
1A79409F2DF77DC4002569DA /* Models */ = { 1A79409F2DF77DC4002569DA /* Models */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1ACE61182E22FF1400B37AC5 /* Post.swift */,
1A7940A52DF77DF5002569DA /* User.swift */, 1A7940A52DF77DF5002569DA /* User.swift */,
); );
path = Models; path = Models;
@ -166,6 +193,7 @@
children = ( children = (
1A7940A12DF77DE9002569DA /* AuthService.swift */, 1A7940A12DF77DE9002569DA /* AuthService.swift */,
1A0276022DF909F900D8BC53 /* refreshtokenex.swift */, 1A0276022DF909F900D8BC53 /* refreshtokenex.swift */,
1AE587042E23264800254F06 /* PostService.swift */,
); );
path = Network; path = Network;
sourceTree = "<group>"; sourceTree = "<group>";
@ -186,6 +214,17 @@
path = Resources; path = Resources;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
1ACE60FF2E22F54700B37AC5 /* Settings */ = {
isa = PBXGroup;
children = (
1ACE60F72E22F3DC00B37AC5 /* SettingsView.swift */,
1ACE61002E22F55C00B37AC5 /* EditProfileView.swift */,
1ACE61042E22F56800B37AC5 /* SecuritySettingsView.swift */,
1ACE61082E22F57100B37AC5 /* AppPreferencesView.swift */,
);
path = Settings;
sourceTree = "<group>";
};
1AEE5EA52E21947B00A3DCA3 /* login */ = { 1AEE5EA52E21947B00A3DCA3 /* login */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -202,10 +241,10 @@
path = contacts; path = contacts;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
1AEE5EA92E21A4FC00A3DCA3 /* tab */ = { 1AEE5EA92E21A4FC00A3DCA3 /* Tab */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1AEE5ECC2E21C9D100A3DCA3 /* profile */, 1AEE5ECC2E21C9D100A3DCA3 /* Profile */,
1A7940C52DF7A98E002569DA /* ContactsTab.swift */, 1A7940C52DF7A98E002569DA /* ContactsTab.swift */,
1A7940C92DF7A99B002569DA /* ChatsTab.swift */, 1A7940C92DF7A99B002569DA /* ChatsTab.swift */,
1A7940B52DF77F21002569DA /* MainView.swift */, 1A7940B52DF77F21002569DA /* MainView.swift */,
@ -213,15 +252,23 @@
1AEE5EAE2E21A84500A3DCA3 /* PublicsTab.swift */, 1AEE5EAE2E21A84500A3DCA3 /* PublicsTab.swift */,
1AEE5EB22E21A85800A3DCA3 /* SearchTab.swift */, 1AEE5EB22E21A85800A3DCA3 /* SearchTab.swift */,
); );
path = tab; path = Tab;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
1AEE5ECC2E21C9D100A3DCA3 /* profile */ = { 1AEE5ECC2E21C9D100A3DCA3 /* Profile */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1AD757CC2E27608C0069C1FD /* PostFeedView.swift */,
1ACE60FF2E22F54700B37AC5 /* Settings */,
1A7940CD2DF7A9AA002569DA /* ProfileTab.swift */, 1A7940CD2DF7A9AA002569DA /* ProfileTab.swift */,
1AB4F8CC2E22E341002B6E40 /* AccountShareSheet.swift */,
1AE587242E23337000254F06 /* ProfileContentGrid.swift */,
1ACE61202E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift */,
1AB4F8F22E22EC9F002B6E40 /* FollowersView.swift */,
1AB4F8F62E22ECAC002B6E40 /* FollowingView.swift */,
1ACE61142E22FE2000B37AC5 /* PostDetailView.swift */,
); );
path = profile; path = Profile;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXGroup section */ /* End PBXGroup section */
@ -324,23 +371,36 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
1AEE5EAB2E21A83200A3DCA3 /* HomeTab.swift in Sources */, 1AEE5EAB2E21A83200A3DCA3 /* HomeTab.swift in Sources */,
1ACE60F82E22F3DC00B37AC5 /* SettingsView.swift in Sources */,
1AD757CD2E27608C0069C1FD /* PostFeedView.swift in Sources */,
1A7940C62DF7A98E002569DA /* ContactsTab.swift in Sources */, 1A7940C62DF7A98E002569DA /* ContactsTab.swift in Sources */,
1ACE61092E22F57100B37AC5 /* AppPreferencesView.swift in Sources */,
1ACE61052E22F56800B37AC5 /* SecuritySettingsView.swift in Sources */,
1A7940E72DF7B5E5002569DA /* SplashScreenView.swift in Sources */, 1A7940E72DF7B5E5002569DA /* SplashScreenView.swift in Sources */,
1A7940B02DF77E26002569DA /* LoginView.swift in Sources */, 1A7940B02DF77E26002569DA /* LoginView.swift in Sources */,
1A79410C2DF7C81D002569DA /* RegistrationView.swift in Sources */, 1A79410C2DF7C81D002569DA /* RegistrationView.swift in Sources */,
1A0276112DF9247000D8BC53 /* CustomTextField.swift in Sources */, 1A0276112DF9247000D8BC53 /* CustomTextField.swift in Sources */,
1A7940CE2DF7A9AA002569DA /* ProfileTab.swift in Sources */, 1A7940CE2DF7A9AA002569DA /* ProfileTab.swift in Sources */,
1ACE61212E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift in Sources */,
1AB4F8F72E22ECAC002B6E40 /* FollowingView.swift in Sources */,
1A7940DE2DF7B0D7002569DA /* config.swift in Sources */, 1A7940DE2DF7B0D7002569DA /* config.swift in Sources */,
1AE587252E23337000254F06 /* ProfileContentGrid.swift in Sources */,
1A0276032DF909F900D8BC53 /* refreshtokenex.swift in Sources */, 1A0276032DF909F900D8BC53 /* refreshtokenex.swift in Sources */,
1A7940B62DF77F21002569DA /* MainView.swift in Sources */, 1A7940B62DF77F21002569DA /* MainView.swift in Sources */,
1A7940E22DF7B1C5002569DA /* KeychainService.swift in Sources */, 1A7940E22DF7B1C5002569DA /* KeychainService.swift in Sources */,
1A7940A62DF77DF5002569DA /* User.swift in Sources */, 1A7940A62DF77DF5002569DA /* User.swift in Sources */,
1A7940A22DF77DE9002569DA /* AuthService.swift in Sources */, 1A7940A22DF77DE9002569DA /* AuthService.swift in Sources */,
1AEE5EB32E21A85800A3DCA3 /* SearchTab.swift in Sources */, 1AEE5EB32E21A85800A3DCA3 /* SearchTab.swift in Sources */,
1ACE61152E22FE2000B37AC5 /* PostDetailView.swift in Sources */,
1ACE61192E22FF1400B37AC5 /* Post.swift in Sources */,
1A79408F2DF77BC3002569DA /* ContentView.swift in Sources */, 1A79408F2DF77BC3002569DA /* ContentView.swift in Sources */,
1AB4F8CD2E22E341002B6E40 /* AccountShareSheet.swift in Sources */,
1ACE61012E22F55C00B37AC5 /* EditProfileView.swift in Sources */,
1AEE5EAF2E21A84500A3DCA3 /* PublicsTab.swift in Sources */, 1AEE5EAF2E21A84500A3DCA3 /* PublicsTab.swift in Sources */,
1AE587052E23264800254F06 /* PostService.swift in Sources */,
1A7940CA2DF7A99B002569DA /* ChatsTab.swift in Sources */, 1A7940CA2DF7A99B002569DA /* ChatsTab.swift in Sources */,
1A7940AA2DF77E05002569DA /* LoginViewModel.swift in Sources */, 1A7940AA2DF77E05002569DA /* LoginViewModel.swift in Sources */,
1AB4F8F32E22EC9F002B6E40 /* FollowersView.swift in Sources */,
1A79408D2DF77BC3002569DA /* yobbleApp.swift in Sources */, 1A79408D2DF77BC3002569DA /* yobbleApp.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

@ -14,6 +14,16 @@
<key>orderHint</key> <key>orderHint</key>
<integer>0</integer> <integer>0</integer>
</dict> </dict>
<key>yobble (iOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>yobble (macOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
</dict> </dict>
</dict> </dict>
</plist> </plist>