Refactoring

This commit is contained in:
Marcin Czachursk 2023-02-03 15:16:30 +01:00
parent ee3407dd69
commit f9ffe542a5
40 changed files with 581 additions and 702 deletions

View File

@ -14,18 +14,14 @@
F80048082961E6DE00E6868A /* StatusDataHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80048072961E6DE00E6868A /* StatusDataHandler.swift */; };
F800480A2961EA1900E6868A /* AttachmentDataHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80048092961EA1900E6868A /* AttachmentDataHandler.swift */; };
F802884F297AEED5000BDD51 /* DatabaseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F802884E297AEED5000BDD51 /* DatabaseError.swift */; };
F80864112975537F009F035C /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80864102975537F009F035C /* NotificationService.swift */; };
F808641429756666009F035C /* NotificationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F808641329756666009F035C /* NotificationRow.swift */; };
F8121CA5298A819100B466C7 /* InstanceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8121CA4298A819100B466C7 /* InstanceService.swift */; };
F8121CA8298A86D600B466C7 /* InstanceRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8121CA7298A86D600B466C7 /* InstanceRow.swift */; };
F8163776297C3E3D00E6E04A /* PublicTimelineService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8163775297C3E3D00E6E04A /* PublicTimelineService.swift */; };
F8210DCF2966B600001D9973 /* ImageRowAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DCE2966B600001D9973 /* ImageRowAsync.swift */; };
F8210DD52966BB7E001D9973 /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = F8210DD42966BB7E001D9973 /* Nuke */; };
F8210DD72966BB7E001D9973 /* NukeExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = F8210DD62966BB7E001D9973 /* NukeExtensions */; };
F8210DD92966BB7E001D9973 /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = F8210DD82966BB7E001D9973 /* NukeUI */; };
F8210DDD2966CF17001D9973 /* StatusData+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DDC2966CF17001D9973 /* StatusData+Status.swift */; };
F8210DDF2966CFC7001D9973 /* AttachmentData+Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DDE2966CFC7001D9973 /* AttachmentData+Attachment.swift */; };
F8210DE12966D0C4001D9973 /* StatusService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DE02966D0C4001D9973 /* StatusService.swift */; };
F8210DE52966E160001D9973 /* Color+SystemColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DE42966E160001D9973 /* Color+SystemColors.swift */; };
F8210DE72966E1D1001D9973 /* Color+Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DE62966E1D1001D9973 /* Color+Assets.swift */; };
F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DE92966E4F9001D9973 /* AnimatePlaceholderModifier.swift */; };
@ -83,7 +79,6 @@
F88C2482295C3A4F0006098B /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2481295C3A4F0006098B /* StatusView.swift */; };
F88E4D42297E69FD0057491A /* StatusesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D41297E69FD0057491A /* StatusesView.swift */; };
F88E4D44297E82EB0057491A /* Status+MediaAttachmentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D43297E82EB0057491A /* Status+MediaAttachmentType.swift */; };
F88E4D46297E89DF0057491A /* TrendsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D45297E89DF0057491A /* TrendsService.swift */; };
F88E4D48297E90CD0057491A /* TrendStatusesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D47297E90CD0057491A /* TrendStatusesView.swift */; };
F88E4D4A297EA0490057491A /* RouterPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D49297EA0490057491A /* RouterPath.swift */; };
F88E4D4D297EA4290057491A /* EmojiText in Frameworks */ = {isa = PBXBuildFile; productRef = F88E4D4C297EA4290057491A /* EmojiText */; };
@ -91,7 +86,6 @@
F88E4D52297EA6DA0057491A /* String+Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D51297EA6DA0057491A /* String+Markdown.swift */; };
F88E4D54297EA7EE0057491A /* MarkdownFormattedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D53297EA7EE0057491A /* MarkdownFormattedText.swift */; };
F88E4D56297EAD6E0057491A /* AppRouteur.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D55297EAD6E0057491A /* AppRouteur.swift */; };
F88E4D5A297ECEE60057491A /* SearchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88E4D59297ECEE60057491A /* SearchService.swift */; };
F88FAD21295F3944009B20C9 /* HomeFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD20295F3944009B20C9 /* HomeFeedView.swift */; };
F88FAD27295F400E009B20C9 /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD26295F400E009B20C9 /* NotificationsView.swift */; };
F88FAD2A295F43B8009B20C9 /* AccountData+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD28295F43B8009B20C9 /* AccountData+CoreDataClass.swift */; };
@ -119,14 +113,21 @@
F89D6C4629718193001DA3D4 /* ThemeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C4529718193001DA3D4 /* ThemeSection.swift */; };
F89D6C4A297196FF001DA3D4 /* ImagesViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C49297196FF001DA3D4 /* ImagesViewer.swift */; };
F8A93D7E2965FD89001D8331 /* UserProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A93D7D2965FD89001D8331 /* UserProfileView.swift */; };
F8A93D802965FED4001D8331 /* AccountService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A93D7F2965FED4001D8331 /* AccountService.swift */; };
F8B1E64F2973F61400EE0D10 /* Drops in Frameworks */ = {isa = PBXBuildFile; productRef = F8B1E64E2973F61400EE0D10 /* Drops */; };
F8B1E6512973FB7E00EE0D10 /* ToastrService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B1E6502973FB7E00EE0D10 /* ToastrService.swift */; };
F8B9B345298D1FCB009CC69C /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B9B344298D1FCB009CC69C /* Client.swift */; };
F8B9B347298D4A7C009CC69C /* Client+Trends.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B9B346298D4A7C009CC69C /* Client+Trends.swift */; };
F8B9B349298D4AA2009CC69C /* Client+Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B9B348298D4AA2009CC69C /* Client+Timeline.swift */; };
F8B9B34B298D4ACE009CC69C /* Client+Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B9B34A298D4ACE009CC69C /* Client+Tags.swift */; };
F8B9B34D298D4AE4009CC69C /* Client+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B9B34C298D4AE4009CC69C /* Client+Notifications.swift */; };
F8B9B34F298D4B14009CC69C /* Client+Statuses.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B9B34E298D4B14009CC69C /* Client+Statuses.swift */; };
F8B9B351298D4B34009CC69C /* Client+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B9B350298D4B34009CC69C /* Client+Account.swift */; };
F8B9B353298D4B5D009CC69C /* Client+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B9B352298D4B5D009CC69C /* Client+Search.swift */; };
F8B9B356298D4C1E009CC69C /* Client+Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B9B355298D4C1E009CC69C /* Client+Instance.swift */; };
F8C14392296AF0B3001FE31D /* String+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C14391296AF0B3001FE31D /* String+Exif.swift */; };
F8C14394296AF21B001FE31D /* Double+Round.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C14393296AF21B001FE31D /* Double+Round.swift */; };
F8C5E55F2988E92600ADF6A7 /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C5E55E2988E92600ADF6A7 /* AccountModel.swift */; };
F8C5E56229892CC300ADF6A7 /* FirstAppear.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C5E56129892CC300ADF6A7 /* FirstAppear.swift */; };
F8C7EDBF298169EE002843BC /* TagsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C7EDBE298169EE002843BC /* TagsService.swift */; };
F8CC95CE2970761D00C9C2AC /* TintColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CC95CD2970761D00C9C2AC /* TintColor.swift */; };
/* End PBXBuildFile section */
@ -138,15 +139,11 @@
F80048072961E6DE00E6868A /* StatusDataHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusDataHandler.swift; sourceTree = "<group>"; };
F80048092961EA1900E6868A /* AttachmentDataHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentDataHandler.swift; sourceTree = "<group>"; };
F802884E297AEED5000BDD51 /* DatabaseError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseError.swift; sourceTree = "<group>"; };
F80864102975537F009F035C /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
F808641329756666009F035C /* NotificationRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRow.swift; sourceTree = "<group>"; };
F8121CA4298A819100B466C7 /* InstanceService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceService.swift; sourceTree = "<group>"; };
F8121CA7298A86D600B466C7 /* InstanceRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceRow.swift; sourceTree = "<group>"; };
F8163775297C3E3D00E6E04A /* PublicTimelineService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineService.swift; sourceTree = "<group>"; };
F8210DCE2966B600001D9973 /* ImageRowAsync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRowAsync.swift; sourceTree = "<group>"; };
F8210DDC2966CF17001D9973 /* StatusData+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusData+Status.swift"; sourceTree = "<group>"; };
F8210DDE2966CFC7001D9973 /* AttachmentData+Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttachmentData+Attachment.swift"; sourceTree = "<group>"; };
F8210DE02966D0C4001D9973 /* StatusService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusService.swift; sourceTree = "<group>"; };
F8210DE42966E160001D9973 /* Color+SystemColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+SystemColors.swift"; sourceTree = "<group>"; };
F8210DE62966E1D1001D9973 /* Color+Assets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Assets.swift"; sourceTree = "<group>"; };
F8210DE92966E4F9001D9973 /* AnimatePlaceholderModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimatePlaceholderModifier.swift; sourceTree = "<group>"; };
@ -207,13 +204,11 @@
F88C2481295C3A4F0006098B /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
F88E4D41297E69FD0057491A /* StatusesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusesView.swift; sourceTree = "<group>"; };
F88E4D43297E82EB0057491A /* Status+MediaAttachmentType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Status+MediaAttachmentType.swift"; sourceTree = "<group>"; };
F88E4D45297E89DF0057491A /* TrendsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsService.swift; sourceTree = "<group>"; };
F88E4D47297E90CD0057491A /* TrendStatusesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendStatusesView.swift; sourceTree = "<group>"; };
F88E4D49297EA0490057491A /* RouterPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterPath.swift; sourceTree = "<group>"; };
F88E4D51297EA6DA0057491A /* String+Markdown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Markdown.swift"; sourceTree = "<group>"; };
F88E4D53297EA7EE0057491A /* MarkdownFormattedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownFormattedText.swift; sourceTree = "<group>"; };
F88E4D55297EAD6E0057491A /* AppRouteur.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouteur.swift; sourceTree = "<group>"; };
F88E4D59297ECEE60057491A /* SearchService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchService.swift; sourceTree = "<group>"; };
F88FAD20295F3944009B20C9 /* HomeFeedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeFeedView.swift; sourceTree = "<group>"; };
F88FAD26295F400E009B20C9 /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
F88FAD28295F43B8009B20C9 /* AccountData+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountData+CoreDataClass.swift"; sourceTree = "<group>"; };
@ -240,13 +235,20 @@
F89D6C4529718193001DA3D4 /* ThemeSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeSection.swift; sourceTree = "<group>"; };
F89D6C49297196FF001DA3D4 /* ImagesViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagesViewer.swift; sourceTree = "<group>"; };
F8A93D7D2965FD89001D8331 /* UserProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileView.swift; sourceTree = "<group>"; };
F8A93D7F2965FED4001D8331 /* AccountService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountService.swift; sourceTree = "<group>"; };
F8B1E6502973FB7E00EE0D10 /* ToastrService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastrService.swift; sourceTree = "<group>"; };
F8B9B344298D1FCB009CC69C /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; };
F8B9B346298D4A7C009CC69C /* Client+Trends.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Trends.swift"; sourceTree = "<group>"; };
F8B9B348298D4AA2009CC69C /* Client+Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Timeline.swift"; sourceTree = "<group>"; };
F8B9B34A298D4ACE009CC69C /* Client+Tags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Tags.swift"; sourceTree = "<group>"; };
F8B9B34C298D4AE4009CC69C /* Client+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Notifications.swift"; sourceTree = "<group>"; };
F8B9B34E298D4B14009CC69C /* Client+Statuses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Statuses.swift"; sourceTree = "<group>"; };
F8B9B350298D4B34009CC69C /* Client+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Account.swift"; sourceTree = "<group>"; };
F8B9B352298D4B5D009CC69C /* Client+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Search.swift"; sourceTree = "<group>"; };
F8B9B355298D4C1E009CC69C /* Client+Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+Instance.swift"; sourceTree = "<group>"; };
F8C14391296AF0B3001FE31D /* String+Exif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Exif.swift"; sourceTree = "<group>"; };
F8C14393296AF21B001FE31D /* Double+Round.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Round.swift"; sourceTree = "<group>"; };
F8C5E55E2988E92600ADF6A7 /* AccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountModel.swift; sourceTree = "<group>"; };
F8C5E56129892CC300ADF6A7 /* FirstAppear.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstAppear.swift; sourceTree = "<group>"; };
F8C7EDBE298169EE002843BC /* TagsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsService.swift; sourceTree = "<group>"; };
F8C937A929882CA90004D782 /* Vernissage-001.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-001.xcdatamodel"; sourceTree = "<group>"; };
F8CC95CD2970761D00C9C2AC /* TintColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TintColor.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -348,7 +350,6 @@
F89992CD296D92E7005994BF /* AttachmentModel.swift */,
F898DE7129728CB2004B4A6A /* CommentModel.swift */,
F8C5E55E2988E92600ADF6A7 /* AccountModel.swift */,
F88FAD2C295F4AD7009B20C9 /* ApplicationState.swift */,
F866F6AD29606367002E8F88 /* ApplicationViewMode.swift */,
F8CC95CD2970761D00C9C2AC /* TintColor.swift */,
F89D6C3E29716E41001DA3D4 /* Theme.swift */,
@ -463,6 +464,7 @@
F83901A2295D863B00456AE2 /* Widgets */,
F8341F96295C6427009C8EE6 /* CoreData */,
F8341F95295C640C009C8EE6 /* Models */,
F8B9B354298D4B88009CC69C /* EnvironmentObjects */,
F8341F94295C63FE009C8EE6 /* Extensions */,
F8341F93295C63E2009C8EE6 /* Views */,
F88C246B295C37B80006098B /* VernissageApp.swift */,
@ -491,19 +493,11 @@
F85D4970296402DC00751DF7 /* AuthorizationService.swift */,
F87AEB912986C44E00434FB6 /* AuthorizationSession.swift */,
F85D4974296407F100751DF7 /* HomeTimelineService.swift */,
F8A93D7F2965FED4001D8331 /* AccountService.swift */,
F8210DE02966D0C4001D9973 /* StatusService.swift */,
F8B1E6502973FB7E00EE0D10 /* ToastrService.swift */,
F85E131F297409CD006A051D /* ErrorsService.swift */,
F80864102975537F009F035C /* NotificationService.swift */,
F886F256297859E300879356 /* CacheImageService.swift */,
F8163775297C3E3D00E6E04A /* PublicTimelineService.swift */,
F88E4D45297E89DF0057491A /* TrendsService.swift */,
F88E4D49297EA0490057491A /* RouterPath.swift */,
F88E4D59297ECEE60057491A /* SearchService.swift */,
F8C7EDBE298169EE002843BC /* TagsService.swift */,
F829193B2983012400367CE2 /* ImageSizeService.swift */,
F8121CA4298A819100B466C7 /* InstanceService.swift */,
);
path = Services;
sourceTree = "<group>";
@ -553,6 +547,23 @@
path = StatusView;
sourceTree = "<group>";
};
F8B9B354298D4B88009CC69C /* EnvironmentObjects */ = {
isa = PBXGroup;
children = (
F88FAD2C295F4AD7009B20C9 /* ApplicationState.swift */,
F8B9B344298D1FCB009CC69C /* Client.swift */,
F8B9B346298D4A7C009CC69C /* Client+Trends.swift */,
F8B9B348298D4AA2009CC69C /* Client+Timeline.swift */,
F8B9B34A298D4ACE009CC69C /* Client+Tags.swift */,
F8B9B34C298D4AE4009CC69C /* Client+Notifications.swift */,
F8B9B34E298D4B14009CC69C /* Client+Statuses.swift */,
F8B9B350298D4B34009CC69C /* Client+Account.swift */,
F8B9B352298D4B5D009CC69C /* Client+Search.swift */,
F8B9B355298D4C1E009CC69C /* Client+Instance.swift */,
);
path = EnvironmentObjects;
sourceTree = "<group>";
};
F8C5E56029892C8A00ADF6A7 /* ViewModifiers */ = {
isa = PBXGroup;
children = (
@ -657,12 +668,11 @@
F866F6A0296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift in Sources */,
F8764189298ABEC80057D362 /* ErrorView.swift in Sources */,
F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */,
F8163776297C3E3D00E6E04A /* PublicTimelineService.swift in Sources */,
F886F257297859E300879356 /* CacheImageService.swift in Sources */,
F8984E4D296B648000A2610F /* UIImage+Blurhash.swift in Sources */,
F897978A2968314A00B22335 /* LoadingIndicator.swift in Sources */,
F8B9B351298D4B34009CC69C /* Client+Account.swift in Sources */,
F8210DE52966E160001D9973 /* Color+SystemColors.swift in Sources */,
F8C7EDBF298169EE002843BC /* TagsService.swift in Sources */,
F88FAD2B295F43B8009B20C9 /* AccountData+CoreDataProperties.swift in Sources */,
F85D4975296407F100751DF7 /* HomeTimelineService.swift in Sources */,
F80048062961850500E6868A /* StatusData+CoreDataProperties.swift in Sources */,
@ -670,8 +680,8 @@
F88C2475295C37BB0006098B /* CoreDataHandler.swift in Sources */,
F87AEB972986D16D00434FB6 /* AuthorisationError.swift in Sources */,
F829193C2983012400367CE2 /* ImageSizeService.swift in Sources */,
F8B9B345298D1FCB009CC69C /* Client.swift in Sources */,
F88FAD2A295F43B8009B20C9 /* AccountData+CoreDataClass.swift in Sources */,
F8210DE12966D0C4001D9973 /* StatusService.swift in Sources */,
F85DBF8F296732E20069BF89 /* AccountsView.swift in Sources */,
F85D49872964334100751DF7 /* String+Date.swift in Sources */,
F897978829681B9C00B22335 /* UserAvatar.swift in Sources */,
@ -679,6 +689,7 @@
F8210DDD2966CF17001D9973 /* StatusData+Status.swift in Sources */,
F8210DCF2966B600001D9973 /* ImageRowAsync.swift in Sources */,
F85D498329642FAC00751DF7 /* AttachmentData+Comperable.swift in Sources */,
F8B9B353298D4B5D009CC69C /* Client+Search.swift in Sources */,
F85D497B29640C8200751DF7 /* UsernameRow.swift in Sources */,
F89D6C4429718092001DA3D4 /* AccentsSection.swift in Sources */,
F88E4D42297E69FD0057491A /* StatusesView.swift in Sources */,
@ -686,7 +697,6 @@
F85D497929640B9D00751DF7 /* ImagesCarousel.swift in Sources */,
F8C5E55F2988E92600ADF6A7 /* AccountModel.swift in Sources */,
F89D6C3F29716E41001DA3D4 /* Theme.swift in Sources */,
F88E4D5A297ECEE60057491A /* SearchService.swift in Sources */,
F8CC95CE2970761D00C9C2AC /* TintColor.swift in Sources */,
F89992CC296D9231005994BF /* StatusModel.swift in Sources */,
F80048052961850500E6868A /* StatusData+CoreDataClass.swift in Sources */,
@ -698,16 +708,16 @@
F8B1E6512973FB7E00EE0D10 /* ToastrService.swift in Sources */,
F88E4D48297E90CD0057491A /* TrendStatusesView.swift in Sources */,
F89992CE296D92E7005994BF /* AttachmentModel.swift in Sources */,
F8121CA5298A819100B466C7 /* InstanceService.swift in Sources */,
F800480A2961EA1900E6868A /* AttachmentDataHandler.swift in Sources */,
F80048032961850500E6868A /* AttachmentData+CoreDataClass.swift in Sources */,
F897978D2968369600B22335 /* HapticService.swift in Sources */,
F8341F90295C636C009C8EE6 /* Data+Exif.swift in Sources */,
F89D6C4A297196FF001DA3D4 /* ImagesViewer.swift in Sources */,
F80864112975537F009F035C /* NotificationService.swift in Sources */,
F8A93D7E2965FD89001D8331 /* UserProfileView.swift in Sources */,
F88C246E295C37B80006098B /* MainView.swift in Sources */,
F86B721E296C458700EE59EC /* BlurredImage.swift in Sources */,
F8B9B349298D4AA2009CC69C /* Client+Timeline.swift in Sources */,
F8B9B34B298D4ACE009CC69C /* Client+Tags.swift in Sources */,
F88C2478295C37BB0006098B /* Vernissage.xcdatamodeld in Sources */,
F88E4D52297EA6DA0057491A /* String+Markdown.swift in Sources */,
F898DE7229728CB2004B4A6A /* CommentModel.swift in Sources */,
@ -723,13 +733,13 @@
F857F9FD297D8ED3002C109C /* ActionMenu.swift in Sources */,
F876418D298AE5020057D362 /* PaginableStatusesView.swift in Sources */,
F85D49852964301800751DF7 /* StatusData+Attachments.swift in Sources */,
F8B9B34D298D4AE4009CC69C /* Client+Notifications.swift in Sources */,
F8764187298ABB520057D362 /* ViewState.swift in Sources */,
F8210DE72966E1D1001D9973 /* Color+Assets.swift in Sources */,
F88ABD9429687CA4004EF61E /* ComposeView.swift in Sources */,
F89CEB802984198600A1376F /* AttachmentData+HighestImage.swift in Sources */,
F87AEB942986C51B00434FB6 /* AppConstants.swift in Sources */,
F86B7214296BFDCE00EE59EC /* UserProfileHeader.swift in Sources */,
F88E4D46297E89DF0057491A /* TrendsService.swift in Sources */,
F85D497D29640D5900751DF7 /* InteractionRow.swift in Sources */,
F86167C6297FE6CC004D1F67 /* AvatarShapesSection.swift in Sources */,
F866F6A729604629002E8F88 /* SignInView.swift in Sources */,
@ -739,12 +749,15 @@
F8121CA8298A86D600B466C7 /* InstanceRow.swift in Sources */,
F802884F297AEED5000BDD51 /* DatabaseError.swift in Sources */,
F85D4971296402DC00751DF7 /* AuthorizationService.swift in Sources */,
F8B9B356298D4C1E009CC69C /* Client+Instance.swift in Sources */,
F88E4D56297EAD6E0057491A /* AppRouteur.swift in Sources */,
F88FAD32295F5029009B20C9 /* RemoteFileService.swift in Sources */,
F88FAD27295F400E009B20C9 /* NotificationsView.swift in Sources */,
F86B7216296BFFDA00EE59EC /* UserProfileStatuses.swift in Sources */,
F897978F29684BCB00B22335 /* LoadingView.swift in Sources */,
F8B9B34F298D4B14009CC69C /* Client+Statuses.swift in Sources */,
F89992C9296D6DC7005994BF /* CommentBody.swift in Sources */,
F8B9B347298D4A7C009CC69C /* Client+Trends.swift in Sources */,
F88FAD2D295F4AD7009B20C9 /* ApplicationState.swift in Sources */,
F88E4D54297EA7EE0057491A /* MarkdownFormattedText.swift in Sources */,
F866F6A1296040A8002E8F88 /* ApplicationSettings+CoreDataProperties.swift in Sources */,
@ -753,7 +766,6 @@
F83CBEFB298298A1002972C8 /* ImageCarouselPicture.swift in Sources */,
F89A46DC296EAACE0062125F /* SettingsView.swift in Sources */,
F866F6AE29606367002E8F88 /* ApplicationViewMode.swift in Sources */,
F8A93D802965FED4001D8331 /* AccountService.swift in Sources */,
F866F6AA29605AFA002E8F88 /* SceneDelegate.swift in Sources */,
F86167C8297FE781004D1F67 /* AvatarShape.swift in Sources */,
F85D4973296406E700751DF7 /* BottomRight.swift in Sources */,

View File

@ -0,0 +1,86 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
/// Mastodon 'Account'.
extension Client {
public class Accounts: BaseClient {
public func account(withId accountId: String) async throws -> Account? {
return try await mastodonClient.account(for: accountId)
}
public func relationships(withId accountId: String) async throws -> Relationship? {
return try await mastodonClient.relationships(for: accountId)
}
public func statuses(createdBy accountId: String,
onlyMedia: Bool = true,
excludeReplies: Bool = true,
maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 40) async throws -> [Status] {
return try await mastodonClient.statuses(for: accountId,
onlyMedia: onlyMedia,
excludeReplies: excludeReplies,
maxId: maxId,
sinceId: sinceId,
minId: minId,
limit: limit)
}
public func follow(account accountId: String) async throws -> Relationship? {
return try await mastodonClient.follow(for: accountId)
}
public func unfollow(account accountId: String) async throws -> Relationship? {
return try await mastodonClient.unfollow(for: accountId)
}
public func mute(account accountId: String) async throws -> Relationship? {
return try await mastodonClient.mute(for: accountId)
}
public func unmute(account accountId: String) async throws -> Relationship? {
return try await mastodonClient.unmute(for: accountId)
}
public func block(account accountId: String) async throws -> Relationship? {
return try await mastodonClient.block(for: accountId)
}
public func unblock(account accountId: String) async throws -> Relationship? {
return try await mastodonClient.unblock(for: accountId)
}
public func followers(account accountId: String, page: Int) async throws -> [Account] {
return try await mastodonClient.followers(for: accountId, page: page)
}
public func following(account accountId: String, page: Int) async throws -> [Account] {
return try await mastodonClient.following(for: accountId, page: page)
}
public func favourites(maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 10,
page: Int? = nil) async throws -> [Status] {
return try await mastodonClient.favourites(limit: limit, page: page)
}
public func bookmarks(maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 10,
page: Int? = nil) async throws -> [Status] {
return try await mastodonClient.bookmarks(limit: limit, page: page)
}
}
}

View File

@ -0,0 +1,84 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
/// Mastodon 'Search'.
extension Client {
public class Instances {
public let pixelfedInstances: [String] = [
"https://pixelfed.de",
"https://pixelfed.social",
"https://pxlmo.com",
"https://metapixl.com",
"https://pixey.org",
"https://pixel.tchncs.de",
"https://pixelfed.tokyo",
"https://pixelfed.fr",
"https://pixelfed.nz",
"https://pixelfed.au",
"https://pixelfed.eus",
"https://pixelfed.bachgau.social",
"https://pixelfed.es",
"https://pixelfed.cz",
"https://pixelfed.automat.click",
"https://gram.social",
"https://nixorigin.one",
"https://miniature.photography",
"https://fedifilm.art",
"https://fedipix.de",
"https://pixel.jabbxi.de",
"https://nodegray.com",
"https://socialpixels.xyz",
"https://pixel.mamutut.space",
"https://pixelfed.fioverse.zone",
"https://pixel.artemai.art",
"https://pix.anduin.net",
"https://jauntypix.net",
"https://faf.photos",
"https://pix.vleij.com",
"https://pixels.gsi.li",
"https://eorzea.photos"
]
func instances(urls: [String]) async -> [Instance] {
var instances: [Instance] = []
await withTaskGroup(of: Instance?.self) { group in
for url in urls {
group.addTask {
do {
if let baseUrl = URL(string: url) {
let client = MastodonClient(baseURL: baseUrl)
return try await client.readInstanceInformation()
}
return nil
} catch {
ErrorService.shared.handle(error, message: "Cannot download instance information: \(url.string)")
return nil
}
}
}
for await instance in group {
if let instance {
instances.append(instance)
}
}
}
return instances
}
func instance(url: URL) async throws -> Instance {
let client = MastodonClient(baseURL: url)
return try await client.readInstanceInformation()
}
}
}

View File

@ -0,0 +1,21 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
/// Mastodon 'Notifications'.
extension Client {
public class Notifications: BaseClient {
public func notifications(maxId: MaxId? = nil,
sinceId: SinceId? = nil,
minId: MinId? = nil,
limit: Int? = nil
) async throws -> Linkable<[MastodonKit.Notification]> {
return try await mastodonClient.notifications(maxId: maxId, sinceId: sinceId, minId: minId, limit: limit)
}
}
}

View File

@ -0,0 +1,17 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
/// Mastodon 'Search'.
extension Client {
public class Search: BaseClient {
public func search(query: String, resultsType: Mastodon.Search.ResultsType) async throws -> SearchResults? {
return try await mastodonClient.search(query: query, type: resultsType)
}
}
}

View File

@ -0,0 +1,75 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
/// Mastodon 'Statuses'.
extension Client {
public class Statuses: BaseClient {
public func status(withId statusId: String) async throws -> Status? {
return try await mastodonClient.status(statusId: statusId)
}
func favourite(statusId: String) async throws -> Status? {
return try await mastodonClient.favourite(statusId: statusId)
}
func unfavourite(statusId: String) async throws -> Status? {
return try await mastodonClient.unfavourite(statusId: statusId)
}
func boost(statusId: String) async throws -> Status? {
return try await mastodonClient.boost(statusId: statusId)
}
func unboost(statusId: String) async throws -> Status? {
return try await mastodonClient.unboost(statusId: statusId)
}
func bookmark(statusId: String) async throws -> Status? {
return try await mastodonClient.bookmark(statusId: statusId)
}
func unbookmark(statusId: String) async throws -> Status? {
return try await mastodonClient.unbookmark(statusId: statusId)
}
func new(status: Mastodon.Statuses.Components) async throws -> Status? {
return try await mastodonClient.new(statusComponents: status)
}
func comments(to statusId: String) async throws -> [CommentModel] {
var commentViewModels: [CommentModel] = []
try await self.getCommentDescendants(to: statusId, showDivider: true, to: &commentViewModels)
return commentViewModels
}
public func favouritedBy(statusId: String, page: Int) async throws -> [Account] {
return try await mastodonClient.favouritedBy(for: statusId, page: page)
}
public func rebloggedBy(statusId: String, page: Int) async throws -> [Account] {
return try await mastodonClient.rebloggedBy(for: statusId, page: page)
}
private func getCommentDescendants(to statusId: String, showDivider: Bool, to commentViewModels: inout [CommentModel]) async throws {
let context = try await mastodonClient.getContext(for: statusId)
let descendants = context.descendants.toStatusViewModel()
for status in descendants {
commentViewModels.append(CommentModel(status: status, showDivider: showDivider))
if status.repliesCount > 0 {
try await self.getCommentDescendants(to: status.id, showDivider: false, to: &commentViewModels)
}
}
}
}
}

View File

@ -0,0 +1,25 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
/// Mastodon 'Tags'.
extension Client {
public class Tags: BaseClient {
public func get(tag: String) async throws -> Tag? {
return try await mastodonClient.tag(hashtag: tag)
}
public func follow(tag: String) async throws -> Tag? {
return try await mastodonClient.follow(hashtag: tag)
}
public func unfollow(tag: String) async throws -> Tag? {
return try await mastodonClient.unfollow(hashtag: tag)
}
}
}

View File

@ -0,0 +1,45 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
/// Mastodon 'Timeline'.
extension Client {
public class PublicTimeline: BaseClient {
public func getStatuses(local: Bool,
remote: Bool,
maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 40) async throws -> [Status] {
return try await mastodonClient.getPublicTimeline(local: local,
remote: remote,
onlyMedia: true,
maxId: maxId,
sinceId: sinceId,
minId: minId,
limit: limit)
}
public func getTagStatuses(tag: String,
local: Bool,
remote: Bool,
maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 40) async throws -> [Status] {
return try await mastodonClient.getTagTimeline(tag: tag,
local: local,
remote: remote,
onlyMedia: true,
maxId: maxId,
sinceId: sinceId,
minId: minId,
limit: limit)
}
}
}

View File

@ -0,0 +1,17 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
/// Mastodon 'Trends'.
extension Client {
public class Trends: BaseClient {
public func statuses(range: Mastodon.PixelfedTrends.TrendRange) async throws -> [Status] {
return try await mastodonClient.statusesTrends(range: range)
}
}
}

View File

@ -0,0 +1,46 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
public class Client: ObservableObject {
public static let shared = Client()
private init() { }
private var mastodonClient: MastodonClientAuthenticated?
func setAccount(account: AccountModel) {
guard let accessToken = account.accessToken else {
return
}
self.mastodonClient = MastodonClient(baseURL: account.serverUrl).getAuthenticated(token: accessToken)
}
}
extension Client {
public var trends: Trends? { return Trends(mastodonClient: self.mastodonClient) }
public var publicTimeline: PublicTimeline? { return PublicTimeline(mastodonClient: self.mastodonClient) }
public var tags: Tags? { return Tags(mastodonClient: self.mastodonClient) }
public var notifications: Notifications? { return Notifications(mastodonClient: self.mastodonClient) }
public var statuses: Statuses? { return Statuses(mastodonClient: self.mastodonClient) }
public var accounts: Accounts? { return Accounts(mastodonClient: self.mastodonClient) }
public var search: Search? { return Search(mastodonClient: self.mastodonClient) }
public var instances: Instances { return Instances() }
}
public class BaseClient {
public var mastodonClient: MastodonClientAuthenticated
init?(mastodonClient: MastodonClientAuthenticated?) {
guard let mastodonClient else {
return nil
}
self.mastodonClient = mastodonClient
}
}

View File

@ -8,6 +8,7 @@ import Foundation
public enum AuthorisationError: Error {
case badServerUrl
case accessTokenNotFound
}
extension AuthorisationError: LocalizedError {
@ -15,6 +16,8 @@ extension AuthorisationError: LocalizedError {
switch self {
case .badServerUrl:
return NSLocalizedString("Bad url to server.", comment: "User enter bad URL to server.")
case .accessTokenNotFound:
return NSLocalizedString("Acess token not found.", comment: "Access token is not saved in account model.")
}
}
}

View File

@ -6,6 +6,7 @@
import SwiftUI
/// Modifier for using with view placeholders (with redacted modifier).
struct AnimatePlaceholderModifier: AnimatableModifier {
@Binding var isLoading: Bool
@ -39,6 +40,8 @@ struct AnimatePlaceholderModifier: AnimatableModifier {
}
extension View {
/// Animate redacted placeholders.
func animatePlaceholder(isLoading: Binding<Bool>) -> some View {
self.modifier(AnimatePlaceholderModifier(isLoading: isLoading))
}

View File

@ -1,153 +0,0 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
public class AccountService {
public static let shared = AccountService()
private init() { }
public func account(withId accountId: String, for account: AccountModel?) async throws -> Account? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.account(for: accountId)
}
public func relationships(withId accountId: String, for account: AccountModel?) async throws -> Relationship? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.relationships(for: accountId)
}
public func statuses(createdBy accountId: String,
for account: AccountModel?,
onlyMedia: Bool = true,
excludeReplies: Bool = true,
maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 40) async throws -> [Status] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.statuses(for: accountId,
onlyMedia: onlyMedia,
excludeReplies: excludeReplies,
maxId: maxId,
sinceId: sinceId,
minId: minId,
limit: limit)
}
public func follow(account accountId: String, for account: AccountModel?) async throws -> Relationship? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.follow(for: accountId)
}
public func unfollow(account accountId: String, for account: AccountModel?) async throws -> Relationship? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.unfollow(for: accountId)
}
public func mute(account accountId: String, for account: AccountModel?) async throws -> Relationship? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.mute(for: accountId)
}
public func unmute(account accountId: String, for account: AccountModel?) async throws -> Relationship? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.unmute(for: accountId)
}
public func block(account accountId: String, for account: AccountModel?) async throws -> Relationship? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.block(for: accountId)
}
public func unblock(account accountId: String, for account: AccountModel?) async throws -> Relationship? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.unblock(for: accountId)
}
public func followers(account accountId: String, for account: AccountModel?, page: Int) async throws -> [Account] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.followers(for: accountId, page: page)
}
public func following(account accountId: String, for account: AccountModel?, page: Int) async throws -> [Account] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.following(for: accountId, page: page)
}
public func favourites(for account: AccountModel?,
maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 10,
page: Int? = nil) async throws -> [Status] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.favourites(limit: limit, page: page)
}
public func bookmarks(for account: AccountModel?,
maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 10,
page: Int? = nil) async throws -> [Status] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.bookmarks(limit: limit, page: page)
}
}

View File

@ -8,6 +8,8 @@ import Foundation
import AuthenticationServices
public class AuthorizationSession: NSObject, ObservableObject, ASWebAuthenticationPresentationContextProviding {
/// Presentation anchor used during showing in app browser for sign in user (OAuth).
public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
DispatchQueue.main.sync {
return ASPresentationAnchor()

View File

@ -1,78 +0,0 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
public class InstanceService {
public static let shared = InstanceService()
private init() { }
public let pixelfedInstances: [String] = [
"https://pixelfed.de",
"https://pixelfed.social",
"https://pxlmo.com",
"https://metapixl.com",
"https://pixey.org",
"https://pixel.tchncs.de",
"https://pixelfed.tokyo",
"https://pixelfed.fr",
"https://pixelfed.nz",
"https://pixelfed.au",
"https://pixelfed.eus",
"https://pixelfed.bachgau.social",
"https://pixelfed.es",
"https://pixelfed.cz",
"https://pixelfed.automat.click",
"https://gram.social",
"https://nixorigin.one",
"https://miniature.photography",
"https://fedifilm.art",
"https://fedipix.de",
"https://pixel.jabbxi.de",
"https://nodegray.com",
"https://socialpixels.xyz",
"https://pixel.mamutut.space",
"https://pixelfed.fioverse.zone",
"https://pixel.artemai.art",
"https://pix.anduin.net",
"https://jauntypix.net",
"https://faf.photos",
"https://pix.vleij.com",
"https://pixels.gsi.li",
"https://eorzea.photos"
]
func instances(urls: [String]) async -> [Instance] {
var instances: [Instance] = []
await withTaskGroup(of: Instance?.self) { group in
for url in urls {
group.addTask {
do {
if let baseUrl = URL(string: url) {
let client = MastodonClient(baseURL: baseUrl)
return try await client.readInstanceInformation()
}
return nil
} catch {
ErrorService.shared.handle(error, message: "Cannot download instance information: \(url.string)")
return nil
}
}
}
for await instance in group {
if let instance {
instances.append(instance)
}
}
}
return instances
}
}

View File

@ -1,27 +0,0 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
public class NotificationService {
public static let shared = NotificationService()
private init() { }
public func notifications(for account: AccountModel?,
maxId: MaxId? = nil,
sinceId: SinceId? = nil,
minId: MinId? = nil,
limit: Int? = nil
) async throws -> Linkable<[MastodonKit.Notification]> {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return Linkable<[MastodonKit.Notification]>(data: [])
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.notifications(maxId: maxId, sinceId: sinceId, minId: minId, limit: limit)
}
}

View File

@ -1,44 +0,0 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
public class PublicTimelineService {
public static let shared = PublicTimelineService()
private init() { }
public func getStatuses(for account: AccountModel?,
local: Bool,
remote: Bool,
maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 40) async throws -> [Status] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.getPublicTimeline(local: local, remote: remote, onlyMedia: true, maxId: maxId, sinceId: sinceId, minId: minId, limit: limit)
}
public func getTagStatuses(for account: AccountModel?,
tag: String,
local: Bool,
remote: Bool,
maxId: String? = nil,
sinceId: String? = nil,
minId: String? = nil,
limit: Int = 40) async throws -> [Status] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.getTagTimeline(tag: tag, local: local, remote: remote, onlyMedia: true, maxId: maxId, sinceId: sinceId, minId: minId, limit: limit)
}
}

View File

@ -47,14 +47,14 @@ class RouterPath: ObservableObject {
path.append(to)
}
public func handle(url: URL, account: AccountModel? = nil) -> OpenURLAction.Result {
public func handle(url: URL) -> OpenURLAction.Result {
if url.pathComponents.contains(where: { $0 == "tags" }), let tag = url.pathComponents.last {
navigate(to: .tag(hashTag: tag))
return .handled
} else if url.lastPathComponent.first == "@", let host = url.host {
let acct = "\(url.lastPathComponent)@\(host)"
Task {
await navigateToAccountFrom(acct: acct, url: url, account: account)
await navigateToAccountFrom(acct: acct, url: url)
}
return .handled
@ -63,13 +63,9 @@ class RouterPath: ObservableObject {
return urlHandler?(url) ?? .systemAction
}
public func navigateToAccountFrom(acct: String, url: URL, account: AccountModel? = nil) async {
guard let account else { return }
public func navigateToAccountFrom(acct: String, url: URL) async {
Task {
let results = try? await SearchService.shared.search(for: account,
query: acct,
resultsType: Mastodon.Search.ResultsType.accounts)
let results = try? await Client.shared.search?.search(query: acct, resultsType: Mastodon.Search.ResultsType.accounts)
if let accountFromApi = results?.accounts.first {
navigate(to: .userProfile(accountId: accountFromApi.id,

View File

@ -1,24 +0,0 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
public class SearchService {
public static let shared = SearchService()
private init() { }
public func search(for account: AccountModel?,
query: String,
resultsType: Mastodon.Search.ResultsType) async throws -> SearchResults? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.search(query: query, type: resultsType)
}
}

View File

@ -1,125 +0,0 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
public class StatusService {
public static let shared = StatusService()
private init() { }
public func status(withId statusId: String, for account: AccountModel?) async throws -> Status? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.status(statusId: statusId)
}
func favourite(statusId: String, for account: AccountModel?) async throws -> Status? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.favourite(statusId: statusId)
}
func unfavourite(statusId: String, for account: AccountModel?) async throws -> Status? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.unfavourite(statusId: statusId)
}
func boost(statusId: String, for account: AccountModel?) async throws -> Status? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.boost(statusId: statusId)
}
func unboost(statusId: String, for account: AccountModel?) async throws -> Status? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.unboost(statusId: statusId)
}
func bookmark(statusId: String, for account: AccountModel?) async throws -> Status? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.bookmark(statusId: statusId)
}
func unbookmark(statusId: String, for account: AccountModel?) async throws -> Status? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.unbookmark(statusId: statusId)
}
func new(status: Mastodon.Statuses.Components, for account: AccountModel?) async throws -> Status? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.new(statusComponents: status)
}
func comments(to statusId: String, for account: AccountModel) async throws -> [CommentModel] {
var commentViewModels: [CommentModel] = []
let client = MastodonClient(baseURL: account.serverUrl).getAuthenticated(token: account.accessToken ?? String.empty())
try await self.getCommentDescendants(to: statusId, client: client, showDivider: true, to: &commentViewModels)
return commentViewModels
}
public func favouritedBy(statusId: String, for account: AccountModel?, page: Int) async throws -> [Account] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.favouritedBy(for: statusId, page: page)
}
public func rebloggedBy(statusId: String, for account: AccountModel?, page: Int) async throws -> [Account] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.rebloggedBy(for: statusId, page: page)
}
private func getCommentDescendants(to statusId: String, client: MastodonClientAuthenticated, showDivider: Bool, to commentViewModels: inout [CommentModel]) async throws {
let context = try await client.getContext(for: statusId)
let descendants = context.descendants.toStatusViewModel()
for status in descendants {
commentViewModels.append(CommentModel(status: status, showDivider: showDivider))
if status.repliesCount > 0 {
try await self.getCommentDescendants(to: status.id, client: client, showDivider: false, to: &commentViewModels)
}
}
}
}

View File

@ -1,40 +0,0 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
public class TagsService {
public static let shared = TagsService()
private init() { }
public func get(tag: String, for account: AccountModel?) async throws -> Tag? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.tag(hashtag: tag)
}
public func follow(tag: String, for account: AccountModel?) async throws -> Tag? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.follow(hashtag: tag)
}
public func unfollow(tag: String, for account: AccountModel?) async throws -> Tag? {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return nil
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.unfollow(hashtag: tag)
}
}

View File

@ -1,23 +0,0 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonKit
public class TrendsService {
public static let shared = TrendsService()
private init() { }
public func statuses(for account: AccountModel?,
range: Mastodon.PixelfedTrends.TrendRange) async throws -> [Status] {
guard let accessToken = account?.accessToken, let serverUrl = account?.serverUrl else {
return []
}
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
return try await client.statusesTrends(range: range)
}
}

View File

@ -13,14 +13,15 @@ struct VernissageApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
let coreDataHandler = CoreDataHandler.shared
@StateObject var applicationState = ApplicationState.shared
@StateObject var client = Client.shared
@StateObject var routerPath = RouterPath()
@State var applicationViewMode: ApplicationViewMode = .loading
@State var tintColor = ApplicationState.shared.tintColor.color()
@State var theme = ApplicationState.shared.theme.colorScheme()
@StateObject private var routerPath = RouterPath()
var body: some Scene {
WindowGroup {
NavigationStack(path: $routerPath.path) {
@ -43,6 +44,7 @@ struct VernissageApp: App {
}
.environment(\.managedObjectContext, coreDataHandler.container.viewContext)
.environmentObject(applicationState)
.environmentObject(client)
.environmentObject(routerPath)
.tint(self.tintColor)
.preferredColorScheme(self.theme)
@ -65,8 +67,10 @@ struct VernissageApp: App {
}
Task { @MainActor in
self.applicationState.account = AccountModel(accountData: accountData)
let accountModel = AccountModel(accountData: accountData)
self.applicationState.account = accountModel
self.applicationState.lastSeenStatusId = accountData.lastSeenStatusId
self.client.setAccount(account: accountModel)
self.applicationViewMode = .mainView
}
}

View File

@ -17,6 +17,7 @@ struct AccountsView: View {
}
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@State var entityId: String
@State var listType: ListType
@ -124,25 +125,13 @@ struct AccountsView: View {
private func loadFromApi() async throws -> [Account] {
switch self.listType {
case .followers:
return try await AccountService.shared.followers(
account: self.entityId,
for: self.applicationState.account,
page: page)
return try await self.client.accounts?.followers(account: self.entityId, page: page) ?? []
case .following:
return try await AccountService.shared.following(
account: self.entityId,
for: self.applicationState.account,
page: page)
return try await self.client.accounts?.following(account: self.entityId, page: page) ?? []
case .favourited:
return try await StatusService.shared.favouritedBy(
statusId: self.entityId,
for: self.applicationState.account,
page: page)
return try await self.client.statuses?.favouritedBy(statusId: self.entityId, page: page) ?? []
case .reblogged:
return try await StatusService.shared.rebloggedBy(
statusId: self.entityId,
for: self.applicationState.account,
page: page)
return try await self.client.statuses?.rebloggedBy(statusId: self.entityId, page: page) ?? []
}
}

View File

@ -13,6 +13,7 @@ struct ComposeView: View {
}
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@Environment(\.dismiss) private var dismiss
@State var statusViewModel: StatusModel?
@ -100,9 +101,7 @@ struct ComposeView: View {
private func publishStatus() async {
do {
_ = try await StatusService.shared.new(
status: Mastodon.Statuses.Components(inReplyToId: self.statusViewModel?.id, text: self.text),
for: self.applicationState.account)
_ = try await self.client.statuses?.new(status: Mastodon.Statuses.Components(inReplyToId: self.statusViewModel?.id, text: self.text))
} catch {
ErrorService.shared.handle(error, message: "Error during post status.", showToastr: true)
}

View File

@ -16,7 +16,9 @@ struct MainView: View {
}
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@EnvironmentObject var routerPath: RouterPath
@State private var navBarTitle: String = "Home"
@ -151,7 +153,9 @@ struct MainView: View {
Menu {
ForEach(self.dbAccounts) { account in
Button {
self.applicationState.account = AccountModel(accountData: account)
let accountModel = AccountModel(accountData: account)
self.applicationState.account = accountModel
self.client.setAccount(account: accountModel)
self.applicationState.lastSeenStatusId = account.lastSeenStatusId
ApplicationSettingsHandler.shared.setAccountAsDefault(accountData: account)

View File

@ -9,6 +9,7 @@ import MastodonKit
struct NotificationsView: View {
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@State var accountId: String
@State private var notifications: [MastodonKit.Notification] = []
@ -70,21 +71,17 @@ struct NotificationsView: View {
func loadNotifications() async {
do {
let linkable = try await NotificationService.shared.notifications(
for: self.applicationState.account,
maxId: maxId,
minId: minId,
limit: 5)
self.minId = linkable.link?.minId
self.maxId = linkable.link?.maxId
self.notifications = linkable.data
if linkable.data.isEmpty {
self.allItemsLoaded = true
if let linkable = try await self.client.notifications?.notifications(maxId: maxId, minId: minId, limit: 5) {
self.minId = linkable.link?.minId
self.maxId = linkable.link?.maxId
self.notifications = linkable.data
if linkable.data.isEmpty {
self.allItemsLoaded = true
}
self.state = .loaded
}
self.state = .loaded
} catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Error during download notifications from server.", showToastr: true)
@ -97,18 +94,15 @@ struct NotificationsView: View {
private func loadMoreNotifications() async {
do {
let linkable = try await NotificationService.shared.notifications(
for: self.applicationState.account,
maxId: self.maxId,
limit: self.defaultPageSize)
if linkable.data.isEmpty {
self.allItemsLoaded = true
return
if let linkable = try await self.client.notifications?.notifications(maxId: self.maxId, limit: self.defaultPageSize) {
if linkable.data.isEmpty {
self.allItemsLoaded = true
return
}
self.maxId = linkable.link?.maxId
self.notifications.append(contentsOf: linkable.data)
}
self.maxId = linkable.link?.maxId
self.notifications.append(contentsOf: linkable.data)
} catch {
ErrorService.shared.handle(error, message: "Error during download notifications from server.", showToastr: !Task.isCancelled)
}
@ -116,18 +110,15 @@ struct NotificationsView: View {
private func loadNewNotifications() async {
do {
let linkable = try await NotificationService.shared.notifications(
for: self.applicationState.account,
minId: self.minId,
limit: self.defaultPageSize)
if let first = linkable.data.first, self.notifications.contains(where: { notification in notification.id == first.id }) {
// We have all notifications, we don't have to do anything.
return
if let linkable = try await self.client.notifications?.notifications(minId: self.minId, limit: self.defaultPageSize) {
if let first = linkable.data.first, self.notifications.contains(where: { notification in notification.id == first.id }) {
// We have all notifications, we don't have to do anything.
return
}
self.minId = linkable.link?.minId
self.notifications.insert(contentsOf: linkable.data, at: 0)
}
self.minId = linkable.link?.minId
self.notifications.insert(contentsOf: linkable.data, at: 0)
} catch {
ErrorService.shared.handle(error, message: "Error during download notifications from server.", showToastr: !Task.isCancelled)
}

View File

@ -14,6 +14,7 @@ struct PaginableStatusesView: View {
}
@EnvironmentObject private var applicationState: ApplicationState
@EnvironmentObject private var client: Client
@EnvironmentObject private var routerPath: RouterPath
@State public var listType: ListType
@ -137,15 +138,9 @@ struct PaginableStatusesView: View {
switch self.listType {
case .favourites:
return try await AccountService.shared.favourites(
for: self.applicationState.account,
limit: self.defaultLimit,
page: self.page)
return try await self.client.accounts?.favourites(limit: self.defaultLimit, page: self.page) ?? []
case .bookmarks:
return try await AccountService.shared.bookmarks(
for: self.applicationState.account,
limit: self.defaultLimit,
page: self.page)
return try await self.client.accounts?.bookmarks(limit: self.defaultLimit, page: self.page) ?? []
}
}

View File

@ -11,7 +11,9 @@ import AuthenticationServices
struct SignInView: View {
@Environment(\.managedObjectContext) private var viewContext
@Environment(\.dismiss) private var dismiss
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@State private var serverAddress: String = String.empty()
@State private var instances: [Instance] = []
@ -62,7 +64,7 @@ struct SignInView: View {
}
.onFirstAppear {
self.instances = await InstanceService.shared.instances(urls: InstanceService.shared.pixelfedInstances)
self.instances = await self.client.instances.instances(urls: self.client.instances.pixelfedInstances)
}
.navigationBarTitle("Sign in to Pixelfed")
.navigationBarTitleDisplayMode(.inline)

View File

@ -10,6 +10,7 @@ import AVFoundation
struct StatusView: View {
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@EnvironmentObject var routerPath: RouterPath
@Environment(\.dismiss) private var dismiss
@ -80,7 +81,7 @@ struct StatusView: View {
MarkdownFormattedText(statusViewModel.content.asMarkdown)
.environment(\.openURL, OpenURLAction { url in
routerPath.handle(url: url, account: self.applicationState.account)
routerPath.handle(url: url)
})
VStack (alignment: .leading) {
@ -145,7 +146,7 @@ struct StatusView: View {
private func loadData() async {
do {
// Get status from API.
if let status = try await StatusService.shared.status(withId: self.statusId, for: self.applicationState.account) {
if let status = try await self.client.statuses?.status(withId: self.statusId) {
let statusViewModel = StatusModel(status: status)
self.statusViewModel = statusViewModel

View File

@ -17,6 +17,7 @@ struct StatusesView: View {
}
@EnvironmentObject private var applicationState: ApplicationState
@EnvironmentObject private var client: Client
@EnvironmentObject private var routerPath: RouterPath
@State public var listType: ListType
@ -162,47 +163,42 @@ struct StatusesView: View {
private func loadFromApi(maxId: String? = nil, sinceId: String? = nil, minId: String? = nil) async throws -> [Status] {
switch self.listType {
case .local:
return try await PublicTimelineService.shared.getStatuses(
for: self.applicationState.account,
return try await self.client.publicTimeline?.getStatuses(
local: true,
remote: false,
maxId: maxId,
sinceId: sinceId,
minId: minId,
limit: self.defaultLimit)
limit: self.defaultLimit) ?? []
case .federated:
return try await PublicTimelineService.shared.getStatuses(
for: self.applicationState.account,
return try await self.client.publicTimeline?.getStatuses(
local: false,
remote: true,
maxId: maxId,
sinceId: sinceId,
minId: minId,
limit: self.defaultLimit)
limit: self.defaultLimit) ?? []
case .favourites:
return try await AccountService.shared.favourites(
for: self.applicationState.account,
return try await self.client.accounts?.favourites(
maxId: maxId,
sinceId: sinceId,
minId: minId,
limit: self.defaultLimit)
limit: self.defaultLimit) ?? []
case .bookmarks:
return try await AccountService.shared.bookmarks(
for: self.applicationState.account,
return try await self.client.accounts?.bookmarks(
maxId: maxId,
sinceId: sinceId,
minId: minId,
limit: self.defaultLimit)
limit: self.defaultLimit) ?? []
case .hashtag(let tag):
return try await PublicTimelineService.shared.getTagStatuses(
for: self.applicationState.account,
return try await self.client.publicTimeline?.getTagStatuses(
tag: tag,
local: false,
remote: true,
maxId: maxId,
sinceId: sinceId,
minId: minId,
limit: self.defaultLimit)
limit: self.defaultLimit) ?? []
}
}
@ -243,7 +239,7 @@ struct StatusesView: View {
private func loadTag(hashtag: String) async {
do {
self.tag = try await TagsService.shared.get(tag: hashtag, for: self.applicationState.account)
self.tag = try await self.client.tags?.get(tag: hashtag)
} catch {
ErrorService.shared.handle(error, message: "Error during loading tag from server.", showToastr: false)
}
@ -251,7 +247,7 @@ struct StatusesView: View {
private func follow(hashtag: String) async {
do {
self.tag = try await TagsService.shared.follow(tag: hashtag, for: self.applicationState.account)
self.tag = try await self.client.tags?.follow(tag: hashtag)
ToastrService.shared.showSuccess("You are following the tag.", imageSystemName: "number.square.fill")
} catch {
ErrorService.shared.handle(error, message: "Error during following tag.", showToastr: true)
@ -260,7 +256,7 @@ struct StatusesView: View {
private func unfollow(hashtag: String) async {
do {
self.tag = try await TagsService.shared.unfollow(tag: hashtag, for: self.applicationState.account)
self.tag = try await self.client.tags?.unfollow(tag: hashtag)
ToastrService.shared.showSuccess("Tag has been unfollowed.", imageSystemName: "number.square")
} catch {
ErrorService.shared.handle(error, message: "Error during unfollowing tag.", showToastr: true)

View File

@ -8,8 +8,9 @@ import SwiftUI
import MastodonKit
struct TrendStatusesView: View {
@EnvironmentObject private var applicationState: ApplicationState
@EnvironmentObject private var client: Client
@State public var accountId: String
@State private var tabSelectedValue: Mastodon.PixelfedTrends.TrendRange = .daily
@ -108,16 +109,14 @@ struct TrendStatusesView: View {
}
private func loadStatuses() async throws {
let statuses = try await TrendsService.shared.statuses(
for: self.applicationState.account,
range: tabSelectedValue)
var inPlaceStatuses: [StatusModel] = []
for item in statuses.getStatusesWithImagesOnly() {
inPlaceStatuses.append(StatusModel(status: item))
if let statuses = try await client.trends?.statuses(range: tabSelectedValue) {
var inPlaceStatuses: [StatusModel] = []
for item in statuses.getStatusesWithImagesOnly() {
inPlaceStatuses.append(StatusModel(status: item))
}
self.statusViewModels = inPlaceStatuses
}
self.statusViewModels = inPlaceStatuses
}
}

View File

@ -9,6 +9,7 @@ import MastodonKit
struct UserProfileView: View {
@EnvironmentObject private var applicationState: ApplicationState
@EnvironmentObject private var client: Client
@State public var accountId: String
@State public var accountDisplayName: String?
@ -58,8 +59,8 @@ struct UserProfileView: View {
private func loadData() async {
do {
async let relationshipTask = AccountService.shared.relationships(withId: self.accountId, for: self.applicationState.account)
async let accountTask = AccountService.shared.account(withId: self.accountId, for: self.applicationState.account)
async let relationshipTask = self.client.accounts?.relationships(withId: self.accountId)
async let accountTask = self.client.accounts?.account(withId: self.accountId)
// Wait for download account and relationships.
(self.relationship, self.account) = try await (relationshipTask, accountTask)
@ -155,17 +156,11 @@ struct UserProfileView: View {
private func onMuteAccount(account: Account) async {
do {
if self.relationship?.muting == true {
if let relationship = try await AccountService.shared.unmute(
account: account.id,
for: self.applicationState.account
) {
if let relationship = try await self.client.accounts?.unmute(account: account.id) {
self.relationship = relationship
}
} else {
if let relationship = try await AccountService.shared.mute(
account: account.id,
for: self.applicationState.account
) {
if let relationship = try await self.client.accounts?.mute(account: account.id) {
self.relationship = relationship
}
}
@ -177,17 +172,11 @@ struct UserProfileView: View {
private func onBlockAccount(account: Account) async {
do {
if self.relationship?.blocking == true {
if let relationship = try await AccountService.shared.unblock(
account: account.id,
for: self.applicationState.account
) {
if let relationship = try await self.client.accounts?.unblock(account: account.id) {
self.relationship = relationship
}
} else {
if let relationship = try await AccountService.shared.block(
account: account.id,
for: self.applicationState.account
) {
if let relationship = try await self.client.accounts?.block(account: account.id) {
self.relationship = relationship
}
}

View File

@ -8,6 +8,7 @@ import SwiftUI
struct ImageRow: View {
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@EnvironmentObject var routerPath: RouterPath
private let status: StatusData
@ -74,7 +75,7 @@ struct ImageRow: View {
}
.onLongPressGesture(minimumDuration: 0.2) {
Task {
try? await StatusService.shared.favourite(statusId: self.status.id, for: self.applicationState.account)
try? await self.client.statuses?.favourite(statusId: self.status.id)
}
self.showThumbImage = true

View File

@ -10,6 +10,7 @@ import NukeUI
struct ImageRowAsync: View {
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@EnvironmentObject var routerPath: RouterPath
@State public var statusViewModel: StatusModel
@ -129,7 +130,7 @@ struct ImageRowAsync: View {
}
.onLongPressGesture(minimumDuration: 0.2) {
Task {
try? await StatusService.shared.favourite(statusId: self.statusViewModel.id, for: self.applicationState.account)
try? await self.client.statuses?.favourite(statusId: self.statusViewModel.id)
}
self.showThumbImage = true

View File

@ -10,6 +10,7 @@ import Drops
struct InteractionRow: View {
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@EnvironmentObject var routerPath: RouterPath
@State var statusViewModel: StatusModel
@ -38,8 +39,8 @@ struct InteractionRow: View {
ActionButton {
do {
let status = self.reblogged
? try await StatusService.shared.unboost(statusId: self.statusViewModel.id, for: self.applicationState.account)
: try await StatusService.shared.boost(statusId: self.statusViewModel.id, for: self.applicationState.account)
? try await self.client.statuses?.unboost(statusId: self.statusViewModel.id)
: try await self.client.statuses?.boost(statusId: self.statusViewModel.id)
if let status {
self.reblogsCount = status.reblogsCount == self.reblogsCount
@ -66,8 +67,8 @@ struct InteractionRow: View {
ActionButton {
do {
let status = self.favourited
? try await StatusService.shared.unfavourite(statusId: self.statusViewModel.id, for: self.applicationState.account)
: try await StatusService.shared.favourite(statusId: self.statusViewModel.id, for: self.applicationState.account)
? try await self.client.statuses?.unfavourite(statusId: self.statusViewModel.id)
: try await self.client.statuses?.favourite(statusId: self.statusViewModel.id)
if let status {
self.favouritesCount = status.favouritesCount == self.favouritesCount
@ -94,8 +95,8 @@ struct InteractionRow: View {
ActionButton {
do {
_ = self.bookmarked
? try await StatusService.shared.unbookmark(statusId: self.statusViewModel.id, for: self.applicationState.account)
: try await StatusService.shared.bookmark(statusId: self.statusViewModel.id, for: self.applicationState.account)
? try await self.client.statuses?.unbookmark(statusId: self.statusViewModel.id)
: try await self.client.statuses?.bookmark(statusId: self.statusViewModel.id)
self.bookmarked.toggle()
ToastrService.shared.showSuccess("Bookmarked", imageSystemName: "bookmark.fill")

View File

@ -10,6 +10,7 @@ import MastodonKit
struct CommentsSection: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var applicationState: ApplicationState
@EnvironmentObject var client: Client
@State public var statusId: String
@State private var commentViewModels: [CommentModel]?
@ -51,9 +52,7 @@ struct CommentsSection: View {
}
.task {
do {
if let accountData = applicationState.account {
self.commentViewModels = try await StatusService.shared.comments(to: statusId, for: accountData)
}
self.commentViewModels = try await self.client.statuses?.comments(to: statusId) ?? []
} catch {
ErrorService.shared.handle(error, message: "Comments cannot be downloaded.", showToastr: !Task.isCancelled)
}

View File

@ -9,6 +9,7 @@ import MastodonKit
struct UserProfileHeader: View {
@EnvironmentObject private var applicationState: ApplicationState
@EnvironmentObject private var client: Client
@EnvironmentObject private var routerPath: RouterPath
@State var account: Account
@ -75,7 +76,7 @@ struct UserProfileHeader: View {
if let note = account.note, !note.isEmpty {
MarkdownFormattedText(note.asMarkdown, withFontSize: 14, andWidth: Int(UIScreen.main.bounds.width) - 16)
.environment(\.openURL, OpenURLAction { url in
routerPath.handle(url: url, account: self.applicationState.account)
routerPath.handle(url: url)
})
}
@ -104,17 +105,11 @@ struct UserProfileHeader: View {
private func onRelationshipButtonTap() async {
do {
if self.relationship?.following == true {
if let relationship = try await AccountService.shared.unfollow(
account: self.account.id,
for: self.applicationState.account
) {
if let relationship = try await self.client.accounts?.unfollow(account: self.account.id) {
self.relationship = relationship
}
} else {
if let relationship = try await AccountService.shared.follow(
account: self.account.id,
for: self.applicationState.account
) {
if let relationship = try await self.client.accounts?.follow(account: self.account.id) {
self.relationship = relationship
}
}

View File

@ -9,6 +9,8 @@ import MastodonKit
struct UserProfileStatuses: View {
@EnvironmentObject private var applicationState: ApplicationState
@EnvironmentObject private var client: Client
@State public var accountId: String
@State private var allItemsLoaded = false
@ -60,10 +62,7 @@ struct UserProfileStatuses: View {
}
private func loadStatuses() async throws {
let statuses = try await AccountService.shared.statuses(
createdBy: self.accountId,
for: self.applicationState.account,
limit: self.defaultLimit)
let statuses = try await self.client.accounts?.statuses(createdBy: self.accountId, limit: self.defaultLimit) ?? []
var inPlaceStatuses: [StatusModel] = []
for item in statuses {
@ -80,11 +79,7 @@ struct UserProfileStatuses: View {
private func loadMoreStatuses() async throws {
if let lastStatusId = self.statusViewModels.last?.id {
let previousStatuses = try await AccountService.shared.statuses(
createdBy: self.accountId,
for: self.applicationState.account,
maxId: lastStatusId,
limit: self.defaultLimit)
let previousStatuses = try await self.client.accounts?.statuses(createdBy: self.accountId, maxId: lastStatusId, limit: self.defaultLimit) ?? []
if previousStatuses.isEmpty {
self.allItemsLoaded = true