Add instance information.
This commit is contained in:
parent
f4322f74e4
commit
8ec3817c52
|
@ -95,6 +95,7 @@
|
|||
"userProfile.title.unfollow" = "Unfollow";
|
||||
"userProfile.title.followBack" = "Follow back";
|
||||
"userProfile.title.follow" = "Follow";
|
||||
"userProfile.title.instance" = "Instance information";
|
||||
"userProfile.error.notExists" = "Account does not exists.";
|
||||
"userProfile.error.loadingAccountFailed" = "Error during download account from server.";
|
||||
"userProfile.error.muting" = "Muting/unmuting action failed.";
|
||||
|
@ -273,3 +274,21 @@
|
|||
"editProfile.error.loadingAvatarFailed" = "Loading avatar failed.";
|
||||
"editProfile.error.noProfileData" = "Profile data cannot be displayed.";
|
||||
"editProfile.error.loadingAccountFailed" = "Error during download account from server.";
|
||||
|
||||
// Mark: Instance information.
|
||||
"instance.navigationBar.title" = "Instance";
|
||||
"instance.title.instanceInfo" = "Instance info";
|
||||
"instance.title.name" = "Name";
|
||||
"instance.title.address" = "Address";
|
||||
"instance.title.email" = "Email";
|
||||
"instance.title.version" = "Version";
|
||||
"instance.title.users" = "Users";
|
||||
"instance.title.posts" = "Posts";
|
||||
"instance.title.domains" = "Domains";
|
||||
"instance.title.registrations" = "Registrations";
|
||||
"instance.title.approvalRequired" = "Approval required";
|
||||
"instance.title.rules" = "Instance rules";
|
||||
"instance.title.contact" = "Contact";
|
||||
"instance.title.pixelfedAccount" = "Pixelfed account";
|
||||
"instance.error.noInstanceData" = "Instance data cannot be displayed.";
|
||||
"instance.error.loadingDataFailed" = "Error during download instance data from server.";
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
"userProfile.title.unfollow" = "Przestań obserwować";
|
||||
"userProfile.title.followBack" = "Również obserwuj";
|
||||
"userProfile.title.follow" = "Obserwuj";
|
||||
"userProfile.title.instance" = "Informacje o instancji";
|
||||
"userProfile.error.notExists" = "Konto nie istnieje.";
|
||||
"userProfile.error.notExists" = "Błąd podczas pobierania danych użytkownika.";
|
||||
"userProfile.error.mute" = "Błąd podczas wyciszania użytkownika.";
|
||||
|
@ -273,3 +274,21 @@
|
|||
"editProfile.error.loadingAvatarFailed" = "Błąd podczas wczytywania zdjęcia.";
|
||||
"editProfile.error.noProfileData" = "Dane profilu nie mogą zostać wyświetlone.";
|
||||
"editProfile.error.loadingAccountFailed" = "Błąd podczas pobierania profilu użytkownika.";
|
||||
|
||||
// Mark: Instance information.
|
||||
"instance.navigationBar.title" = "Instancja";
|
||||
"instance.title.instanceInfo" = "Informacja o instancji";
|
||||
"instance.title.name" = "Nazwa";
|
||||
"instance.title.address" = "Adres";
|
||||
"instance.title.email" = "Email";
|
||||
"instance.title.version" = "Wersja";
|
||||
"instance.title.users" = "Użytkownicy";
|
||||
"instance.title.posts" = "Postów";
|
||||
"instance.title.domains" = "Domen";
|
||||
"instance.title.registrations" = "Rejestracja";
|
||||
"instance.title.approvalRequired" = "Akeptowanie rejestracji";
|
||||
"instance.title.rules" = "Reguły instancji";
|
||||
"instance.title.contact" = "Kontakt";
|
||||
"instance.title.pixelfedAccount" = "Konto Pixelfed";
|
||||
"instance.error.noInstanceData" = "Dane instancji nie mogą zostać wyświetlone.";
|
||||
"instance.error.loadingDataFailed" = "Błąd podczas pobierania danych instancji.";
|
||||
|
|
|
@ -50,6 +50,9 @@ public struct Instance: Codable {
|
|||
/// Statistics about the instance.
|
||||
public let stats: Stats?
|
||||
|
||||
/// Contact account.
|
||||
public let contactAccount: Account?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case uri
|
||||
case title
|
||||
|
@ -65,6 +68,7 @@ public struct Instance: Codable {
|
|||
case registrations
|
||||
case approvalRequired = "approval_required"
|
||||
case stats
|
||||
case contactAccount = "contact_account"
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
|
@ -84,5 +88,6 @@ public struct Instance: Codable {
|
|||
self.registrations = (try? container.decodeIfPresent(Bool.self, forKey: .registrations)) ?? false
|
||||
self.approvalRequired = (try? container.decodeIfPresent(Bool.self, forKey: .approvalRequired)) ?? false
|
||||
self.stats = try? container.decodeIfPresent(Stats.self, forKey: .stats)
|
||||
self.contactAccount = try? container.decodeIfPresent(Account.self, forKey: .contactAccount)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@
|
|||
F89AC00729A208CC00F4159F /* PlaceSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89AC00629A208CC00F4159F /* PlaceSelectorView.swift */; };
|
||||
F89AC00929A20C5C00F4159F /* Client+Places.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89AC00829A20C5C00F4159F /* Client+Places.swift */; };
|
||||
F89B5CC029D019B600549F2F /* HTMLString in Frameworks */ = {isa = PBXBuildFile; productRef = F89B5CBF29D019B600549F2F /* HTMLString */; };
|
||||
F89B5CC229D01BF700549F2F /* InstanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89B5CC129D01BF700549F2F /* InstanceView.swift */; };
|
||||
F89CEB802984198600A1376F /* AttachmentData+HighestImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89CEB7F2984198600A1376F /* AttachmentData+HighestImage.swift */; };
|
||||
F89D6C3F29716E41001DA3D4 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C3E29716E41001DA3D4 /* Theme.swift */; };
|
||||
F89D6C4229717FDC001DA3D4 /* AccountsSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C4129717FDC001DA3D4 /* AccountsSectionView.swift */; };
|
||||
|
@ -374,6 +375,7 @@
|
|||
F89AC00429A1F9B500F4159F /* AppMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMetadata.swift; sourceTree = "<group>"; };
|
||||
F89AC00629A208CC00F4159F /* PlaceSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceSelectorView.swift; sourceTree = "<group>"; };
|
||||
F89AC00829A20C5C00F4159F /* Client+Places.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Places.swift"; sourceTree = "<group>"; };
|
||||
F89B5CC129D01BF700549F2F /* InstanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceView.swift; sourceTree = "<group>"; };
|
||||
F89CEB7F2984198600A1376F /* AttachmentData+HighestImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttachmentData+HighestImage.swift"; sourceTree = "<group>"; };
|
||||
F89D6C3E29716E41001DA3D4 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||
F89D6C4129717FDC001DA3D4 /* AccountsSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsSectionView.swift; sourceTree = "<group>"; };
|
||||
|
@ -521,6 +523,7 @@
|
|||
F8FA991D299FAB92007AB130 /* PhotoEditorView.swift */,
|
||||
F89AC00629A208CC00F4159F /* PlaceSelectorView.swift */,
|
||||
F8E6D03229CDD52500416CCA /* EditProfileView.swift */,
|
||||
F89B5CC129D01BF700549F2F /* InstanceView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1216,6 +1219,7 @@
|
|||
F8CEEDFA29ABAFD200DBED66 /* ImageFileTranseferable.swift in Sources */,
|
||||
F802884F297AEED5000BDD51 /* DatabaseError.swift in Sources */,
|
||||
F86A4307299AA5E900DF7645 /* ThanksView.swift in Sources */,
|
||||
F89B5CC229D01BF700549F2F /* InstanceView.swift in Sources */,
|
||||
F88AB05829B36B8200345EDE /* AccountsPhotoView.swift in Sources */,
|
||||
F85D4971296402DC00751DF7 /* AuthorizationService.swift in Sources */,
|
||||
F8B9B356298D4C1E009CC69C /* Client+Instance.swift in Sources */,
|
||||
|
@ -1282,7 +1286,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 84;
|
||||
CURRENT_PROJECT_VERSION = 85;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
||||
|
@ -1310,7 +1314,7 @@
|
|||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 84;
|
||||
CURRENT_PROJECT_VERSION = 85;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
||||
|
@ -1458,7 +1462,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 84;
|
||||
CURRENT_PROJECT_VERSION = 85;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
@ -1498,7 +1502,7 @@
|
|||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 84;
|
||||
CURRENT_PROJECT_VERSION = 85;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
|
@ -46,6 +46,8 @@ extension View {
|
|||
SearchView()
|
||||
case .editProfile:
|
||||
EditProfileView()
|
||||
case .instance:
|
||||
InstanceView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ enum RouteurDestinations: Hashable {
|
|||
case accountsPhoto(listType: AccountsPhotoView.ListType)
|
||||
case search
|
||||
case editProfile
|
||||
case instance
|
||||
}
|
||||
|
||||
enum SheetDestinations: Identifiable {
|
||||
|
|
|
@ -61,7 +61,6 @@ struct ComposeView: View {
|
|||
}
|
||||
|
||||
private let statusViewModel: StatusModel?
|
||||
private let contentWidth = Int(UIScreen.main.bounds.width) - 50
|
||||
private let keyboardFontImageSize = 20.0
|
||||
private let keyboardFontTextSize = 16.0
|
||||
private let autocompleteFontTextSize = 12.0
|
||||
|
@ -216,7 +215,8 @@ struct ComposeView: View {
|
|||
Spacer()
|
||||
}
|
||||
|
||||
MarkdownFormattedText(status.content.asMarkdown, withFontSize: 14, andWidth: contentWidth)
|
||||
MarkdownFormattedText(status.content.asMarkdown)
|
||||
.font(.subheadline)
|
||||
.environment(\.openURL, OpenURLAction { url in .handled })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
import PixelfedKit
|
||||
|
||||
struct InstanceView: View {
|
||||
@EnvironmentObject private var applicationState: ApplicationState
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@EnvironmentObject private var client: Client
|
||||
|
||||
@State private var state: ViewState = .loading
|
||||
@State private var instance: Instance?
|
||||
|
||||
var body: some View {
|
||||
switch state {
|
||||
case .loading:
|
||||
LoadingIndicator()
|
||||
.task {
|
||||
await self.loadData()
|
||||
}
|
||||
case .loaded:
|
||||
if let instance = self.instance {
|
||||
self.details(instance: instance)
|
||||
} else {
|
||||
NoDataView(imageSystemName: "server.rack", text: "instance.error.noInstanceData")
|
||||
}
|
||||
case .error(let error):
|
||||
ErrorView(error: error) {
|
||||
self.state = .loading
|
||||
await self.loadData()
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func details(instance: Instance) -> some View {
|
||||
List {
|
||||
Section("instance.title.instanceInfo") {
|
||||
self.dataRow(title: "instance.title.name", value: instance.title ?? String.empty())
|
||||
self.dataRow(title: "instance.title.address", value: "https://\(instance.uri)")
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
if let description = instance.description {
|
||||
MarkdownFormattedText(description.asMarkdown)
|
||||
.font(.subheadline)
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routerPath.handle(url: url)
|
||||
})
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
|
||||
if let shortDescription = instance.shortDescription {
|
||||
Text(shortDescription)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.lightGrayColor)
|
||||
}
|
||||
}
|
||||
|
||||
self.dataRow(title: "instance.title.version", value: instance.version)
|
||||
self.dataRow(title: "instance.title.users", value: "\(instance.stats?.userCount ?? 0)")
|
||||
self.dataRow(title: "instance.title.posts", value: "\(instance.stats?.statusCount ?? 0)")
|
||||
self.dataRow(title: "instance.title.domains", value: "\(instance.stats?.domainCount ?? 0)")
|
||||
|
||||
Toggle("instance.title.registrations", isOn: Binding.constant(instance.registrations))
|
||||
.disabled(true)
|
||||
Toggle("instance.title.approvalRequired", isOn: Binding.constant(instance.approvalRequired))
|
||||
.disabled(true)
|
||||
}
|
||||
|
||||
Section("instance.title.contact") {
|
||||
self.dataRow(title: "instance.title.email", value: instance.email ?? String.empty())
|
||||
|
||||
if let contactAccount = instance.contactAccount {
|
||||
NavigationLink(value: RouteurDestinations.userProfile(
|
||||
accountId: contactAccount.id,
|
||||
accountDisplayName: contactAccount.displayNameWithoutEmojis,
|
||||
accountUserName: contactAccount.acct)
|
||||
) {
|
||||
HStack {
|
||||
Text("instance.title.pixelfedAccount", comment: "Pixelfed account")
|
||||
Spacer()
|
||||
Text("@\(contactAccount.displayNameWithoutEmojis)")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let rules = self.instance?.rules {
|
||||
Section("instance.title.rules") {
|
||||
ForEach(rules, id: \.id) { rule in
|
||||
Text(rule.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("instance.navigationBar.title")
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func dataRow(title: LocalizedStringKey, value: String) -> some View {
|
||||
HStack {
|
||||
Text(title, comment: "Title")
|
||||
Spacer()
|
||||
Text(value)
|
||||
.foregroundColor(.lightGrayColor)
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
|
||||
private func loadData() async {
|
||||
do {
|
||||
if let serverUrl = self.applicationState.account?.serverUrl {
|
||||
self.instance = try await self.client.instances.instance(url: serverUrl)
|
||||
}
|
||||
|
||||
self.state = .loaded
|
||||
} catch {
|
||||
if !Task.isCancelled {
|
||||
ErrorService.shared.handle(error, message: "instance.error.loadingDataFailed", showToastr: true)
|
||||
self.state = .error(error)
|
||||
} else {
|
||||
ErrorService.shared.handle(error, message: "instance.error.loadingDataFailed", showToastr: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,6 @@ struct NotificationRowView: View {
|
|||
|
||||
private var attachment: MediaAttachment?
|
||||
private var notification: PixelfedKit.Notification
|
||||
private let contentWidth = Int(UIScreen.main.bounds.width) - 150
|
||||
|
||||
public init(notification: PixelfedKit.Notification) {
|
||||
self.notification = notification
|
||||
|
@ -91,14 +90,16 @@ struct NotificationRowView: View {
|
|||
}
|
||||
case .mention:
|
||||
if let status = self.notification.status {
|
||||
MarkdownFormattedText(status.content.asMarkdown, withFontSize: 12, andWidth: contentWidth)
|
||||
MarkdownFormattedText(status.content.asMarkdown)
|
||||
.font(.caption)
|
||||
.environment(\.openURL, OpenURLAction { url in .handled })
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
case .follow, .followRequest, .adminSignUp:
|
||||
if let note = self.notification.account.note {
|
||||
MarkdownFormattedText(note.asMarkdown, withFontSize: 12, andWidth: contentWidth)
|
||||
MarkdownFormattedText(note.asMarkdown)
|
||||
.font(.caption)
|
||||
.environment(\.openURL, OpenURLAction { url in .handled })
|
||||
} else {
|
||||
EmptyView()
|
||||
|
|
|
@ -60,7 +60,8 @@ struct InstanceRowView: View {
|
|||
}
|
||||
|
||||
if let description = instance.description {
|
||||
MarkdownFormattedText(description.asMarkdown, withFontSize: 14)
|
||||
MarkdownFormattedText(description.asMarkdown)
|
||||
.font(.subheadline)
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routerPath.handle(url: url)
|
||||
})
|
||||
|
|
|
@ -80,6 +80,7 @@ struct StatusView: View {
|
|||
}
|
||||
|
||||
MarkdownFormattedText(statusViewModel.content.asMarkdown)
|
||||
.font(.callout)
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routerPath.handle(url: url)
|
||||
})
|
||||
|
|
|
@ -12,7 +12,6 @@ struct CommentBodyView: View {
|
|||
@EnvironmentObject var routerPath: RouterPath
|
||||
|
||||
@State var statusViewModel: StatusModel
|
||||
private let contentWidth = Int(UIScreen.main.bounds.width) - 60
|
||||
|
||||
var body: some View {
|
||||
HStack (alignment: .top) {
|
||||
|
@ -37,7 +36,8 @@ struct CommentBodyView: View {
|
|||
.font(.footnote)
|
||||
}
|
||||
|
||||
MarkdownFormattedText(self.statusViewModel.content.asMarkdown, withFontSize: 14, andWidth: contentWidth)
|
||||
MarkdownFormattedText(self.statusViewModel.content.asMarkdown)
|
||||
.font(.footnote)
|
||||
.environment(\.openURL, OpenURLAction { url in .handled })
|
||||
.padding(.top, 4)
|
||||
|
||||
|
|
|
@ -74,7 +74,8 @@ struct UserProfileHeaderView: View {
|
|||
}
|
||||
|
||||
if let note = account.note, !note.asMarkdown.isEmpty {
|
||||
MarkdownFormattedText(note.asMarkdown, withFontSize: 14, andWidth: Int(UIScreen.main.bounds.width) - 16)
|
||||
MarkdownFormattedText(note.asMarkdown)
|
||||
.font(.subheadline)
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routerPath.handle(url: url)
|
||||
})
|
||||
|
|
|
@ -160,6 +160,12 @@ struct UserProfileView: View {
|
|||
|
||||
Divider()
|
||||
}
|
||||
|
||||
NavigationLink(value: RouteurDestinations.instance) {
|
||||
Label(NSLocalizedString("userProfile.title.instance", comment: "Instance information"), systemImage: "server.rack")
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
NavigationLink(value: RouteurDestinations.favourites) {
|
||||
Label(NSLocalizedString("userProfile.title.favourites", comment: "Favourites"), systemImage: "hand.thumbsup")
|
||||
|
|
|
@ -14,17 +14,12 @@ struct MarkdownFormattedText: View {
|
|||
|
||||
private let markdown: String
|
||||
private let textView = UITextView()
|
||||
private let fontSize: CGFloat
|
||||
private let width: Int
|
||||
|
||||
init(_ markdown: String, withFontSize fontSize: CGFloat = 16, andWidth width: Int? = nil) {
|
||||
init(_ markdown: String) {
|
||||
self.markdown = markdown
|
||||
self.fontSize = fontSize
|
||||
self.width = width ?? Int(UIScreen.main.bounds.width) - 16
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
EmojiText(markdown: markdown, emojis: [])
|
||||
.font(.system(size: self.fontSize))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue