Compare commits
No commits in common. "bb8c9a2b918e4ae8fad071d77e3fbd2cb67975a5" and "39f4c5bb7cef50ce0a3d4ab48b0b694dffa35ec1" have entirely different histories.
bb8c9a2b91
...
39f4c5bb7c
@ -1,45 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@ -1,144 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct FollowersView: View {
|
|
||||||
let followers: [String]
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
List(followers, id: \.self) { user in
|
|
||||||
Text(user)
|
|
||||||
}
|
|
||||||
.navigationTitle("Подписчики")
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct FollowingView: View {
|
|
||||||
let following: [String]
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
List(following, id: \.self) { user in
|
|
||||||
Text(user)
|
|
||||||
}
|
|
||||||
.navigationTitle("Подписки")
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,112 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,147 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct AppPreferencesView: View {
|
|
||||||
var body: some View {
|
|
||||||
Text("Настройки приложения")
|
|
||||||
.navigationTitle("Приложение")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
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("Редактировать профиль")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct SecuritySettingsView: View {
|
|
||||||
var body: some View {
|
|
||||||
Text("Настройки безопасности")
|
|
||||||
.navigationTitle("Безопасность")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
16
Shared/Views/tab/HomeTab.swift
Normal file
16
Shared/Views/tab/HomeTab.swift
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct HomeTab: View {
|
||||||
|
var body: some View {
|
||||||
|
NavigationView {
|
||||||
|
VStack {
|
||||||
|
Text("Домой")
|
||||||
|
.font(.largeTitle)
|
||||||
|
.bold()
|
||||||
|
.padding()
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.navigationTitle("Домой")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
297
Shared/Views/tab/profile/ProfileTab.swift
Normal file
297
Shared/Views/tab/profile/ProfileTab.swift
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,9 +10,3 @@ 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"
|
|
||||||
}
|
|
||||||
|
|||||||
@ -27,19 +27,6 @@
|
|||||||
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 */; };
|
||||||
@ -70,19 +57,6 @@
|
|||||||
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>"; };
|
||||||
@ -161,7 +135,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 */,
|
||||||
@ -182,7 +156,6 @@
|
|||||||
1A79409F2DF77DC4002569DA /* Models */ = {
|
1A79409F2DF77DC4002569DA /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1ACE61182E22FF1400B37AC5 /* Post.swift */,
|
|
||||||
1A7940A52DF77DF5002569DA /* User.swift */,
|
1A7940A52DF77DF5002569DA /* User.swift */,
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
@ -193,7 +166,6 @@
|
|||||||
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>";
|
||||||
@ -214,17 +186,6 @@
|
|||||||
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 = (
|
||||||
@ -241,10 +202,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 */,
|
||||||
@ -252,23 +213,15 @@
|
|||||||
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 */
|
||||||
@ -371,36 +324,23 @@
|
|||||||
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;
|
||||||
|
|||||||
@ -14,16 +14,6 @@
|
|||||||
<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>
|
||||||
|
|||||||
Reference in New Issue
Block a user