Compare commits

..

4 Commits

Author SHA1 Message Date
12be44673a profile change 2025-12-11 02:22:40 +03:00
ee4a19155f change media 2025-12-11 02:11:34 +03:00
66bd6a99d4 change button in profile 2025-12-11 02:03:36 +03:00
55839ffd5d profile when user delete 2025-12-11 01:52:49 +03:00
2 changed files with 132 additions and 86 deletions

View File

@ -131,6 +131,9 @@
} }
} }
}, },
"GIF" : {
"comment" : "Message profile category gifs"
},
"Hello, world!" : { "Hello, world!" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -508,6 +511,9 @@
"Глобальный поиск" : { "Глобальный поиск" : {
"comment" : "Global search section" "comment" : "Global search section"
}, },
"Голосовые" : {
"comment" : "Message profile category voice"
},
"Голосовые звонки пока недоступны. Как только включим WebRTC, кнопка оживёт." : { "Голосовые звонки пока недоступны. Как только включим WebRTC, кнопка оживёт." : {
"comment" : "Message profile call action description" "comment" : "Message profile call action description"
}, },
@ -522,6 +528,9 @@
} }
} }
}, },
"Группы" : {
"comment" : "Message profile category groups"
},
"Данные" : { "Данные" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -843,9 +852,6 @@
} }
} }
}, },
"История медиа синхронизируется. Как только появятся первые вложения, они покажутся здесь списком превью." : {
"comment" : "Message profile media footer"
},
"Ищем пользователей…" : { "Ищем пользователей…" : {
"comment" : "Global search loading" "comment" : "Global search loading"
}, },
@ -1061,8 +1067,8 @@
"Массовая отчистка" : { "Массовая отчистка" : {
}, },
"Медиа, ссылки и файлы" : { "Медиа" : {
"comment" : "Message profile media title" "comment" : "Message profile category media"
}, },
"Мессенджер-режим сейчас проработан примерно на 50%." : { "Мессенджер-режим сейчас проработан примерно на 50%." : {
@ -1106,6 +1112,9 @@
} }
} }
}, },
"Музыка" : {
"comment" : "Message profile category music"
},
"Мы используем адрес только для ответа на ваш запрос." : { "Мы используем адрес только для ответа на ваш запрос." : {
"comment" : "feedback: email hint", "comment" : "feedback: email hint",
"localizations" : { "localizations" : {
@ -1943,12 +1952,6 @@
"Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям." : { "Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям." : {
"comment" : "FAQ answer: reset password" "comment" : "FAQ answer: reset password"
}, },
"Перестанет появляться в чате и не сможет писать." : {
"comment" : "Message profile block subtitle"
},
"Плитки как в Telegram — скоро здесь появятся вложения из чата." : {
"comment" : "Message profile media description"
},
"По умолчанию это полноценная соцсеть с лентой, историями и подписками. Если нужно только общение без лишнего контента, переключитесь на режим “Только чаты”. Переключить режим можно в любой момент." : { "По умолчанию это полноценная соцсеть с лентой, историями и подписками. Если нужно только общение без лишнего контента, переключитесь на режим “Только чаты”. Переключить режим можно в любой момент." : {
}, },
@ -2119,9 +2122,6 @@
} }
} }
}, },
"Пользователь снова сможет писать вам." : {
"comment" : "Message profile unblock subtitle"
},
"Пользователь удалён" : { "Пользователь удалён" : {
"comment" : "Message profile deleted user status" "comment" : "Message profile deleted user status"
}, },
@ -2151,6 +2151,9 @@
"Последний вход: %@" : { "Последний вход: %@" : {
"comment" : "Дата последнего входа в сессию" "comment" : "Дата последнего входа в сессию"
}, },
"Посты" : {
"comment" : "Message profile category posts"
},
"Похвала" : { "Похвала" : {
"comment" : "feedback category: praise", "comment" : "feedback category: praise",
"localizations" : { "localizations" : {
@ -2165,9 +2168,6 @@
"Появится мутация на 1 час, 1 день или навсегда." : { "Появится мутация на 1 час, 1 день или навсегда." : {
"comment" : "Message profile mute action description" "comment" : "Message profile mute action description"
}, },
"Появится отдельная запись в адресной книге Yobble." : {
"comment" : "Message profile add to contacts subtitle"
},
"Правила сервиса" : { "Правила сервиса" : {
}, },
@ -2392,6 +2392,12 @@
"Разблокировать" : { "Разблокировать" : {
"comment" : "Message profile unblock alert title\nMessage profile unblock title\nUnblock confirmation action" "comment" : "Message profile unblock alert title\nMessage profile unblock title\nUnblock confirmation action"
}, },
"Раздел скоро станет активным — собираем и индексируем вложения." : {
"comment" : "Message profile media placeholder message"
},
"Разделы временно показывают заглушки — позже спрячем пустые категории." : {
"comment" : "Message profile media footer new"
},
"Разрешить пересылку сообщений" : { "Разрешить пересылку сообщений" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -2709,6 +2715,9 @@
}, },
"Сохранение..." : { "Сохранение..." : {
},
"Сохранённые" : {
"comment" : "Message profile category saved"
}, },
"Сохраните секретный ключ и введите код из приложения, чтобы завершить настройку." : { "Сохраните секретный ключ и введите код из приложения, чтобы завершить настройку." : {
"comment" : "Сообщение после активации 2FA" "comment" : "Сообщение после активации 2FA"
@ -2751,6 +2760,9 @@
} }
} }
}, },
"Ссылки" : {
"comment" : "Message profile category links"
},
"Старый пароль" : { "Старый пароль" : {
"comment" : "Старый пароль", "comment" : "Старый пароль",
"localizations" : { "localizations" : {
@ -2907,6 +2919,9 @@
} }
} }
}, },
"Файлы" : {
"comment" : "Message profile category files"
},
"Функция пока недоступна." : { "Функция пока недоступна." : {
"comment" : "Сообщение заглушки" "comment" : "Сообщение заглушки"
}, },

View File

@ -232,6 +232,11 @@ struct MessageProfileView: View {
title: NSLocalizedString("Юзернейм", comment: ""), title: NSLocalizedString("Юзернейм", comment: ""),
value: login value: login
) )
}else{
infoRow(
title: NSLocalizedString("Юзернейм", comment: ""),
value: "Неизвестный пользователь"
)
} }
if let membership = membershipDescription { if let membership = membershipDescription {
@ -253,31 +258,23 @@ struct MessageProfileView: View {
if shouldShowRelationshipQuickActions { if shouldShowRelationshipQuickActions {
rowDivider rowDivider
VStack(spacing: 8) { filledActionButton(
buttonRow( title: NSLocalizedString("Добавить в контакты", comment: "Message profile add to contacts title"),
icon: "person.badge.plus", tint: Color.accentColor
title: NSLocalizedString("Добавить в контакты", comment: "Message profile add to contacts title"), ) {
subtitle: NSLocalizedString("Появится отдельная запись в адресной книге Yobble.", comment: "Message profile add to contacts subtitle"), handleAddContactTap()
iconTint: .accentColor
) {
handleAddContactTap()
}
buttonRow(
icon: "hand.raised.slash.fill",
title: isBlockedByCurrentUser
? NSLocalizedString("Разблокировать", comment: "Message profile unblock title")
: NSLocalizedString("Заблокировать", comment: "Message profile block title"),
subtitle: isBlockedByCurrentUser
? NSLocalizedString("Пользователь снова сможет писать вам.", comment: "Message profile unblock subtitle")
: NSLocalizedString("Перестанет появляться в чате и не сможет писать.", comment: "Message profile block subtitle"),
iconTint: .red,
destructive: true
) {
handleBlockToggleTap()
}
} }
.padding(.top, 4)
rowDivider
filledActionButton(
title: isBlockedByCurrentUser
? NSLocalizedString("Разблокировать", comment: "Message profile unblock title")
: NSLocalizedString("Заблокировать", comment: "Message profile block title"),
tint: isBlockedByCurrentUser ? Color.green : Color.red
) {
handleBlockToggleTap()
}
} }
} }
@ -335,34 +332,20 @@ struct MessageProfileView: View {
} }
private var mediaPreviewSection: some View { private var mediaPreviewSection: some View {
section( VStack(alignment: .leading, spacing: 12) {
title: NSLocalizedString("Медиа, ссылки и файлы", comment: "Message profile media title"),
description: NSLocalizedString("Плитки как в Telegram — скоро здесь появятся вложения из чата.", comment: "Message profile media description") ScrollView(.horizontal, showsIndicators: false) {
) { HStack(spacing: 12) {
card { ForEach(mediaCategories) { category in
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: 3), spacing: 8) { mediaCategoryButton(category)
ForEach(Array(sharedMediaPlaceholderIcons.enumerated()), id: \.offset) { index, icon in
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(sharedMediaPlaceholderColors[index % sharedMediaPlaceholderColors.count])
.frame(height: 72)
.overlay(
Image(systemName: icon)
.font(.system(size: 20, weight: .semibold))
.foregroundColor(.white.opacity(0.9))
)
.overlay(
RoundedRectangle(cornerRadius: 16, style: .continuous)
.stroke(Color.white.opacity(0.18), lineWidth: 1)
)
} }
} }
.frame(maxWidth: .infinity) .padding(.vertical, 4)
Text(NSLocalizedString("История медиа синхронизируется. Как только появятся первые вложения, они покажутся здесь списком превью.", comment: "Message profile media footer"))
.font(.caption)
.foregroundColor(.secondary)
.padding(.top, 12)
} }
Text(NSLocalizedString("Разделы временно показывают заглушки.", comment: "Message profile media footer new"))
.font(.caption)
.foregroundColor(.secondary)
} }
} }
@ -478,6 +461,30 @@ struct MessageProfileView: View {
.buttonStyle(.plain) .buttonStyle(.plain)
} }
private func filledActionButton(
title: String,
subtitle: String? = nil,
tint: Color,
action: @escaping () -> Void
) -> some View {
Button(action: action) {
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.body)
.fontWeight(.semibold)
if let subtitle {
Text(subtitle)
.font(.caption)
.foregroundColor(tint.opacity(0.7))
}
}
.foregroundColor(tint)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, 10)
}
.buttonStyle(.plain)
}
private func iconBackground<Content: View>(color: Color, @ViewBuilder content: () -> Content) -> some View { private func iconBackground<Content: View>(color: Color, @ViewBuilder content: () -> Content) -> some View {
RoundedRectangle(cornerRadius: 14, style: .continuous) RoundedRectangle(cornerRadius: 14, style: .continuous)
.fill(color) .fill(color)
@ -513,6 +520,20 @@ struct MessageProfileView: View {
showPlaceholderAction(title: title, message: message) showPlaceholderAction(title: title, message: message)
} }
private var mediaCategories: [MediaCategory] {
[
MediaCategory(title: NSLocalizedString("Медиа", comment: "Message profile category media")),
MediaCategory(title: NSLocalizedString("Сохранённые", comment: "Message profile category saved")),
MediaCategory(title: NSLocalizedString("Файлы", comment: "Message profile category files")),
MediaCategory(title: NSLocalizedString("Голосовые", comment: "Message profile category voice")),
MediaCategory(title: NSLocalizedString("Ссылки", comment: "Message profile category links")),
MediaCategory(title: NSLocalizedString("Группы", comment: "Message profile category groups")),
MediaCategory(title: NSLocalizedString("Музыка", comment: "Message profile category music")),
MediaCategory(title: NSLocalizedString("GIF", comment: "Message profile category gifs")),
MediaCategory(title: NSLocalizedString("Посты", comment: "Message profile category posts"))
]
}
// MARK: - Derived Data // MARK: - Derived Data
private var profileBio: String? { private var profileBio: String? {
@ -664,26 +685,31 @@ struct MessageProfileView: View {
return tags return tags
} }
private var sharedMediaPlaceholderIcons: [String] { private func mediaCategoryButton(_ category: MediaCategory) -> some View {
[ Button {
"photo.on.rectangle", showPlaceholderAction(
"doc.text.fill", title: category.title,
"link", message: NSLocalizedString("Раздел скоро станет активным — собираем и индексируем вложения.", comment: "Message profile media placeholder message")
"paperclip", )
"music.note", } label: {
"video.fill" VStack(alignment: .leading, spacing: 6) {
] Text(category.title)
} .font(.footnote)
.fontWeight(.medium)
private var sharedMediaPlaceholderColors: [Color] { .foregroundColor(.primary)
[ }
Color.accentColor.opacity(0.8), .padding(.vertical, 12)
Color.purple.opacity(0.8), .padding(.horizontal, 14)
Color.blue.opacity(0.7), .background(
Color.orange.opacity(0.8), RoundedRectangle(cornerRadius: 20, style: .continuous)
Color.green.opacity(0.7), .fill(Color(UIColor.secondarySystemBackground))
Color.pink.opacity(0.8) )
] .overlay(
RoundedRectangle(cornerRadius: 20, style: .continuous)
.stroke(Color(UIColor.separator).opacity(0.2), lineWidth: 1)
)
}
.buttonStyle(.plain)
} }
private var quickActionItems: [ProfileQuickAction] { private var quickActionItems: [ProfileQuickAction] {
@ -896,6 +922,11 @@ private struct StatusTag: Identifiable {
let tint: Color let tint: Color
} }
private struct MediaCategory: Identifiable {
let id = UUID()
let title: String
}
private struct ProfileQuickAction: Identifiable { private struct ProfileQuickAction: Identifiable {
let id = UUID() let id = UUID()
let icon: String let icon: String