chore: [WIP] restore the replyTo entry for compose

This commit is contained in:
CMK 2022-10-10 19:14:52 +08:00
parent 56f04db40f
commit 02e3ad9a16
105 changed files with 4011 additions and 4041 deletions

View File

@ -28,7 +28,6 @@
2D206B8625F5FB0900143C56 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B8525F5FB0900143C56 /* Double.swift */; }; 2D206B8625F5FB0900143C56 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B8525F5FB0900143C56 /* Double.swift */; };
2D206B9225F60EA700143C56 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B9125F60EA700143C56 /* UIControl.swift */; }; 2D206B9225F60EA700143C56 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B9125F60EA700143C56 /* UIControl.swift */; };
2D24E1232626ED9D00A59D4F /* UIView+Gesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D24E1222626ED9D00A59D4F /* UIView+Gesture.swift */; }; 2D24E1232626ED9D00A59D4F /* UIView+Gesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D24E1222626ED9D00A59D4F /* UIView+Gesture.swift */; };
2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */; };
2D35237A26256D920031AF25 /* NotificationSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D35237926256D920031AF25 /* NotificationSection.swift */; }; 2D35237A26256D920031AF25 /* NotificationSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D35237926256D920031AF25 /* NotificationSection.swift */; };
2D364F7225E66D7500204FDC /* MastodonResendEmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D364F7125E66D7500204FDC /* MastodonResendEmailViewController.swift */; }; 2D364F7225E66D7500204FDC /* MastodonResendEmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D364F7125E66D7500204FDC /* MastodonResendEmailViewController.swift */; };
2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D364F7725E66D8300204FDC /* MastodonResendEmailViewModel.swift */; }; 2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D364F7725E66D8300204FDC /* MastodonResendEmailViewModel.swift */; };
@ -44,12 +43,10 @@
2D571B2F26004EC000540450 /* NavigationBarProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D571B2E26004EC000540450 /* NavigationBarProgressView.swift */; }; 2D571B2F26004EC000540450 /* NavigationBarProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D571B2E26004EC000540450 /* NavigationBarProgressView.swift */; };
2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D59819A25E4A581000FB903 /* MastodonConfirmEmailViewController.swift */; }; 2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D59819A25E4A581000FB903 /* MastodonConfirmEmailViewController.swift */; };
2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5981A025E4A593000FB903 /* MastodonConfirmEmailViewModel.swift */; }; 2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5981A025E4A593000FB903 /* MastodonConfirmEmailViewModel.swift */; };
2D5A3D0325CF8742002347D6 /* ControlContainableScrollViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */; };
2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */; }; 2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */; };
2D5A3D3825CF8D9F002347D6 /* ScrollViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */; }; 2D5A3D3825CF8D9F002347D6 /* ScrollViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */; };
2D5A3D6225CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D6125CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift */; }; 2D5A3D6225CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D6125CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift */; };
2D607AD826242FC500B70763 /* NotificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D607AD726242FC500B70763 /* NotificationViewModel.swift */; }; 2D607AD826242FC500B70763 /* NotificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D607AD726242FC500B70763 /* NotificationViewModel.swift */; };
2D650FAB25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D650FAA25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift */; };
2D694A7425F9EB4E0038ADDC /* ContentWarningOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D694A7325F9EB4E0038ADDC /* ContentWarningOverlayView.swift */; }; 2D694A7425F9EB4E0038ADDC /* ContentWarningOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D694A7325F9EB4E0038ADDC /* ContentWarningOverlayView.swift */; };
2D6DE40026141DF600A63F6A /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D6DE3FF26141DF600A63F6A /* SearchViewModel.swift */; }; 2D6DE40026141DF600A63F6A /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D6DE3FF26141DF600A63F6A /* SearchViewModel.swift */; };
2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76319E25C1521200929FB9 /* StatusSection.swift */; }; 2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76319E25C1521200929FB9 /* StatusSection.swift */; };
@ -62,13 +59,9 @@
2D84350525FF858100EECE90 /* UIScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D84350425FF858100EECE90 /* UIScrollView.swift */; }; 2D84350525FF858100EECE90 /* UIScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D84350425FF858100EECE90 /* UIScrollView.swift */; };
2D939AB525EDD8A90076FA61 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; }; 2D939AB525EDD8A90076FA61 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
2D939AE825EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AE725EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift */; }; 2D939AE825EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AE725EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift */; };
2DA504692601ADE7008F4E6C /* SawToothView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA504682601ADE7008F4E6C /* SawToothView.swift */; };
2DA7D04425CA52B200804E11 /* TimelineLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA7D04325CA52B200804E11 /* TimelineLoaderTableViewCell.swift */; };
2DA7D04A25CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA7D04925CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift */; };
2DAC9E38262FC2320062E1A6 /* SuggestionAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */; }; 2DAC9E38262FC2320062E1A6 /* SuggestionAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */; };
2DAC9E3E262FC2400062E1A6 /* SuggestionAccountViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAC9E3D262FC2400062E1A6 /* SuggestionAccountViewModel.swift */; }; 2DAC9E3E262FC2400062E1A6 /* SuggestionAccountViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAC9E3D262FC2400062E1A6 /* SuggestionAccountViewModel.swift */; };
2DAC9E46262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAC9E45262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift */; }; 2DAC9E46262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAC9E45262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift */; };
2DB72C8C262D764300CE6173 /* Mastodon+Entity+Notification+Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB72C8B262D764300CE6173 /* Mastodon+Entity+Notification+Type.swift */; };
2DCB73FD2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DCB73FC2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift */; }; 2DCB73FD2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DCB73FC2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift */; };
2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */; }; 2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */; };
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; }; 2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; };
@ -84,9 +77,6 @@
5D0393902612D259007FE196 /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D03938F2612D259007FE196 /* WebViewController.swift */; }; 5D0393902612D259007FE196 /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D03938F2612D259007FE196 /* WebViewController.swift */; };
5D0393962612D266007FE196 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0393952612D266007FE196 /* WebViewModel.swift */; }; 5D0393962612D266007FE196 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0393952612D266007FE196 /* WebViewModel.swift */; };
5DA732CC2629CEF500A92342 /* UIView+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA732CB2629CEF500A92342 /* UIView+Remove.swift */; }; 5DA732CC2629CEF500A92342 /* UIView+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA732CB2629CEF500A92342 /* UIView+Remove.swift */; };
5DDDF1932617442700311060 /* Mastodon+Entity+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDF1922617442700311060 /* Mastodon+Entity+Account.swift */; };
5DDDF1992617447F00311060 /* Mastodon+Entity+Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDF1982617447F00311060 /* Mastodon+Entity+Tag.swift */; };
5DDDF1A92617489F00311060 /* Mastodon+Entity+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDF1A82617489F00311060 /* Mastodon+Entity+History.swift */; };
5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */; }; 5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */; };
5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */; }; 5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */; };
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; }; 5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
@ -110,8 +100,6 @@
DB02CDBF2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB02CDBE2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift */; }; DB02CDBF2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB02CDBE2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift */; };
DB03A793272A7E5700EE37C5 /* SidebarListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03A792272A7E5700EE37C5 /* SidebarListHeaderView.swift */; }; DB03A793272A7E5700EE37C5 /* SidebarListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03A792272A7E5700EE37C5 /* SidebarListHeaderView.swift */; };
DB03A795272A981400EE37C5 /* ContentSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03A794272A981400EE37C5 /* ContentSplitViewController.swift */; }; DB03A795272A981400EE37C5 /* ContentSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03A794272A981400EE37C5 /* ContentSplitViewController.swift */; };
DB03F7F32689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03F7F22689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift */; };
DB03F7F52689B782007B274C /* ComposeTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03F7F42689B782007B274C /* ComposeTableView.swift */; };
DB0617EB277EF3820030EE79 /* GradientBorderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617EA277EF3820030EE79 /* GradientBorderView.swift */; }; DB0617EB277EF3820030EE79 /* GradientBorderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617EA277EF3820030EE79 /* GradientBorderView.swift */; };
DB0617ED277F02C50030EE79 /* OnboardingNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617EC277F02C50030EE79 /* OnboardingNavigationController.swift */; }; DB0617ED277F02C50030EE79 /* OnboardingNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617EC277F02C50030EE79 /* OnboardingNavigationController.swift */; };
DB0617EF277F12720030EE79 /* NavigationActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617EE277F12720030EE79 /* NavigationActionView.swift */; }; DB0617EF277F12720030EE79 /* NavigationActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617EE277F12720030EE79 /* NavigationActionView.swift */; };
@ -130,8 +118,6 @@
DB0F9D54283EB3C000379AF8 /* ProfileHeaderView+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0F9D53283EB3C000379AF8 /* ProfileHeaderView+ViewModel.swift */; }; DB0F9D54283EB3C000379AF8 /* ProfileHeaderView+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0F9D53283EB3C000379AF8 /* ProfileHeaderView+ViewModel.swift */; };
DB0F9D56283EB46200379AF8 /* ProfileHeaderView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0F9D55283EB46200379AF8 /* ProfileHeaderView+Configuration.swift */; }; DB0F9D56283EB46200379AF8 /* ProfileHeaderView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0F9D55283EB46200379AF8 /* ProfileHeaderView+Configuration.swift */; };
DB0FCB68279507EF006C02E2 /* DataSourceFacade+Meta.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB67279507EF006C02E2 /* DataSourceFacade+Meta.swift */; }; DB0FCB68279507EF006C02E2 /* DataSourceFacade+Meta.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB67279507EF006C02E2 /* DataSourceFacade+Meta.swift */; };
DB0FCB7027951368006C02E2 /* TimelineMiddleLoaderTableViewCell+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB6F27951368006C02E2 /* TimelineMiddleLoaderTableViewCell+ViewModel.swift */; };
DB0FCB7227952986006C02E2 /* NamingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB7127952986006C02E2 /* NamingState.swift */; };
DB0FCB7427956939006C02E2 /* DataSourceFacade+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB7327956939006C02E2 /* DataSourceFacade+Status.swift */; }; DB0FCB7427956939006C02E2 /* DataSourceFacade+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB7327956939006C02E2 /* DataSourceFacade+Status.swift */; };
DB0FCB76279571C5006C02E2 /* ThreadViewController+DataSourceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB75279571C5006C02E2 /* ThreadViewController+DataSourceProvider.swift */; }; DB0FCB76279571C5006C02E2 /* ThreadViewController+DataSourceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB75279571C5006C02E2 /* ThreadViewController+DataSourceProvider.swift */; };
DB0FCB7827957678006C02E2 /* DataSourceProvider+UITableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB7727957678006C02E2 /* DataSourceProvider+UITableViewDelegate.swift */; }; DB0FCB7827957678006C02E2 /* DataSourceProvider+UITableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0FCB7727957678006C02E2 /* DataSourceProvider+UITableViewDelegate.swift */; };
@ -174,14 +160,6 @@
DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */; }; DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */; };
DB2FF510260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */; }; DB2FF510260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */; };
DB336F3F278E668C0031E64B /* StatusTableViewCell+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB336F3E278E668C0031E64B /* StatusTableViewCell+ViewModel.swift */; }; DB336F3F278E668C0031E64B /* StatusTableViewCell+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB336F3E278E668C0031E64B /* StatusTableViewCell+ViewModel.swift */; };
DB336F41278E68480031E64B /* StatusView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB336F40278E68480031E64B /* StatusView+Configuration.swift */; };
DB336F43278EB1690031E64B /* MediaView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB336F42278EB1680031E64B /* MediaView+Configuration.swift */; };
DB36679D268AB91B0027D07F /* ComposeStatusAttachmentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB36679C268AB91B0027D07F /* ComposeStatusAttachmentTableViewCell.swift */; };
DB36679F268ABAF20027D07F /* ComposeStatusAttachmentSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB36679E268ABAF20027D07F /* ComposeStatusAttachmentSection.swift */; };
DB3667A1268ABB2E0027D07F /* ComposeStatusAttachmentItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3667A0268ABB2E0027D07F /* ComposeStatusAttachmentItem.swift */; };
DB3667A4268AE2370027D07F /* ComposeStatusPollTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3667A3268AE2370027D07F /* ComposeStatusPollTableViewCell.swift */; };
DB3667A6268AE2620027D07F /* ComposeStatusPollSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3667A5268AE2620027D07F /* ComposeStatusPollSection.swift */; };
DB3667A8268AE2900027D07F /* ComposeStatusPollItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3667A7268AE2900027D07F /* ComposeStatusPollItem.swift */; };
DB3E6FDD2806A40F00B035AE /* DiscoveryHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3E6FDC2806A40F00B035AE /* DiscoveryHashtagsViewController.swift */; }; DB3E6FDD2806A40F00B035AE /* DiscoveryHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3E6FDC2806A40F00B035AE /* DiscoveryHashtagsViewController.swift */; };
DB3E6FE02806A4ED00B035AE /* DiscoveryHashtagsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3E6FDF2806A4ED00B035AE /* DiscoveryHashtagsViewModel.swift */; }; DB3E6FE02806A4ED00B035AE /* DiscoveryHashtagsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3E6FDF2806A4ED00B035AE /* DiscoveryHashtagsViewModel.swift */; };
DB3E6FE22806A50100B035AE /* DiscoveryHashtagsViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3E6FE12806A50100B035AE /* DiscoveryHashtagsViewModel+Diffable.swift */; }; DB3E6FE22806A50100B035AE /* DiscoveryHashtagsViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3E6FE12806A50100B035AE /* DiscoveryHashtagsViewModel+Diffable.swift */; };
@ -208,8 +186,6 @@
DB427DF825BAA00100D1B89D /* MastodonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DF725BAA00100D1B89D /* MastodonUITests.swift */; }; DB427DF825BAA00100D1B89D /* MastodonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DF725BAA00100D1B89D /* MastodonUITests.swift */; };
DB443CD42694627B00159B29 /* AppearanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB443CD32694627B00159B29 /* AppearanceView.swift */; }; DB443CD42694627B00159B29 /* AppearanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB443CD32694627B00159B29 /* AppearanceView.swift */; };
DB44767B260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */; }; DB44767B260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */; };
DB447681260B3ED600B66B82 /* CustomEmojiPickerSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */; };
DB44768B260B3F2100B66B82 /* CustomEmojiPickerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44768A260B3F2100B66B82 /* CustomEmojiPickerItem.swift */; };
DB447691260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB447690260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift */; }; DB447691260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB447690260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift */; };
DB447697260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB447696260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift */; }; DB447697260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB447696260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift */; };
DB4481B925EE289600BEFB67 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4481B825EE289600BEFB67 /* UITableView.swift */; }; DB4481B925EE289600BEFB67 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4481B825EE289600BEFB67 /* UITableView.swift */; };
@ -282,8 +258,6 @@
DB64BA482851F29300ADF1B7 /* Account+Fetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB64BA472851F29300ADF1B7 /* Account+Fetch.swift */; }; DB64BA482851F29300ADF1B7 /* Account+Fetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB64BA472851F29300ADF1B7 /* Account+Fetch.swift */; };
DB65C63727A2AF6C008BAC2E /* ReportItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB65C63627A2AF6C008BAC2E /* ReportItem.swift */; }; DB65C63727A2AF6C008BAC2E /* ReportItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB65C63627A2AF6C008BAC2E /* ReportItem.swift */; };
DB66728C25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */; }; DB66728C25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */; };
DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729525F9F91600D60309 /* ComposeStatusSection.swift */; };
DB66729C25F9F91F00D60309 /* ComposeStatusItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */; };
DB6746EB278ED8B0008A6B94 /* PollOptionView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6746EA278ED8B0008A6B94 /* PollOptionView+Configuration.swift */; }; DB6746EB278ED8B0008A6B94 /* PollOptionView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6746EA278ED8B0008A6B94 /* PollOptionView+Configuration.swift */; };
DB6746ED278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6746EC278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift */; }; DB6746ED278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6746EC278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift */; };
DB6746F0278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6746EF278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift */; }; DB6746F0278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6746EF278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift */; };
@ -311,14 +285,10 @@
DB6B74FE272FF59000C70B6E /* UserItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74FD272FF59000C70B6E /* UserItem.swift */; }; DB6B74FE272FF59000C70B6E /* UserItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74FD272FF59000C70B6E /* UserItem.swift */; };
DB6B7500272FF73800C70B6E /* UserTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74FF272FF73800C70B6E /* UserTableViewCell.swift */; }; DB6B7500272FF73800C70B6E /* UserTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74FF272FF73800C70B6E /* UserTableViewCell.swift */; };
DB6B750427300B4000C70B6E /* TimelineFooterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */; }; DB6B750427300B4000C70B6E /* TimelineFooterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */; };
DB6C8C0F25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6C8C0E25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift */; };
DB6D1B44263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B43263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift */; };
DB6D9F3526351B7A008423CD /* NotificationService+Decrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F3426351B7A008423CD /* NotificationService+Decrypt.swift */; }; DB6D9F3526351B7A008423CD /* NotificationService+Decrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F3426351B7A008423CD /* NotificationService+Decrypt.swift */; };
DB6D9F7D26358ED4008423CD /* SettingsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F7C26358ED4008423CD /* SettingsSection.swift */; }; DB6D9F7D26358ED4008423CD /* SettingsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F7C26358ED4008423CD /* SettingsSection.swift */; };
DB6D9F8426358EEC008423CD /* SettingsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F8326358EEC008423CD /* SettingsItem.swift */; }; DB6D9F8426358EEC008423CD /* SettingsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F8326358EEC008423CD /* SettingsItem.swift */; };
DB6D9F9726367249008423CD /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F9626367249008423CD /* SettingsViewController.swift */; }; DB6D9F9726367249008423CD /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F9626367249008423CD /* SettingsViewController.swift */; };
DB6F5E35264E78E7009108F4 /* AutoCompleteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6F5E34264E78E7009108F4 /* AutoCompleteViewController.swift */; };
DB6F5E38264E994A009108F4 /* AutoCompleteTopChevronView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6F5E37264E994A009108F4 /* AutoCompleteTopChevronView.swift */; };
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; }; DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; };
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; }; DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; };
DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */; }; DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */; };
@ -353,7 +323,6 @@
DB938F0926240F3C00E5B6C1 /* RemoteThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB938F0826240F3C00E5B6C1 /* RemoteThreadViewModel.swift */; }; DB938F0926240F3C00E5B6C1 /* RemoteThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB938F0826240F3C00E5B6C1 /* RemoteThreadViewModel.swift */; };
DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB938F0E2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift */; }; DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB938F0E2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift */; };
DB938F1F2624382F00E5B6C1 /* ThreadViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB938F1E2624382F00E5B6C1 /* ThreadViewModel+Diffable.swift */; }; DB938F1F2624382F00E5B6C1 /* ThreadViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB938F1E2624382F00E5B6C1 /* ThreadViewModel+Diffable.swift */; };
DB938F3326243D6200E5B6C1 /* TimelineTopLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB938F3226243D6200E5B6C1 /* TimelineTopLoaderTableViewCell.swift */; };
DB98EB4727B0DFAA0082E365 /* ReportStatusViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4627B0DFAA0082E365 /* ReportStatusViewModel+State.swift */; }; DB98EB4727B0DFAA0082E365 /* ReportStatusViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4627B0DFAA0082E365 /* ReportStatusViewModel+State.swift */; };
DB98EB4927B0F0CD0082E365 /* ReportStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4827B0F0CD0082E365 /* ReportStatusTableViewCell.swift */; }; DB98EB4927B0F0CD0082E365 /* ReportStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4827B0F0CD0082E365 /* ReportStatusTableViewCell.swift */; };
DB98EB4C27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4B27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift */; }; DB98EB4C27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4B27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift */; };
@ -387,7 +356,6 @@
DBA94434265CBB5300C537E1 /* ProfileFieldSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA94433265CBB5300C537E1 /* ProfileFieldSection.swift */; }; DBA94434265CBB5300C537E1 /* ProfileFieldSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA94433265CBB5300C537E1 /* ProfileFieldSection.swift */; };
DBA94436265CBB7400C537E1 /* ProfileFieldItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA94435265CBB7400C537E1 /* ProfileFieldItem.swift */; }; DBA94436265CBB7400C537E1 /* ProfileFieldItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA94435265CBB7400C537E1 /* ProfileFieldItem.swift */; };
DBA9443E265CFA6400C537E1 /* ProfileFieldCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA9443D265CFA6400C537E1 /* ProfileFieldCollectionViewCell.swift */; }; DBA9443E265CFA6400C537E1 /* ProfileFieldCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA9443D265CFA6400C537E1 /* ProfileFieldCollectionViewCell.swift */; };
DBA94440265D137600C537E1 /* Mastodon+Entity+Field.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA9443F265D137600C537E1 /* Mastodon+Entity+Field.swift */; };
DBABE3EC25ECAC4B00879EE5 /* WelcomeIllustrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */; }; DBABE3EC25ECAC4B00879EE5 /* WelcomeIllustrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */; };
DBAE3FAF26172FC0004B8251 /* RemoteProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBAE3FAE26172FC0004B8251 /* RemoteProfileViewModel.swift */; }; DBAE3FAF26172FC0004B8251 /* RemoteProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBAE3FAE26172FC0004B8251 /* RemoteProfileViewModel.swift */; };
DBB3BA2A26A81C020004F2D4 /* FLAnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB3BA2926A81C020004F2D4 /* FLAnimatedImageView.swift */; }; DBB3BA2A26A81C020004F2D4 /* FLAnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB3BA2926A81C020004F2D4 /* FLAnimatedImageView.swift */; };
@ -408,20 +376,12 @@
DBB8AB4626AECDE200F6D281 /* SendPostIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB8AB4526AECDE200F6D281 /* SendPostIntentHandler.swift */; }; DBB8AB4626AECDE200F6D281 /* SendPostIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB8AB4526AECDE200F6D281 /* SendPostIntentHandler.swift */; };
DBB8AB4F26AED13F00F6D281 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DB427DDE25BAA00100D1B89D /* Assets.xcassets */; }; DBB8AB4F26AED13F00F6D281 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DB427DDE25BAA00100D1B89D /* Assets.xcassets */; };
DBB9759C262462E1004620BD /* ThreadMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB9759B262462E1004620BD /* ThreadMetaView.swift */; }; DBB9759C262462E1004620BD /* ThreadMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB9759B262462E1004620BD /* ThreadMetaView.swift */; };
DBBC24A826A52F9000398BB9 /* ComposeToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBC24A726A52F9000398BB9 /* ComposeToolbarView.swift */; };
DBBC24AC26A53D9300398BB9 /* ComposeStatusContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBC24AB26A53D9300398BB9 /* ComposeStatusContentTableViewCell.swift */; };
DBBC24DC26A54BCB00398BB9 /* MastodonRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBC24D626A54BCB00398BB9 /* MastodonRegex.swift */; }; DBBC24DC26A54BCB00398BB9 /* MastodonRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBC24D626A54BCB00398BB9 /* MastodonRegex.swift */; };
DBBE1B4525F3474B0081417A /* MastodonPickServerAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBE1B4425F3474B0081417A /* MastodonPickServerAppearance.swift */; }; DBBE1B4525F3474B0081417A /* MastodonPickServerAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBE1B4425F3474B0081417A /* MastodonPickServerAppearance.swift */; };
DBBF1DBF2652401B00E5B703 /* AutoCompleteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBF1DBE2652401B00E5B703 /* AutoCompleteViewModel.swift */; }; DBC6461526A170AB00B0E31B /* ComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC6461426A170AB00B0E31B /* ComposeViewController.swift */; };
DBBF1DC226524D2900E5B703 /* AutoCompleteTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBF1DC126524D2900E5B703 /* AutoCompleteTableViewCell.swift */; };
DBBF1DC5265251C300E5B703 /* AutoCompleteViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBF1DC4265251C300E5B703 /* AutoCompleteViewModel+Diffable.swift */; };
DBBF1DC7265251D400E5B703 /* AutoCompleteViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBF1DC6265251D400E5B703 /* AutoCompleteViewModel+State.swift */; };
DBBF1DC92652538500E5B703 /* AutoCompleteSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBF1DC82652538500E5B703 /* AutoCompleteSection.swift */; };
DBBF1DCB2652539E00E5B703 /* AutoCompleteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBF1DCA2652539E00E5B703 /* AutoCompleteItem.swift */; };
DBC6461526A170AB00B0E31B /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC6461426A170AB00B0E31B /* ShareViewController.swift */; };
DBC6461826A170AB00B0E31B /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DBC6461626A170AB00B0E31B /* MainInterface.storyboard */; }; DBC6461826A170AB00B0E31B /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DBC6461626A170AB00B0E31B /* MainInterface.storyboard */; };
DBC6461C26A170AB00B0E31B /* ShareActionExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; DBC6461C26A170AB00B0E31B /* ShareActionExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
DBC6462326A1712000B0E31B /* ShareViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC6462226A1712000B0E31B /* ShareViewModel.swift */; }; DBC6462326A1712000B0E31B /* ComposeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC6462226A1712000B0E31B /* ComposeViewModel.swift */; };
DBC6462826A1736300B0E31B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DB427DDE25BAA00100D1B89D /* Assets.xcassets */; }; DBC6462826A1736300B0E31B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DB427DDE25BAA00100D1B89D /* Assets.xcassets */; };
DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */; }; DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */; };
DBCA0EBC282BB38A0029E2B0 /* PageboyNavigateable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCA0EBB282BB38A0029E2B0 /* PageboyNavigateable.swift */; }; DBCA0EBC282BB38A0029E2B0 /* PageboyNavigateable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCA0EBB282BB38A0029E2B0 /* PageboyNavigateable.swift */; };
@ -474,14 +434,6 @@
DBFEEC99279BDCDE004F81DD /* ProfileAboutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC98279BDCDE004F81DD /* ProfileAboutViewModel.swift */; }; DBFEEC99279BDCDE004F81DD /* ProfileAboutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC98279BDCDE004F81DD /* ProfileAboutViewModel.swift */; };
DBFEEC9B279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC9A279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift */; }; DBFEEC9B279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC9A279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift */; };
DBFEEC9D279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC9C279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift */; }; DBFEEC9D279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC9C279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift */; };
DBFEF05B26A57715006D7ED1 /* ComposeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEF05726A576EE006D7ED1 /* ComposeViewModel.swift */; };
DBFEF05C26A57715006D7ED1 /* StatusEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEF05526A576EE006D7ED1 /* StatusEditorView.swift */; };
DBFEF05D26A57715006D7ED1 /* ContentWarningEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEF05826A576EE006D7ED1 /* ContentWarningEditorView.swift */; };
DBFEF05E26A57715006D7ED1 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEF05626A576EE006D7ED1 /* ComposeView.swift */; };
DBFEF05F26A57715006D7ED1 /* StatusAuthorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEF05926A576EE006D7ED1 /* StatusAuthorView.swift */; };
DBFEF06026A57715006D7ED1 /* StatusAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEF05A26A576EE006D7ED1 /* StatusAttachmentView.swift */; };
DBFEF06326A577F2006D7ED1 /* StatusAttachmentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEF06226A577F2006D7ED1 /* StatusAttachmentViewModel.swift */; };
DBFEF06D26A67FB7006D7ED1 /* StatusAttachmentViewModel+UploadState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEF06C26A67FB7006D7ED1 /* StatusAttachmentViewModel+UploadState.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -572,7 +524,6 @@
2D206B8525F5FB0900143C56 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = "<group>"; }; 2D206B8525F5FB0900143C56 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = "<group>"; };
2D206B9125F60EA700143C56 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; }; 2D206B9125F60EA700143C56 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; };
2D24E1222626ED9D00A59D4F /* UIView+Gesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Gesture.swift"; sourceTree = "<group>"; }; 2D24E1222626ED9D00A59D4F /* UIView+Gesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Gesture.swift"; sourceTree = "<group>"; };
2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMiddleLoaderTableViewCell.swift; sourceTree = "<group>"; };
2D35237926256D920031AF25 /* NotificationSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSection.swift; sourceTree = "<group>"; }; 2D35237926256D920031AF25 /* NotificationSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSection.swift; sourceTree = "<group>"; };
2D364F7125E66D7500204FDC /* MastodonResendEmailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewController.swift; sourceTree = "<group>"; }; 2D364F7125E66D7500204FDC /* MastodonResendEmailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewController.swift; sourceTree = "<group>"; };
2D364F7725E66D8300204FDC /* MastodonResendEmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewModel.swift; sourceTree = "<group>"; }; 2D364F7725E66D8300204FDC /* MastodonResendEmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewModel.swift; sourceTree = "<group>"; };
@ -588,12 +539,10 @@
2D571B2E26004EC000540450 /* NavigationBarProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarProgressView.swift; sourceTree = "<group>"; }; 2D571B2E26004EC000540450 /* NavigationBarProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarProgressView.swift; sourceTree = "<group>"; };
2D59819A25E4A581000FB903 /* MastodonConfirmEmailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonConfirmEmailViewController.swift; sourceTree = "<group>"; }; 2D59819A25E4A581000FB903 /* MastodonConfirmEmailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonConfirmEmailViewController.swift; sourceTree = "<group>"; };
2D5981A025E4A593000FB903 /* MastodonConfirmEmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonConfirmEmailViewModel.swift; sourceTree = "<group>"; }; 2D5981A025E4A593000FB903 /* MastodonConfirmEmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonConfirmEmailViewModel.swift; sourceTree = "<group>"; };
2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlContainableScrollViews.swift; sourceTree = "<group>"; };
2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HomeTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; }; 2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HomeTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewContainer.swift; sourceTree = "<group>"; }; 2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewContainer.swift; sourceTree = "<group>"; };
2D5A3D6125CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HomeTimelineViewController+DebugAction.swift"; sourceTree = "<group>"; }; 2D5A3D6125CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HomeTimelineViewController+DebugAction.swift"; sourceTree = "<group>"; };
2D607AD726242FC500B70763 /* NotificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewModel.swift; sourceTree = "<group>"; }; 2D607AD726242FC500B70763 /* NotificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewModel.swift; sourceTree = "<group>"; };
2D650FAA25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Error+Detail.swift"; sourceTree = "<group>"; };
2D694A7325F9EB4E0038ADDC /* ContentWarningOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningOverlayView.swift; sourceTree = "<group>"; }; 2D694A7325F9EB4E0038ADDC /* ContentWarningOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningOverlayView.swift; sourceTree = "<group>"; };
2D6DE3FF26141DF600A63F6A /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = "<group>"; }; 2D6DE3FF26141DF600A63F6A /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = "<group>"; };
2D76319E25C1521200929FB9 /* StatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusSection.swift; sourceTree = "<group>"; }; 2D76319E25C1521200929FB9 /* StatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusSection.swift; sourceTree = "<group>"; };
@ -606,14 +555,10 @@
2D84350425FF858100EECE90 /* UIScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = "<group>"; }; 2D84350425FF858100EECE90 /* UIScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = "<group>"; };
2D939AB425EDD8A90076FA61 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; }; 2D939AB425EDD8A90076FA61 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
2D939AE725EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonRegisterViewController+Avatar.swift"; sourceTree = "<group>"; }; 2D939AE725EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonRegisterViewController+Avatar.swift"; sourceTree = "<group>"; };
2DA504682601ADE7008F4E6C /* SawToothView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SawToothView.swift; sourceTree = "<group>"; };
2DA7D04325CA52B200804E11 /* TimelineLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLoaderTableViewCell.swift; sourceTree = "<group>"; };
2DA7D04925CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineBottomLoaderTableViewCell.swift; sourceTree = "<group>"; };
2DA7D05025CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = "<group>"; }; 2DA7D05025CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = "<group>"; };
2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewController.swift; sourceTree = "<group>"; }; 2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewController.swift; sourceTree = "<group>"; };
2DAC9E3D262FC2400062E1A6 /* SuggestionAccountViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewModel.swift; sourceTree = "<group>"; }; 2DAC9E3D262FC2400062E1A6 /* SuggestionAccountViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewModel.swift; sourceTree = "<group>"; };
2DAC9E45262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountTableViewCell.swift; sourceTree = "<group>"; }; 2DAC9E45262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountTableViewCell.swift; sourceTree = "<group>"; };
2DB72C8B262D764300CE6173 /* Mastodon+Entity+Notification+Type.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Notification+Type.swift"; sourceTree = "<group>"; };
2DCB73FC2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchRecommendCollectionHeader.swift; sourceTree = "<group>"; }; 2DCB73FC2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchRecommendCollectionHeader.swift; sourceTree = "<group>"; };
2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendAccountSection.swift; sourceTree = "<group>"; }; 2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendAccountSection.swift; sourceTree = "<group>"; };
2DF123A625C3B0210020F248 /* ActiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveLabel.swift; sourceTree = "<group>"; }; 2DF123A625C3B0210020F248 /* ActiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveLabel.swift; sourceTree = "<group>"; };
@ -638,9 +583,6 @@
5D0393952612D266007FE196 /* WebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewModel.swift; sourceTree = "<group>"; }; 5D0393952612D266007FE196 /* WebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewModel.swift; sourceTree = "<group>"; };
5DA732CB2629CEF500A92342 /* UIView+Remove.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Remove.swift"; sourceTree = "<group>"; }; 5DA732CB2629CEF500A92342 /* UIView+Remove.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Remove.swift"; sourceTree = "<group>"; };
5DA82A9B4ABDAFA3AB9A49C7 /* Pods-MastodonTests.asdk.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.asdk.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.asdk.xcconfig"; sourceTree = "<group>"; }; 5DA82A9B4ABDAFA3AB9A49C7 /* Pods-MastodonTests.asdk.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.asdk.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.asdk.xcconfig"; sourceTree = "<group>"; };
5DDDF1922617442700311060 /* Mastodon+Entity+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Account.swift"; sourceTree = "<group>"; };
5DDDF1982617447F00311060 /* Mastodon+Entity+Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Tag.swift"; sourceTree = "<group>"; };
5DDDF1A82617489F00311060 /* Mastodon+Entity+History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+History.swift"; sourceTree = "<group>"; };
5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayer.swift; sourceTree = "<group>"; }; 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayer.swift; sourceTree = "<group>"; };
6130CBE4B26E3C976ACC1688 /* Pods-ShareActionExtension.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareActionExtension.asdk - debug.xcconfig"; path = "Target Support Files/Pods-ShareActionExtension/Pods-ShareActionExtension.asdk - debug.xcconfig"; sourceTree = "<group>"; }; 6130CBE4B26E3C976ACC1688 /* Pods-ShareActionExtension.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareActionExtension.asdk - debug.xcconfig"; path = "Target Support Files/Pods-ShareActionExtension/Pods-ShareActionExtension.asdk - debug.xcconfig"; sourceTree = "<group>"; };
6213AF5928939C8400BCADB6 /* BookmarkViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkViewModel.swift; sourceTree = "<group>"; }; 6213AF5928939C8400BCADB6 /* BookmarkViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkViewModel.swift; sourceTree = "<group>"; };
@ -684,8 +626,6 @@
DB02CDBE2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveUserInterfaceStyleBarButtonItem.swift; sourceTree = "<group>"; }; DB02CDBE2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveUserInterfaceStyleBarButtonItem.swift; sourceTree = "<group>"; };
DB03A792272A7E5700EE37C5 /* SidebarListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarListHeaderView.swift; sourceTree = "<group>"; }; DB03A792272A7E5700EE37C5 /* SidebarListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarListHeaderView.swift; sourceTree = "<group>"; };
DB03A794272A981400EE37C5 /* ContentSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentSplitViewController.swift; sourceTree = "<group>"; }; DB03A794272A981400EE37C5 /* ContentSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentSplitViewController.swift; sourceTree = "<group>"; };
DB03F7F22689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeRepliedToStatusContentTableViewCell.swift; sourceTree = "<group>"; };
DB03F7F42689B782007B274C /* ComposeTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeTableView.swift; sourceTree = "<group>"; };
DB0617EA277EF3820030EE79 /* GradientBorderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientBorderView.swift; sourceTree = "<group>"; }; DB0617EA277EF3820030EE79 /* GradientBorderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientBorderView.swift; sourceTree = "<group>"; };
DB0617EC277F02C50030EE79 /* OnboardingNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingNavigationController.swift; sourceTree = "<group>"; }; DB0617EC277F02C50030EE79 /* OnboardingNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingNavigationController.swift; sourceTree = "<group>"; };
DB0617EE277F12720030EE79 /* NavigationActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationActionView.swift; sourceTree = "<group>"; }; DB0617EE277F12720030EE79 /* NavigationActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationActionView.swift; sourceTree = "<group>"; };
@ -707,8 +647,6 @@
DB0F9D53283EB3C000379AF8 /* ProfileHeaderView+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileHeaderView+ViewModel.swift"; sourceTree = "<group>"; }; DB0F9D53283EB3C000379AF8 /* ProfileHeaderView+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileHeaderView+ViewModel.swift"; sourceTree = "<group>"; };
DB0F9D55283EB46200379AF8 /* ProfileHeaderView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileHeaderView+Configuration.swift"; sourceTree = "<group>"; }; DB0F9D55283EB46200379AF8 /* ProfileHeaderView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileHeaderView+Configuration.swift"; sourceTree = "<group>"; };
DB0FCB67279507EF006C02E2 /* DataSourceFacade+Meta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Meta.swift"; sourceTree = "<group>"; }; DB0FCB67279507EF006C02E2 /* DataSourceFacade+Meta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Meta.swift"; sourceTree = "<group>"; };
DB0FCB6F27951368006C02E2 /* TimelineMiddleLoaderTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimelineMiddleLoaderTableViewCell+ViewModel.swift"; sourceTree = "<group>"; };
DB0FCB7127952986006C02E2 /* NamingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NamingState.swift; sourceTree = "<group>"; };
DB0FCB7327956939006C02E2 /* DataSourceFacade+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Status.swift"; sourceTree = "<group>"; }; DB0FCB7327956939006C02E2 /* DataSourceFacade+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Status.swift"; sourceTree = "<group>"; };
DB0FCB75279571C5006C02E2 /* ThreadViewController+DataSourceProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThreadViewController+DataSourceProvider.swift"; sourceTree = "<group>"; }; DB0FCB75279571C5006C02E2 /* ThreadViewController+DataSourceProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThreadViewController+DataSourceProvider.swift"; sourceTree = "<group>"; };
DB0FCB7727957678006C02E2 /* DataSourceProvider+UITableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceProvider+UITableViewDelegate.swift"; sourceTree = "<group>"; }; DB0FCB7727957678006C02E2 /* DataSourceProvider+UITableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceProvider+UITableViewDelegate.swift"; sourceTree = "<group>"; };
@ -748,14 +686,6 @@
DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; }; DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollExpiresOptionCollectionViewCell.swift; sourceTree = "<group>"; }; DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollExpiresOptionCollectionViewCell.swift; sourceTree = "<group>"; };
DB336F3E278E668C0031E64B /* StatusTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusTableViewCell+ViewModel.swift"; sourceTree = "<group>"; }; DB336F3E278E668C0031E64B /* StatusTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusTableViewCell+ViewModel.swift"; sourceTree = "<group>"; };
DB336F40278E68480031E64B /* StatusView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusView+Configuration.swift"; sourceTree = "<group>"; };
DB336F42278EB1680031E64B /* MediaView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MediaView+Configuration.swift"; sourceTree = "<group>"; };
DB36679C268AB91B0027D07F /* ComposeStatusAttachmentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusAttachmentTableViewCell.swift; sourceTree = "<group>"; };
DB36679E268ABAF20027D07F /* ComposeStatusAttachmentSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusAttachmentSection.swift; sourceTree = "<group>"; };
DB3667A0268ABB2E0027D07F /* ComposeStatusAttachmentItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusAttachmentItem.swift; sourceTree = "<group>"; };
DB3667A3268AE2370027D07F /* ComposeStatusPollTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollTableViewCell.swift; sourceTree = "<group>"; };
DB3667A5268AE2620027D07F /* ComposeStatusPollSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollSection.swift; sourceTree = "<group>"; };
DB3667A7268AE2900027D07F /* ComposeStatusPollItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollItem.swift; sourceTree = "<group>"; };
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; }; DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
DB3E6FDC2806A40F00B035AE /* DiscoveryHashtagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryHashtagsViewController.swift; sourceTree = "<group>"; }; DB3E6FDC2806A40F00B035AE /* DiscoveryHashtagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryHashtagsViewController.swift; sourceTree = "<group>"; };
DB3E6FDF2806A4ED00B035AE /* DiscoveryHashtagsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryHashtagsViewModel.swift; sourceTree = "<group>"; }; DB3E6FDF2806A4ED00B035AE /* DiscoveryHashtagsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryHashtagsViewModel.swift; sourceTree = "<group>"; };
@ -789,8 +719,6 @@
DB427DF925BAA00100D1B89D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; DB427DF925BAA00100D1B89D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DB443CD32694627B00159B29 /* AppearanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceView.swift; sourceTree = "<group>"; }; DB443CD32694627B00159B29 /* AppearanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceView.swift; sourceTree = "<group>"; };
DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerInputView.swift; sourceTree = "<group>"; }; DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerInputView.swift; sourceTree = "<group>"; };
DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerSection.swift; sourceTree = "<group>"; };
DB44768A260B3F2100B66B82 /* CustomEmojiPickerItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerItem.swift; sourceTree = "<group>"; };
DB447690260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerItemCollectionViewCell.swift; sourceTree = "<group>"; }; DB447690260B406600B66B82 /* CustomEmojiPickerItemCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerItemCollectionViewCell.swift; sourceTree = "<group>"; };
DB447696260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerHeaderCollectionReusableView.swift; sourceTree = "<group>"; }; DB447696260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerHeaderCollectionReusableView.swift; sourceTree = "<group>"; };
DB4481B825EE289600BEFB67 /* UITableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableView.swift; sourceTree = "<group>"; }; DB4481B825EE289600BEFB67 /* UITableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableView.swift; sourceTree = "<group>"; };
@ -893,8 +821,6 @@
DB64BA472851F29300ADF1B7 /* Account+Fetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account+Fetch.swift"; sourceTree = "<group>"; }; DB64BA472851F29300ADF1B7 /* Account+Fetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account+Fetch.swift"; sourceTree = "<group>"; };
DB65C63627A2AF6C008BAC2E /* ReportItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportItem.swift; sourceTree = "<group>"; }; DB65C63627A2AF6C008BAC2E /* ReportItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportItem.swift; sourceTree = "<group>"; };
DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ComposeViewModel+DataSource.swift"; sourceTree = "<group>"; }; DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ComposeViewModel+DataSource.swift"; sourceTree = "<group>"; };
DB66729525F9F91600D60309 /* ComposeStatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusSection.swift; sourceTree = "<group>"; };
DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusItem.swift; sourceTree = "<group>"; };
DB6746EA278ED8B0008A6B94 /* PollOptionView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PollOptionView+Configuration.swift"; sourceTree = "<group>"; }; DB6746EA278ED8B0008A6B94 /* PollOptionView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PollOptionView+Configuration.swift"; sourceTree = "<group>"; };
DB6746EC278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoGenerateProtocolRelayDelegate.swift; sourceTree = "<group>"; }; DB6746EC278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoGenerateProtocolRelayDelegate.swift; sourceTree = "<group>"; };
DB6746EF278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoGenerateProtocolDelegate.swift; sourceTree = "<group>"; }; DB6746EF278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoGenerateProtocolDelegate.swift; sourceTree = "<group>"; };
@ -922,14 +848,10 @@
DB6B74FD272FF59000C70B6E /* UserItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserItem.swift; sourceTree = "<group>"; }; DB6B74FD272FF59000C70B6E /* UserItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserItem.swift; sourceTree = "<group>"; };
DB6B74FF272FF73800C70B6E /* UserTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTableViewCell.swift; sourceTree = "<group>"; }; DB6B74FF272FF73800C70B6E /* UserTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTableViewCell.swift; sourceTree = "<group>"; };
DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineFooterTableViewCell.swift; sourceTree = "<group>"; }; DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineFooterTableViewCell.swift; sourceTree = "<group>"; };
DB6C8C0E25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Error.swift"; sourceTree = "<group>"; };
DB6D1B43263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+API+Subscriptions+Policy.swift"; sourceTree = "<group>"; };
DB6D9F3426351B7A008423CD /* NotificationService+Decrypt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationService+Decrypt.swift"; sourceTree = "<group>"; }; DB6D9F3426351B7A008423CD /* NotificationService+Decrypt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationService+Decrypt.swift"; sourceTree = "<group>"; };
DB6D9F7C26358ED4008423CD /* SettingsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSection.swift; sourceTree = "<group>"; }; DB6D9F7C26358ED4008423CD /* SettingsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSection.swift; sourceTree = "<group>"; };
DB6D9F8326358EEC008423CD /* SettingsItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsItem.swift; sourceTree = "<group>"; }; DB6D9F8326358EEC008423CD /* SettingsItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsItem.swift; sourceTree = "<group>"; };
DB6D9F9626367249008423CD /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; }; DB6D9F9626367249008423CD /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
DB6F5E34264E78E7009108F4 /* AutoCompleteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteViewController.swift; sourceTree = "<group>"; };
DB6F5E37264E994A009108F4 /* AutoCompleteTopChevronView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteTopChevronView.swift; sourceTree = "<group>"; };
DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = "<group>"; }; DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = "<group>"; };
DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = "<group>"; }; DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = "<group>"; };
DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListBatchFetchViewModel.swift; sourceTree = "<group>"; }; DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListBatchFetchViewModel.swift; sourceTree = "<group>"; };
@ -976,7 +898,6 @@
DB938F0826240F3C00E5B6C1 /* RemoteThreadViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteThreadViewModel.swift; sourceTree = "<group>"; }; DB938F0826240F3C00E5B6C1 /* RemoteThreadViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteThreadViewModel.swift; sourceTree = "<group>"; };
DB938F0E2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThreadViewModel+LoadThreadState.swift"; sourceTree = "<group>"; }; DB938F0E2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThreadViewModel+LoadThreadState.swift"; sourceTree = "<group>"; };
DB938F1E2624382F00E5B6C1 /* ThreadViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThreadViewModel+Diffable.swift"; sourceTree = "<group>"; }; DB938F1E2624382F00E5B6C1 /* ThreadViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThreadViewModel+Diffable.swift"; sourceTree = "<group>"; };
DB938F3226243D6200E5B6C1 /* TimelineTopLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTopLoaderTableViewCell.swift; sourceTree = "<group>"; };
DB98EB4627B0DFAA0082E365 /* ReportStatusViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReportStatusViewModel+State.swift"; sourceTree = "<group>"; }; DB98EB4627B0DFAA0082E365 /* ReportStatusViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReportStatusViewModel+State.swift"; sourceTree = "<group>"; };
DB98EB4827B0F0CD0082E365 /* ReportStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportStatusTableViewCell.swift; sourceTree = "<group>"; }; DB98EB4827B0F0CD0082E365 /* ReportStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportStatusTableViewCell.swift; sourceTree = "<group>"; };
DB98EB4B27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReportStatusTableViewCell+ViewModel.swift"; sourceTree = "<group>"; }; DB98EB4B27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReportStatusTableViewCell+ViewModel.swift"; sourceTree = "<group>"; };
@ -1022,7 +943,6 @@
DBA94433265CBB5300C537E1 /* ProfileFieldSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldSection.swift; sourceTree = "<group>"; }; DBA94433265CBB5300C537E1 /* ProfileFieldSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldSection.swift; sourceTree = "<group>"; };
DBA94435265CBB7400C537E1 /* ProfileFieldItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldItem.swift; sourceTree = "<group>"; }; DBA94435265CBB7400C537E1 /* ProfileFieldItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldItem.swift; sourceTree = "<group>"; };
DBA9443D265CFA6400C537E1 /* ProfileFieldCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldCollectionViewCell.swift; sourceTree = "<group>"; }; DBA9443D265CFA6400C537E1 /* ProfileFieldCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldCollectionViewCell.swift; sourceTree = "<group>"; };
DBA9443F265D137600C537E1 /* Mastodon+Entity+Field.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Field.swift"; sourceTree = "<group>"; };
DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeIllustrationView.swift; sourceTree = "<group>"; }; DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeIllustrationView.swift; sourceTree = "<group>"; };
DBAE3FAE26172FC0004B8251 /* RemoteProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteProfileViewModel.swift; sourceTree = "<group>"; }; DBAE3FAE26172FC0004B8251 /* RemoteProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteProfileViewModel.swift; sourceTree = "<group>"; };
DBB3BA2926A81C020004F2D4 /* FLAnimatedImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FLAnimatedImageView.swift; sourceTree = "<group>"; }; DBB3BA2926A81C020004F2D4 /* FLAnimatedImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FLAnimatedImageView.swift; sourceTree = "<group>"; };
@ -1042,20 +962,13 @@
DBB8AB4526AECDE200F6D281 /* SendPostIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendPostIntentHandler.swift; sourceTree = "<group>"; }; DBB8AB4526AECDE200F6D281 /* SendPostIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendPostIntentHandler.swift; sourceTree = "<group>"; };
DBB9759B262462E1004620BD /* ThreadMetaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadMetaView.swift; sourceTree = "<group>"; }; DBB9759B262462E1004620BD /* ThreadMetaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadMetaView.swift; sourceTree = "<group>"; };
DBBC24A726A52F9000398BB9 /* ComposeToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeToolbarView.swift; sourceTree = "<group>"; }; DBBC24A726A52F9000398BB9 /* ComposeToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeToolbarView.swift; sourceTree = "<group>"; };
DBBC24AB26A53D9300398BB9 /* ComposeStatusContentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusContentTableViewCell.swift; sourceTree = "<group>"; };
DBBC24D626A54BCB00398BB9 /* MastodonRegex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MastodonRegex.swift; sourceTree = "<group>"; }; DBBC24D626A54BCB00398BB9 /* MastodonRegex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MastodonRegex.swift; sourceTree = "<group>"; };
DBBE1B4425F3474B0081417A /* MastodonPickServerAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerAppearance.swift; sourceTree = "<group>"; }; DBBE1B4425F3474B0081417A /* MastodonPickServerAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerAppearance.swift; sourceTree = "<group>"; };
DBBF1DBE2652401B00E5B703 /* AutoCompleteViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteViewModel.swift; sourceTree = "<group>"; };
DBBF1DC126524D2900E5B703 /* AutoCompleteTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteTableViewCell.swift; sourceTree = "<group>"; };
DBBF1DC4265251C300E5B703 /* AutoCompleteViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AutoCompleteViewModel+Diffable.swift"; sourceTree = "<group>"; };
DBBF1DC6265251D400E5B703 /* AutoCompleteViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AutoCompleteViewModel+State.swift"; sourceTree = "<group>"; };
DBBF1DC82652538500E5B703 /* AutoCompleteSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteSection.swift; sourceTree = "<group>"; };
DBBF1DCA2652539E00E5B703 /* AutoCompleteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteItem.swift; sourceTree = "<group>"; };
DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareActionExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareActionExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
DBC6461426A170AB00B0E31B /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; }; DBC6461426A170AB00B0E31B /* ComposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewController.swift; sourceTree = "<group>"; };
DBC6461726A170AB00B0E31B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; }; DBC6461726A170AB00B0E31B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
DBC6461926A170AB00B0E31B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; DBC6461926A170AB00B0E31B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DBC6462226A1712000B0E31B /* ShareViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareViewModel.swift; sourceTree = "<group>"; }; DBC6462226A1712000B0E31B /* ComposeViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComposeViewModel.swift; sourceTree = "<group>"; };
DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentWarningEditorView.swift; sourceTree = "<group>"; }; DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentWarningEditorView.swift; sourceTree = "<group>"; };
DBC9E3A3282E13BB0063A4D9 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Intents.strings; sourceTree = "<group>"; }; DBC9E3A3282E13BB0063A4D9 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Intents.strings; sourceTree = "<group>"; };
DBC9E3A4282E13BB0063A4D9 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/InfoPlist.strings; sourceTree = "<group>"; }; DBC9E3A4282E13BB0063A4D9 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@ -1320,8 +1233,6 @@
2D152A8A25C295B8009AA50C /* Content */ = { 2D152A8A25C295B8009AA50C /* Content */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DB336F40278E68480031E64B /* StatusView+Configuration.swift */,
DB336F42278EB1680031E64B /* MediaView+Configuration.swift */,
DB6746EA278ED8B0008A6B94 /* PollOptionView+Configuration.swift */, DB6746EA278ED8B0008A6B94 /* PollOptionView+Configuration.swift */,
DB0FCB992797F7AD006C02E2 /* UserView+Configuration.swift */, DB0FCB992797F7AD006C02E2 /* UserView+Configuration.swift */,
DB63F776279A9A2A00455B82 /* NotificationView+Configuration.swift */, DB63F776279A9A2A00455B82 /* NotificationView+Configuration.swift */,
@ -1406,7 +1317,6 @@
2D5A3D0125CF8640002347D6 /* Vender */ = { 2D5A3D0125CF8640002347D6 /* Vender */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */,
DB6180EC26391C6C0018D199 /* TransitioningMath.swift */, DB6180EC26391C6C0018D199 /* TransitioningMath.swift */,
DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */, DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */,
DBF156E32702DB3F00EC00B7 /* HandleTapAction.swift */, DBF156E32702DB3F00EC00B7 /* HandleTapAction.swift */,
@ -1420,7 +1330,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DB697DD7278F4C34004EF2F7 /* Provider */, DB697DD7278F4C34004EF2F7 /* Provider */,
DB0FCB7127952986006C02E2 /* NamingState.swift */,
2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */, 2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */,
DB4AA6B227BA34B6009EC082 /* CellFrameCacheContainer.swift */, DB4AA6B227BA34B6009EC082 /* CellFrameCacheContainer.swift */,
2D38F20725CD491300561493 /* DisposeBagCollectable.swift */, 2D38F20725CD491300561493 /* DisposeBagCollectable.swift */,
@ -1443,7 +1352,6 @@
DB0FCB892796BE1E006C02E2 /* RecommandAccount */, DB0FCB892796BE1E006C02E2 /* RecommandAccount */,
DB4F097926A039C400D62E92 /* Status */, DB4F097926A039C400D62E92 /* Status */,
DB65C63527A2AF52008BAC2E /* Report */, DB65C63527A2AF52008BAC2E /* Report */,
DB4F097626A0398000D62E92 /* Compose */,
DB0617F727855B010030EE79 /* Notification */, DB0617F727855B010030EE79 /* Notification */,
DB4F097726A039A200D62E92 /* Search */, DB4F097726A039A200D62E92 /* Search */,
DB3E6FE52806A5BA00B035AE /* Discovery */, DB3E6FE52806A5BA00B035AE /* Discovery */,
@ -1467,7 +1375,6 @@
2D7631A525C1532D00929FB9 /* View */ = { 2D7631A525C1532D00929FB9 /* View */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2DA504672601ADBA008F4E6C /* Decoration */,
2D42FF8325C82245004A627A /* Button */, 2D42FF8325C82245004A627A /* Button */,
DBA9B90325F1D4420012E7B6 /* Control */, DBA9B90325F1D4420012E7B6 /* Control */,
2D152A8A25C295B8009AA50C /* Content */, 2D152A8A25C295B8009AA50C /* Content */,
@ -1487,11 +1394,6 @@
DB0FCB7D27958957006C02E2 /* StatusThreadRootTableViewCell+ViewModel.swift */, DB0FCB7D27958957006C02E2 /* StatusThreadRootTableViewCell+ViewModel.swift */,
DB6B74FF272FF73800C70B6E /* UserTableViewCell.swift */, DB6B74FF272FF73800C70B6E /* UserTableViewCell.swift */,
DB0FCB972797F6BF006C02E2 /* UserTableViewCell+ViewModel.swift */, DB0FCB972797F6BF006C02E2 /* UserTableViewCell+ViewModel.swift */,
2DA7D04325CA52B200804E11 /* TimelineLoaderTableViewCell.swift */,
DB938F3226243D6200E5B6C1 /* TimelineTopLoaderTableViewCell.swift */,
2DA7D04925CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift */,
2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */,
DB0FCB6F27951368006C02E2 /* TimelineMiddleLoaderTableViewCell+ViewModel.swift */,
DBE3CDBA261C427900430CC6 /* TimelineHeaderTableViewCell.swift */, DBE3CDBA261C427900430CC6 /* TimelineHeaderTableViewCell.swift */,
DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */, DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */,
DB02CDAA26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift */, DB02CDAA26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift */,
@ -1499,14 +1401,6 @@
path = TableviewCell; path = TableviewCell;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
2DA504672601ADBA008F4E6C /* Decoration */ = {
isa = PBXGroup;
children = (
2DA504682601ADE7008F4E6C /* SawToothView.swift */,
);
path = Decoration;
sourceTree = "<group>";
};
2DAC9E36262FC20B0062E1A6 /* SuggestionAccount */ = { 2DAC9E36262FC20B0062E1A6 /* SuggestionAccount */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1638,17 +1532,6 @@
path = Onboarding; path = Onboarding;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DB03F7F1268990A2007B274C /* TableViewCell */ = {
isa = PBXGroup;
children = (
DB03F7F22689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift */,
DBBC24AB26A53D9300398BB9 /* ComposeStatusContentTableViewCell.swift */,
DB36679C268AB91B0027D07F /* ComposeStatusAttachmentTableViewCell.swift */,
DB3667A3268AE2370027D07F /* ComposeStatusPollTableViewCell.swift */,
);
path = TableViewCell;
sourceTree = "<group>";
};
DB0617F727855B010030EE79 /* Notification */ = { DB0617F727855B010030EE79 /* Notification */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1808,13 +1691,6 @@
path = Discovery; path = Discovery;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DB3E6FEA2806BD2500B035AE /* MastodonUI */ = {
isa = PBXGroup;
children = (
);
path = MastodonUI;
sourceTree = "<group>";
};
DB3E6FED2806D7FC00B035AE /* News */ = { DB3E6FED2806D7FC00B035AE /* News */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1934,23 +1810,6 @@
path = SearchResult; path = SearchResult;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DB4F097626A0398000D62E92 /* Compose */ = {
isa = PBXGroup;
children = (
DB66729525F9F91600D60309 /* ComposeStatusSection.swift */,
DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */,
DB36679E268ABAF20027D07F /* ComposeStatusAttachmentSection.swift */,
DB3667A0268ABB2E0027D07F /* ComposeStatusAttachmentItem.swift */,
DB3667A5268AE2620027D07F /* ComposeStatusPollSection.swift */,
DB3667A7268AE2900027D07F /* ComposeStatusPollItem.swift */,
DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */,
DB44768A260B3F2100B66B82 /* CustomEmojiPickerItem.swift */,
DBBF1DC82652538500E5B703 /* AutoCompleteSection.swift */,
DBBF1DCA2652539E00E5B703 /* AutoCompleteItem.swift */,
);
path = Compose;
sourceTree = "<group>";
};
DB4F097726A039A200D62E92 /* Search */ = { DB4F097726A039A200D62E92 /* Search */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -2016,7 +1875,6 @@
DB55D32225FB4D320002F825 /* View */ = { DB55D32225FB4D320002F825 /* View */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DB03F7F42689B782007B274C /* ComposeTableView.swift */,
DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */, DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */,
DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */, DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */,
DB9A486B26032AC1008B817C /* AttachmentContainerView+EmptyStateView.swift */, DB9A486B26032AC1008B817C /* AttachmentContainerView+EmptyStateView.swift */,
@ -2260,34 +2118,6 @@
path = Follower; path = Follower;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DB6C8C0525F0921200AAA452 /* MastodonSDK */ = {
isa = PBXGroup;
children = (
DB6C8C0E25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift */,
5DDDF1922617442700311060 /* Mastodon+Entity+Account.swift */,
5DDDF1982617447F00311060 /* Mastodon+Entity+Tag.swift */,
5DDDF1A82617489F00311060 /* Mastodon+Entity+History.swift */,
2D650FAA25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift */,
2DB72C8B262D764300CE6173 /* Mastodon+Entity+Notification+Type.swift */,
DBA9443F265D137600C537E1 /* Mastodon+Entity+Field.swift */,
DB6D1B43263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift */,
);
path = MastodonSDK;
sourceTree = "<group>";
};
DB6F5E36264E78EA009108F4 /* AutoComplete */ = {
isa = PBXGroup;
children = (
DBBF1DC02652402000E5B703 /* View */,
DBBF1DC326524D3100E5B703 /* Cell */,
DB6F5E34264E78E7009108F4 /* AutoCompleteViewController.swift */,
DBBF1DBE2652401B00E5B703 /* AutoCompleteViewModel.swift */,
DBBF1DC4265251C300E5B703 /* AutoCompleteViewModel+Diffable.swift */,
DBBF1DC6265251D400E5B703 /* AutoCompleteViewModel+State.swift */,
);
path = AutoComplete;
sourceTree = "<group>";
};
DB72602125E36A2500235243 /* ServerRules */ = { DB72602125E36A2500235243 /* ServerRules */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -2312,10 +2142,8 @@
DB789A1025F9F29B0071ACA0 /* Compose */ = { DB789A1025F9F29B0071ACA0 /* Compose */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DB6F5E36264E78EA009108F4 /* AutoComplete */,
DB55D32225FB4D320002F825 /* View */, DB55D32225FB4D320002F825 /* View */,
DB789A2125F9F76D0071ACA0 /* CollectionViewCell */, DB789A2125F9F76D0071ACA0 /* CollectionViewCell */,
DB03F7F1268990A2007B274C /* TableViewCell */,
DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */, DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */,
DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */, DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */,
DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */, DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */,
@ -2404,8 +2232,6 @@
DB8AF56225C138BC002E6C99 /* Extension */ = { DB8AF56225C138BC002E6C99 /* Extension */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DB3E6FEA2806BD2500B035AE /* MastodonUI */,
DB6C8C0525F0921200AAA452 /* MastodonSDK */,
2DF123A625C3B0210020F248 /* ActiveLabel.swift */, 2DF123A625C3B0210020F248 /* ActiveLabel.swift */,
5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */, 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */,
2D206B8525F5FB0900143C56 /* Double.swift */, 2D206B8525F5FB0900143C56 /* Double.swift */,
@ -2700,22 +2526,6 @@
path = Helper; path = Helper;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DBBF1DC02652402000E5B703 /* View */ = {
isa = PBXGroup;
children = (
DB6F5E37264E994A009108F4 /* AutoCompleteTopChevronView.swift */,
);
path = View;
sourceTree = "<group>";
};
DBBF1DC326524D3100E5B703 /* Cell */ = {
isa = PBXGroup;
children = (
DBBF1DC126524D2900E5B703 /* AutoCompleteTableViewCell.swift */,
);
path = Cell;
sourceTree = "<group>";
};
DBC6461326A170AB00B0E31B /* ShareActionExtension */ = { DBC6461326A170AB00B0E31B /* ShareActionExtension */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -2897,8 +2707,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DBFEF05426A576EE006D7ED1 /* View */, DBFEF05426A576EE006D7ED1 /* View */,
DBC6462226A1712000B0E31B /* ShareViewModel.swift */, DBC6462226A1712000B0E31B /* ComposeViewModel.swift */,
DBC6461426A170AB00B0E31B /* ShareViewController.swift */, DBC6461426A170AB00B0E31B /* ComposeViewController.swift */,
); );
path = Scene; path = Scene;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3347,19 +3157,14 @@
DB65C63727A2AF6C008BAC2E /* ReportItem.swift in Sources */, DB65C63727A2AF6C008BAC2E /* ReportItem.swift in Sources */,
DB5B54B22833C24B00DEF8B2 /* RebloggedByViewController+DataSourceProvider.swift in Sources */, DB5B54B22833C24B00DEF8B2 /* RebloggedByViewController+DataSourceProvider.swift in Sources */,
2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */, 2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */,
DBA94440265D137600C537E1 /* Mastodon+Entity+Field.swift in Sources */,
DBBF1DC7265251D400E5B703 /* AutoCompleteViewModel+State.swift in Sources */,
DB03A793272A7E5700EE37C5 /* SidebarListHeaderView.swift in Sources */, DB03A793272A7E5700EE37C5 /* SidebarListHeaderView.swift in Sources */,
DB4FFC2B269EC39600D62E92 /* SearchToSearchDetailViewControllerAnimatedTransitioning.swift in Sources */, DB4FFC2B269EC39600D62E92 /* SearchToSearchDetailViewControllerAnimatedTransitioning.swift in Sources */,
DB5B7298273112C800081888 /* FollowingListViewModel.swift in Sources */, DB5B7298273112C800081888 /* FollowingListViewModel.swift in Sources */,
0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */, 0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */,
DB5B54AE2833C15F00DEF8B2 /* UserListViewModel+Diffable.swift in Sources */, DB5B54AE2833C15F00DEF8B2 /* UserListViewModel+Diffable.swift in Sources */,
DB336F43278EB1690031E64B /* MediaView+Configuration.swift in Sources */,
DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */,
DB482A3F261331E8008AE74C /* UserTimelineViewModel+State.swift in Sources */, DB482A3F261331E8008AE74C /* UserTimelineViewModel+State.swift in Sources */,
DB3E6FE02806A4ED00B035AE /* DiscoveryHashtagsViewModel.swift in Sources */, DB3E6FE02806A4ED00B035AE /* DiscoveryHashtagsViewModel.swift in Sources */,
2D38F1F725CD47AC00561493 /* HomeTimelineViewModel+LoadOldestState.swift in Sources */, 2D38F1F725CD47AC00561493 /* HomeTimelineViewModel+LoadOldestState.swift in Sources */,
DB447681260B3ED600B66B82 /* CustomEmojiPickerSection.swift in Sources */,
DB0FCB7427956939006C02E2 /* DataSourceFacade+Status.swift in Sources */, DB0FCB7427956939006C02E2 /* DataSourceFacade+Status.swift in Sources */,
DBB525502611ED6D002F1F29 /* ProfileHeaderView.swift in Sources */, DBB525502611ED6D002F1F29 /* ProfileHeaderView.swift in Sources */,
DB63F75A279953F200455B82 /* SearchHistoryUserCollectionViewCell+ViewModel.swift in Sources */, DB63F75A279953F200455B82 /* SearchHistoryUserCollectionViewCell+ViewModel.swift in Sources */,
@ -3395,13 +3200,11 @@
DB697DD9278F4CED004EF2F7 /* HomeTimelineViewController+DataSourceProvider.swift in Sources */, DB697DD9278F4CED004EF2F7 /* HomeTimelineViewController+DataSourceProvider.swift in Sources */,
DB9A488A26034D40008B817C /* ComposeViewModel+PublishState.swift in Sources */, DB9A488A26034D40008B817C /* ComposeViewModel+PublishState.swift in Sources */,
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */, DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
2DA504692601ADE7008F4E6C /* SawToothView.swift in Sources */,
2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */, 2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */,
DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */, DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */,
DB6180F226391CF40018D199 /* MediaPreviewImageViewModel.swift in Sources */, DB6180F226391CF40018D199 /* MediaPreviewImageViewModel.swift in Sources */,
62FD27D12893707600B205C5 /* BookmarkViewController.swift in Sources */, 62FD27D12893707600B205C5 /* BookmarkViewController.swift in Sources */,
DBD5B1F627BCD3D200BD6B38 /* SuggestionAccountTableViewCell+ViewModel.swift in Sources */, DBD5B1F627BCD3D200BD6B38 /* SuggestionAccountTableViewCell+ViewModel.swift in Sources */,
5DDDF1932617442700311060 /* Mastodon+Entity+Account.swift in Sources */,
DB63F767279A5EB300455B82 /* NotificationTimelineViewModel.swift in Sources */, DB63F767279A5EB300455B82 /* NotificationTimelineViewModel.swift in Sources */,
2D607AD826242FC500B70763 /* NotificationViewModel.swift in Sources */, 2D607AD826242FC500B70763 /* NotificationViewModel.swift in Sources */,
DB5B54AB2833C12A00DEF8B2 /* RebloggedByViewController.swift in Sources */, DB5B54AB2833C12A00DEF8B2 /* RebloggedByViewController.swift in Sources */,
@ -3447,7 +3250,6 @@
DBCBED1726132DB500B49291 /* UserTimelineViewModel+Diffable.swift in Sources */, DBCBED1726132DB500B49291 /* UserTimelineViewModel+Diffable.swift in Sources */,
2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */, 2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */,
2DAC9E3E262FC2400062E1A6 /* SuggestionAccountViewModel.swift in Sources */, 2DAC9E3E262FC2400062E1A6 /* SuggestionAccountViewModel.swift in Sources */,
DB3667A8268AE2900027D07F /* ComposeStatusPollItem.swift in Sources */,
DB603113279EBEBA00A935FE /* DataSourceFacade+Block.swift in Sources */, DB603113279EBEBA00A935FE /* DataSourceFacade+Block.swift in Sources */,
DB63F777279A9A2A00455B82 /* NotificationView+Configuration.swift in Sources */, DB63F777279A9A2A00455B82 /* NotificationView+Configuration.swift in Sources */,
DB029E95266A20430062874E /* MastodonAuthenticationController.swift in Sources */, DB029E95266A20430062874E /* MastodonAuthenticationController.swift in Sources */,
@ -3457,7 +3259,6 @@
DBE3CDEC261C6B2900430CC6 /* FavoriteViewController.swift in Sources */, DBE3CDEC261C6B2900430CC6 /* FavoriteViewController.swift in Sources */,
DB938EE62623F50700E5B6C1 /* ThreadViewController.swift in Sources */, DB938EE62623F50700E5B6C1 /* ThreadViewController.swift in Sources */,
DB6180F426391D110018D199 /* MediaPreviewImageView.swift in Sources */, DB6180F426391D110018D199 /* MediaPreviewImageView.swift in Sources */,
DB336F41278E68480031E64B /* StatusView+Configuration.swift in Sources */,
DBF9814A265E24F500E4BA07 /* ProfileFieldCollectionViewHeaderFooterView.swift in Sources */, DBF9814A265E24F500E4BA07 /* ProfileFieldCollectionViewHeaderFooterView.swift in Sources */,
2D939AB525EDD8A90076FA61 /* String.swift in Sources */, 2D939AB525EDD8A90076FA61 /* String.swift in Sources */,
DB4481B925EE289600BEFB67 /* UITableView.swift in Sources */, DB4481B925EE289600BEFB67 /* UITableView.swift in Sources */,
@ -3470,12 +3271,9 @@
DB5B549F2833A72500DEF8B2 /* FamiliarFollowersViewModel+Diffable.swift in Sources */, DB5B549F2833A72500DEF8B2 /* FamiliarFollowersViewModel+Diffable.swift in Sources */,
DB6B351E2601FAEE00DC1E11 /* ComposeStatusAttachmentCollectionViewCell.swift in Sources */, DB6B351E2601FAEE00DC1E11 /* ComposeStatusAttachmentCollectionViewCell.swift in Sources */,
DB8F7076279E954700E1225B /* DataSourceFacade+Follow.swift in Sources */, DB8F7076279E954700E1225B /* DataSourceFacade+Follow.swift in Sources */,
DB36679F268ABAF20027D07F /* ComposeStatusAttachmentSection.swift in Sources */,
2DA7D04425CA52B200804E11 /* TimelineLoaderTableViewCell.swift in Sources */,
DB63F7542799491600455B82 /* DataSourceFacade+SearchHistory.swift in Sources */, DB63F7542799491600455B82 /* DataSourceFacade+SearchHistory.swift in Sources */,
DB7A9F912818EAF10016AF98 /* MastodonRegisterView.swift in Sources */, DB7A9F912818EAF10016AF98 /* MastodonRegisterView.swift in Sources */,
DBF1572F27046F1A00EC00B7 /* SecondaryPlaceholderViewController.swift in Sources */, DBF1572F27046F1A00EC00B7 /* SecondaryPlaceholderViewController.swift in Sources */,
DB03F7F32689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift in Sources */,
2D4AD8A826316D3500613EFC /* SelectedAccountItem.swift in Sources */, 2D4AD8A826316D3500613EFC /* SelectedAccountItem.swift in Sources */,
DBE3CDFB261C6CA500430CC6 /* FavoriteViewModel.swift in Sources */, DBE3CDFB261C6CA500430CC6 /* FavoriteViewModel.swift in Sources */,
DBE3CE01261D623D00430CC6 /* FavoriteViewModel+State.swift in Sources */, DBE3CE01261D623D00430CC6 /* FavoriteViewModel+State.swift in Sources */,
@ -3487,7 +3285,6 @@
DB63F7452799056400455B82 /* HashtagTableViewCell.swift in Sources */, DB63F7452799056400455B82 /* HashtagTableViewCell.swift in Sources */,
DBAE3FAF26172FC0004B8251 /* RemoteProfileViewModel.swift in Sources */, DBAE3FAF26172FC0004B8251 /* RemoteProfileViewModel.swift in Sources */,
DB3EA8EB281B7E0700598866 /* DiscoveryCommunityViewModel+State.swift in Sources */, DB3EA8EB281B7E0700598866 /* DiscoveryCommunityViewModel+State.swift in Sources */,
DB0FCB7227952986006C02E2 /* NamingState.swift in Sources */,
DB0F8150264D1E2500F2A12B /* PickServerLoaderTableViewCell.swift in Sources */, DB0F8150264D1E2500F2A12B /* PickServerLoaderTableViewCell.swift in Sources */,
DB98EB5327B0F9890082E365 /* ReportHeadlineTableViewCell.swift in Sources */, DB98EB5327B0F9890082E365 /* ReportHeadlineTableViewCell.swift in Sources */,
DB5B729C273113C200081888 /* FollowingListViewModel+Diffable.swift in Sources */, DB5B729C273113C200081888 /* FollowingListViewModel+Diffable.swift in Sources */,
@ -3496,14 +3293,12 @@
DBB9759C262462E1004620BD /* ThreadMetaView.swift in Sources */, DBB9759C262462E1004620BD /* ThreadMetaView.swift in Sources */,
DB5B729E273113F300081888 /* FollowingListViewModel+State.swift in Sources */, DB5B729E273113F300081888 /* FollowingListViewModel+State.swift in Sources */,
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */, 2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */,
5DDDF1A92617489F00311060 /* Mastodon+Entity+History.swift in Sources */,
DBF9814C265E339500E4BA07 /* ProfileFieldAddEntryCollectionViewCell.swift in Sources */, DBF9814C265E339500E4BA07 /* ProfileFieldAddEntryCollectionViewCell.swift in Sources */,
DB63F76227996B6600455B82 /* SearchHistoryViewController+DataSourceProvider.swift in Sources */, DB63F76227996B6600455B82 /* SearchHistoryViewController+DataSourceProvider.swift in Sources */,
DB73BF4927140BA300781945 /* UICollectionViewDiffableDataSource.swift in Sources */, DB73BF4927140BA300781945 /* UICollectionViewDiffableDataSource.swift in Sources */,
DBA5E7AB263BD3F5004598BB /* TimelineTableViewCellContextMenuConfiguration.swift in Sources */, DBA5E7AB263BD3F5004598BB /* TimelineTableViewCellContextMenuConfiguration.swift in Sources */,
DB73B490261F030A002E9E9F /* SafariActivity.swift in Sources */, DB73B490261F030A002E9E9F /* SafariActivity.swift in Sources */,
DB63F7492799126300455B82 /* FollowerListViewController+DataSourceProvider.swift in Sources */, DB63F7492799126300455B82 /* FollowerListViewController+DataSourceProvider.swift in Sources */,
DB6D1B44263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift in Sources */,
2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */, 2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */,
2DAC9E38262FC2320062E1A6 /* SuggestionAccountViewController.swift in Sources */, 2DAC9E38262FC2320062E1A6 /* SuggestionAccountViewController.swift in Sources */,
DB66728C25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift in Sources */, DB66728C25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift in Sources */,
@ -3529,14 +3324,12 @@
DB852D1926FAEB6B00FC9D81 /* SidebarViewController.swift in Sources */, DB852D1926FAEB6B00FC9D81 /* SidebarViewController.swift in Sources */,
2D206B9225F60EA700143C56 /* UIControl.swift in Sources */, 2D206B9225F60EA700143C56 /* UIControl.swift in Sources */,
DBDFF1932805554900557A48 /* DiscoveryPostsViewModel.swift in Sources */, DBDFF1932805554900557A48 /* DiscoveryPostsViewModel.swift in Sources */,
DBBF1DC92652538500E5B703 /* AutoCompleteSection.swift in Sources */,
DB3E6FE72806A7A200B035AE /* DiscoveryItem.swift in Sources */, DB3E6FE72806A7A200B035AE /* DiscoveryItem.swift in Sources */,
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */, DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */,
DBEFCD79282A147000C0ABEA /* ReportStatusViewModel.swift in Sources */, DBEFCD79282A147000C0ABEA /* ReportStatusViewModel.swift in Sources */,
DB7F48452620241000796008 /* ProfileHeaderViewModel.swift in Sources */, DB7F48452620241000796008 /* ProfileHeaderViewModel.swift in Sources */,
DB0A322E280EE9FD001729D2 /* DiscoveryIntroBannerView.swift in Sources */, DB0A322E280EE9FD001729D2 /* DiscoveryIntroBannerView.swift in Sources */,
2D3F9E0425DFA133004262D9 /* UITapGestureRecognizer.swift in Sources */, 2D3F9E0425DFA133004262D9 /* UITapGestureRecognizer.swift in Sources */,
5DDDF1992617447F00311060 /* Mastodon+Entity+Tag.swift in Sources */,
5B90C45F262599800002E742 /* SettingsToggleTableViewCell.swift in Sources */, 5B90C45F262599800002E742 /* SettingsToggleTableViewCell.swift in Sources */,
2D694A7425F9EB4E0038ADDC /* ContentWarningOverlayView.swift in Sources */, 2D694A7425F9EB4E0038ADDC /* ContentWarningOverlayView.swift in Sources */,
DB0FCB7827957678006C02E2 /* DataSourceProvider+UITableViewDelegate.swift in Sources */, DB0FCB7827957678006C02E2 /* DataSourceProvider+UITableViewDelegate.swift in Sources */,
@ -3550,12 +3343,9 @@
0F20222D261457EE000C64BF /* HashtagTimelineViewModel+State.swift in Sources */, 0F20222D261457EE000C64BF /* HashtagTimelineViewModel+State.swift in Sources */,
DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */, DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */,
5B90C462262599800002E742 /* SettingsSectionHeader.swift in Sources */, 5B90C462262599800002E742 /* SettingsSectionHeader.swift in Sources */,
DB44768B260B3F2100B66B82 /* CustomEmojiPickerItem.swift in Sources */,
5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */, 5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */,
DB3E6FEF2806D82600B035AE /* DiscoveryNewsViewModel.swift in Sources */, DB3E6FEF2806D82600B035AE /* DiscoveryNewsViewModel.swift in Sources */,
DBBF1DCB2652539E00E5B703 /* AutoCompleteItem.swift in Sources */,
DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */, DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */,
DB3667A6268AE2620027D07F /* ComposeStatusPollSection.swift in Sources */,
DB6B750427300B4000C70B6E /* TimelineFooterTableViewCell.swift in Sources */, DB6B750427300B4000C70B6E /* TimelineFooterTableViewCell.swift in Sources */,
DB98EB4C27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift in Sources */, DB98EB4C27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift in Sources */,
DB852D1F26FB037800FC9D81 /* SidebarViewModel.swift in Sources */, DB852D1F26FB037800FC9D81 /* SidebarViewModel.swift in Sources */,
@ -3568,7 +3358,6 @@
DB0617EF277F12720030EE79 /* NavigationActionView.swift in Sources */, DB0617EF277F12720030EE79 /* NavigationActionView.swift in Sources */,
DB1FD43625F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift in Sources */, DB1FD43625F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift in Sources */,
2D939AE825EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift in Sources */, 2D939AE825EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift in Sources */,
DB3667A1268ABB2E0027D07F /* ComposeStatusAttachmentItem.swift in Sources */,
DB1D186C25EF5BA7003F1F23 /* PollTableView.swift in Sources */, DB1D186C25EF5BA7003F1F23 /* PollTableView.swift in Sources */,
DBA94434265CBB5300C537E1 /* ProfileFieldSection.swift in Sources */, DBA94434265CBB5300C537E1 /* ProfileFieldSection.swift in Sources */,
DBCA0EBC282BB38A0029E2B0 /* PageboyNavigateable.swift in Sources */, DBCA0EBC282BB38A0029E2B0 /* PageboyNavigateable.swift in Sources */,
@ -3587,11 +3376,8 @@
DB023D2C27A10464005AC798 /* NotificationTimelineViewController+DataSourceProvider.swift in Sources */, DB023D2C27A10464005AC798 /* NotificationTimelineViewController+DataSourceProvider.swift in Sources */,
DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */, DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */,
DBF1D257269DBAC600C1C08A /* SearchDetailViewModel.swift in Sources */, DBF1D257269DBAC600C1C08A /* SearchDetailViewModel.swift in Sources */,
DB03F7F52689B782007B274C /* ComposeTableView.swift in Sources */,
DBB45B5927B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift in Sources */, DBB45B5927B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift in Sources */,
DB6C8C0F25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift in Sources */,
DB0FCB76279571C5006C02E2 /* ThreadViewController+DataSourceProvider.swift in Sources */, DB0FCB76279571C5006C02E2 /* ThreadViewController+DataSourceProvider.swift in Sources */,
DB0FCB7027951368006C02E2 /* TimelineMiddleLoaderTableViewCell+ViewModel.swift in Sources */,
DB1E346825F518E20079D7DF /* CategoryPickerSection.swift in Sources */, DB1E346825F518E20079D7DF /* CategoryPickerSection.swift in Sources */,
DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */, DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */,
DB0618052785A73D0030EE79 /* RegisterItem.swift in Sources */, DB0618052785A73D0030EE79 /* RegisterItem.swift in Sources */,
@ -3605,7 +3391,6 @@
DB3E6FDD2806A40F00B035AE /* DiscoveryHashtagsViewController.swift in Sources */, DB3E6FDD2806A40F00B035AE /* DiscoveryHashtagsViewController.swift in Sources */,
DB938EED2623F79B00E5B6C1 /* ThreadViewModel.swift in Sources */, DB938EED2623F79B00E5B6C1 /* ThreadViewModel.swift in Sources */,
DB6988DE2848D11C002398EF /* PagerTabStripNavigateable.swift in Sources */, DB6988DE2848D11C002398EF /* PagerTabStripNavigateable.swift in Sources */,
DBBC24AC26A53D9300398BB9 /* ComposeStatusContentTableViewCell.swift in Sources */,
2DCB73FD2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift in Sources */, 2DCB73FD2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift in Sources */,
DB852D1C26FB021500FC9D81 /* RootSplitViewController.swift in Sources */, DB852D1C26FB021500FC9D81 /* RootSplitViewController.swift in Sources */,
DB697DD1278F4871004EF2F7 /* AutoGenerateTableViewDelegate.swift in Sources */, DB697DD1278F4871004EF2F7 /* AutoGenerateTableViewDelegate.swift in Sources */,
@ -3619,7 +3404,6 @@
DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */, DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */,
DB63F779279ABF9C00455B82 /* DataSourceFacade+Reblog.swift in Sources */, DB63F779279ABF9C00455B82 /* DataSourceFacade+Reblog.swift in Sources */,
DB4F0963269ED06300D62E92 /* SearchResultViewController.swift in Sources */, DB4F0963269ED06300D62E92 /* SearchResultViewController.swift in Sources */,
DBBF1DC5265251C300E5B703 /* AutoCompleteViewModel+Diffable.swift in Sources */,
DB603111279EB38500A935FE /* DataSourceFacade+Mute.swift in Sources */, DB603111279EB38500A935FE /* DataSourceFacade+Mute.swift in Sources */,
DB68A04A25E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift in Sources */, DB68A04A25E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift in Sources */,
0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */, 0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */,
@ -3636,7 +3420,6 @@
DBA5E7A9263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift in Sources */, DBA5E7A9263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift in Sources */,
DBF156E22702DA6900EC00B7 /* UIStatusBarManager+HandleTapAction.m in Sources */, DBF156E22702DA6900EC00B7 /* UIStatusBarManager+HandleTapAction.m in Sources */,
2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */, 2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */,
DB6F5E35264E78E7009108F4 /* AutoCompleteViewController.swift in Sources */,
DB697DE1278F5296004EF2F7 /* DataSourceFacade+Model.swift in Sources */, DB697DE1278F5296004EF2F7 /* DataSourceFacade+Model.swift in Sources */,
DBCC3B8F26148F7B0045B23D /* CachedProfileViewModel.swift in Sources */, DBCC3B8F26148F7B0045B23D /* CachedProfileViewModel.swift in Sources */,
DB4F097526A037F500D62E92 /* SearchHistoryViewModel.swift in Sources */, DB4F097526A037F500D62E92 /* SearchHistoryViewModel.swift in Sources */,
@ -3650,9 +3433,7 @@
DB9F58F126EF512300E7BBE9 /* AccountListTableViewCell.swift in Sources */, DB9F58F126EF512300E7BBE9 /* AccountListTableViewCell.swift in Sources */,
2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */, 2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */,
DB938F0326240EA300E5B6C1 /* CachedThreadViewModel.swift in Sources */, DB938F0326240EA300E5B6C1 /* CachedThreadViewModel.swift in Sources */,
2D650FAB25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift in Sources */,
DBA5A53526F0A36A00CACBAA /* AddAccountTableViewCell.swift in Sources */, DBA5A53526F0A36A00CACBAA /* AddAccountTableViewCell.swift in Sources */,
2DB72C8C262D764300CE6173 /* Mastodon+Entity+Notification+Type.swift in Sources */,
2D35237A26256D920031AF25 /* NotificationSection.swift in Sources */, 2D35237A26256D920031AF25 /* NotificationSection.swift in Sources */,
2D4AD89C263165B500613EFC /* SuggestionAccountCollectionViewCell.swift in Sources */, 2D4AD89C263165B500613EFC /* SuggestionAccountCollectionViewCell.swift in Sources */,
DB98EB6927B21A7C0082E365 /* ReportResultActionTableViewCell.swift in Sources */, DB98EB6927B21A7C0082E365 /* ReportResultActionTableViewCell.swift in Sources */,
@ -3672,8 +3453,6 @@
DB45FAB625CA5485005A8AC7 /* UIAlertController.swift in Sources */, DB45FAB625CA5485005A8AC7 /* UIAlertController.swift in Sources */,
DBE0821525CD382600FD6BBD /* MastodonRegisterViewController.swift in Sources */, DBE0821525CD382600FD6BBD /* MastodonRegisterViewController.swift in Sources */,
DBEFCD74282A130400C0ABEA /* ReportReasonViewModel.swift in Sources */, DBEFCD74282A130400C0ABEA /* ReportReasonViewModel.swift in Sources */,
2D5A3D0325CF8742002347D6 /* ControlContainableScrollViews.swift in Sources */,
DB36679D268AB91B0027D07F /* ComposeStatusAttachmentTableViewCell.swift in Sources */,
DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */, DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */,
DBFEEC96279BDC67004F81DD /* ProfileAboutViewController.swift in Sources */, DBFEEC96279BDC67004F81DD /* ProfileAboutViewController.swift in Sources */,
DB63F74F2799405600455B82 /* SearchHistoryViewModel+Diffable.swift in Sources */, DB63F74F2799405600455B82 /* SearchHistoryViewModel+Diffable.swift in Sources */,
@ -3689,10 +3468,8 @@
DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */, DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */,
DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */, DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */,
DB1D61CF26F1B33600DA8662 /* WelcomeViewModel.swift in Sources */, DB1D61CF26F1B33600DA8662 /* WelcomeViewModel.swift in Sources */,
2DA7D04A25CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift in Sources */,
DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */, DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */,
DB4F0966269ED52200D62E92 /* SearchResultViewModel.swift in Sources */, DB4F0966269ED52200D62E92 /* SearchResultViewModel.swift in Sources */,
DBBF1DBF2652401B00E5B703 /* AutoCompleteViewModel.swift in Sources */,
DB6180FA26391F2E0018D199 /* MediaPreviewViewModel.swift in Sources */, DB6180FA26391F2E0018D199 /* MediaPreviewViewModel.swift in Sources */,
2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */, 2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */,
DB6B7500272FF73800C70B6E /* UserTableViewCell.swift in Sources */, DB6B7500272FF73800C70B6E /* UserTableViewCell.swift in Sources */,
@ -3708,7 +3485,6 @@
DB3E6FE42806A5B800B035AE /* DiscoverySection.swift in Sources */, DB3E6FE42806A5B800B035AE /* DiscoverySection.swift in Sources */,
DB8190C62601FF0400020C08 /* AttachmentContainerView.swift in Sources */, DB8190C62601FF0400020C08 /* AttachmentContainerView.swift in Sources */,
DB697DDB278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift in Sources */, DB697DDB278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift in Sources */,
2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */,
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */, DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */,
DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */, DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */,
DB0FCB8027968F70006C02E2 /* MastodonStatusThreadViewModel.swift in Sources */, DB0FCB8027968F70006C02E2 /* MastodonStatusThreadViewModel.swift in Sources */,
@ -3724,7 +3500,6 @@
DB3E6FF12806D96900B035AE /* DiscoveryNewsViewModel+Diffable.swift in Sources */, DB3E6FF12806D96900B035AE /* DiscoveryNewsViewModel+Diffable.swift in Sources */,
DB3E6FF82807C45300B035AE /* DiscoveryForYouViewModel.swift in Sources */, DB3E6FF82807C45300B035AE /* DiscoveryForYouViewModel.swift in Sources */,
DB0F9D56283EB46200379AF8 /* ProfileHeaderView+Configuration.swift in Sources */, DB0F9D56283EB46200379AF8 /* ProfileHeaderView+Configuration.swift in Sources */,
DB6F5E38264E994A009108F4 /* AutoCompleteTopChevronView.swift in Sources */,
DB6746F0278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift in Sources */, DB6746F0278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift in Sources */,
DBB525412611ED54002F1F29 /* ProfileHeaderViewController.swift in Sources */, DBB525412611ED54002F1F29 /* ProfileHeaderViewController.swift in Sources */,
DBDFF19A28055A1400557A48 /* DiscoveryViewController.swift in Sources */, DBDFF19A28055A1400557A48 /* DiscoveryViewController.swift in Sources */,
@ -3738,10 +3513,7 @@
DB63F74B279914A000455B82 /* FollowingListViewController+DataSourceProvider.swift in Sources */, DB63F74B279914A000455B82 /* FollowingListViewController+DataSourceProvider.swift in Sources */,
DBEFCD7D282A2A3B00C0ABEA /* ReportServerRulesViewController.swift in Sources */, DBEFCD7D282A2A3B00C0ABEA /* ReportServerRulesViewController.swift in Sources */,
DBB525362611ECEB002F1F29 /* UserTimelineViewController.swift in Sources */, DBB525362611ECEB002F1F29 /* UserTimelineViewController.swift in Sources */,
DB938F3326243D6200E5B6C1 /* TimelineTopLoaderTableViewCell.swift in Sources */,
DB98EB4927B0F0CD0082E365 /* ReportStatusTableViewCell.swift in Sources */, DB98EB4927B0F0CD0082E365 /* ReportStatusTableViewCell.swift in Sources */,
DB3667A4268AE2370027D07F /* ComposeStatusPollTableViewCell.swift in Sources */,
DBBF1DC226524D2900E5B703 /* AutoCompleteTableViewCell.swift in Sources */,
DBF3B7412733EB9400E21627 /* MastodonLocalCode.swift in Sources */, DBF3B7412733EB9400E21627 /* MastodonLocalCode.swift in Sources */,
DB98EB6527B216500082E365 /* ReportResultViewModel.swift in Sources */, DB98EB6527B216500082E365 /* ReportResultViewModel.swift in Sources */,
DB4F096A269EDAD200D62E92 /* SearchResultViewModel+State.swift in Sources */, DB4F096A269EDAD200D62E92 /* SearchResultViewModel+State.swift in Sources */,
@ -3751,7 +3523,6 @@
DB023D2A27A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift in Sources */, DB023D2A27A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift in Sources */,
DB98EB6027B10E150082E365 /* ReportCommentTableViewCell.swift in Sources */, DB98EB6027B10E150082E365 /* ReportCommentTableViewCell.swift in Sources */,
DB0FCB962797E6C2006C02E2 /* SearchResultViewController+DataSourceProvider.swift in Sources */, DB0FCB962797E6C2006C02E2 /* SearchResultViewController+DataSourceProvider.swift in Sources */,
DB66729C25F9F91F00D60309 /* ComposeStatusItem.swift in Sources */,
DB6180E326391A4C0018D199 /* ViewControllerAnimatedTransitioning.swift in Sources */, DB6180E326391A4C0018D199 /* ViewControllerAnimatedTransitioning.swift in Sources */,
DBD5B1F827BCFD9D00BD6B38 /* DataSourceProvider+TableViewControllerNavigateable.swift in Sources */, DBD5B1F827BCFD9D00BD6B38 /* DataSourceProvider+TableViewControllerNavigateable.swift in Sources */,
0FB3D31E25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift in Sources */, 0FB3D31E25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift in Sources */,
@ -3796,18 +3567,9 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DBFEF05E26A57715006D7ED1 /* ComposeView.swift in Sources */, DBC6462326A1712000B0E31B /* ComposeViewModel.swift in Sources */,
DBFEF06026A57715006D7ED1 /* StatusAttachmentView.swift in Sources */,
DBC6462326A1712000B0E31B /* ShareViewModel.swift in Sources */,
DBFEF06D26A67FB7006D7ED1 /* StatusAttachmentViewModel+UploadState.swift in Sources */,
DBFEF05F26A57715006D7ED1 /* StatusAuthorView.swift in Sources */,
DBFEF05D26A57715006D7ED1 /* ContentWarningEditorView.swift in Sources */,
DBFEF05C26A57715006D7ED1 /* StatusEditorView.swift in Sources */,
DBB3BA2B26A81D060004F2D4 /* FLAnimatedImageView.swift in Sources */, DBB3BA2B26A81D060004F2D4 /* FLAnimatedImageView.swift in Sources */,
DBBC24A826A52F9000398BB9 /* ComposeToolbarView.swift in Sources */, DBC6461526A170AB00B0E31B /* ComposeViewController.swift in Sources */,
DBFEF05B26A57715006D7ED1 /* ComposeViewModel.swift in Sources */,
DBFEF06326A577F2006D7ED1 /* StatusAttachmentViewModel.swift in Sources */,
DBC6461526A170AB00B0E31B /* ShareViewController.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -112,12 +112,12 @@
<key>NotificationService.xcscheme_^#shared#^_</key> <key>NotificationService.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>25</integer> <integer>18</integer>
</dict> </dict>
<key>ShareActionExtension.xcscheme_^#shared#^_</key> <key>ShareActionExtension.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>24</integer> <integer>17</integer>
</dict> </dict>
</dict> </dict>
<key>SuppressBuildableAutocreation</key> <key>SuppressBuildableAutocreation</key>

View File

@ -1,62 +0,0 @@
//
// CustomEmojiPickerSection.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-3-24.
//
import UIKit
enum CustomEmojiPickerSection: Equatable, Hashable {
case emoji(name: String)
}
extension CustomEmojiPickerSection {
static func collectionViewDiffableDataSource(
for collectionView: UICollectionView,
dependency: NeedsDependency
) -> UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem> {
let dataSource = UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem>(collectionView: collectionView) { [weak dependency] collectionView, indexPath, item -> UICollectionViewCell? in
guard let _ = dependency else { return nil }
switch item {
case .emoji(let attribute):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: CustomEmojiPickerItemCollectionViewCell.self), for: indexPath) as! CustomEmojiPickerItemCollectionViewCell
let placeholder = UIImage.placeholder(size: CustomEmojiPickerItemCollectionViewCell.itemSize, color: .systemFill)
.af.imageRounded(withCornerRadius: 4)
let isAnimated = !UserDefaults.shared.preferredStaticEmoji
let url = URL(string: isAnimated ? attribute.emoji.url : attribute.emoji.staticURL)
cell.emojiImageView.sd_setImage(
with: url,
placeholderImage: placeholder,
options: [],
context: nil
)
cell.accessibilityLabel = attribute.emoji.shortcode
return cell
}
}
dataSource.supplementaryViewProvider = { [weak dataSource] collectionView, kind, indexPath -> UICollectionReusableView? in
guard let dataSource = dataSource else { return nil }
let sections = dataSource.snapshot().sectionIdentifiers
guard indexPath.section < sections.count else { return nil }
let section = sections[indexPath.section]
switch kind {
case String(describing: CustomEmojiPickerHeaderCollectionReusableView.self):
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: CustomEmojiPickerHeaderCollectionReusableView.self), for: indexPath) as! CustomEmojiPickerHeaderCollectionReusableView
switch section {
case .emoji(let name):
header.titleLabel.text = name
}
return header
default:
assertionFailure()
return nil
}
}
return dataSource
}
}

View File

@ -15,6 +15,7 @@ import MetaTextKit
import MastodonMeta import MastodonMeta
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
enum NotificationSection: Equatable, Hashable { enum NotificationSection: Equatable, Hashable {

View File

@ -14,6 +14,7 @@ import UIKit
import os.log import os.log
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
enum ReportSection: Equatable, Hashable { enum ReportSection: Equatable, Hashable {

View File

@ -10,8 +10,9 @@ import UIKit
import CoreData import CoreData
import CoreDataStack import CoreDataStack
import MastodonCore import MastodonCore
import MetaTextKit import MastodonUI
import MastodonMeta import MastodonMeta
import MetaTextKit
enum UserSection: Hashable { enum UserSection: Hashable {
case main case main

View File

@ -1,51 +0,0 @@
//
// Mastodon+Entity+Account.swift
// Mastodon
//
// Created by xiaojian sun on 2021/4/2.
//
import UIKit
import MastodonSDK
import MastodonMeta
extension Mastodon.Entity.Account: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
public static func == (lhs: Mastodon.Entity.Account, rhs: Mastodon.Entity.Account) -> Bool {
return lhs.id == rhs.id
}
}
extension Mastodon.Entity.Account {
var displayNameWithFallback: String {
return !displayName.isEmpty ? displayName : username
}
}
extension Mastodon.Entity.Account {
public func avatarImageURL() -> URL? {
let string = UserDefaults.shared.preferredStaticAvatar ? avatarStatic ?? avatar : avatar
return URL(string: string)
}
public func avatarImageURLWithFallback(domain: String) -> URL {
return avatarImageURL() ?? URL(string: "https://\(domain)/avatars/original/missing.png")!
}
}
extension Mastodon.Entity.Account {
var emojiMeta: MastodonContent.Emojis {
let isAnimated = !UserDefaults.shared.preferredStaticEmoji
var dict = MastodonContent.Emojis()
for emoji in emojis ?? [] {
dict[emoji.shortcode] = isAnimated ? emoji.url : emoji.staticURL
}
return dict
}
}

View File

@ -1,18 +0,0 @@
//
// Mastodon+Entity+Tag.swift
// Mastodon
//
// Created by xiaojian sun on 2021/4/2.
//
import MastodonSDK
//extension Mastodon.Entity.Tag: Hashable {
// public func hash(into hasher: inout Hasher) {
// hasher.combine(name)
// }
//
// public static func == (lhs: Mastodon.Entity.Tag, rhs: Mastodon.Entity.Tag) -> Bool {
// return lhs.name == rhs.name
// }
//}

View File

@ -1,12 +0,0 @@
//
// NamingState.swift
// Mastodon
//
// Created by MainasuK on 2022-1-17.
//
import Foundation
protocol NamingState {
var name: String { get }
}

View File

@ -103,8 +103,8 @@ extension DataSourceFacade {
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: provider.context, context: provider.context,
composeKind: .reply(status: status), authContext: provider.authContext,
authContext: provider.authContext kind: .reply(status: status)
) )
_ = provider.coordinator.present( _ = provider.coordinator.present(
scene: .compose(viewModel: composeViewModel), scene: .compose(viewModel: composeViewModel),

View File

@ -99,10 +99,10 @@ extension StatusTableViewControllerNavigateableCore where Self: DataSourceProvid
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: self.context, context: self.context,
composeKind: .reply(status: status), authContext: authContext,
authContext: authContext kind: .reply(status: status)
) )
self.coordinator.present( _ = self.coordinator.present(
scene: .compose(viewModel: composeViewModel), scene: .compose(viewModel: composeViewModel),
from: self, from: self,
transition: .modal(animated: true, completion: nil) transition: .modal(animated: true, completion: nil)

View File

@ -1,22 +0,0 @@
//
// AutoCompleteViewModel+Diffable.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-5-17.
//
import UIKit
extension AutoCompleteViewModel {
func setupDiffableDataSource(
for tableView: UITableView
) {
diffableDataSource = AutoCompleteSection.tableViewDiffableDataSource(for: tableView)
var snapshot = NSDiffableDataSourceSnapshot<AutoCompleteSection, AutoCompleteItem>()
snapshot.appendSections([.main])
diffableDataSource?.apply(snapshot)
}
}

View File

@ -13,66 +13,66 @@ import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization import MastodonLocalization
protocol ComposeStatusPollExpiresOptionCollectionViewCellDelegate: AnyObject { //protocol ComposeStatusPollExpiresOptionCollectionViewCellDelegate: AnyObject {
func composeStatusPollExpiresOptionCollectionViewCell(_ cell: ComposeStatusPollExpiresOptionCollectionViewCell, didSelectExpiresOption expiresOption: ComposeStatusPollItem.PollExpiresOptionAttribute.ExpiresOption) // func composeStatusPollExpiresOptionCollectionViewCell(_ cell: ComposeStatusPollExpiresOptionCollectionViewCell, didSelectExpiresOption expiresOption: ComposeStatusPollItem.PollExpiresOptionAttribute.ExpiresOption)
} //}
//
final class ComposeStatusPollExpiresOptionCollectionViewCell: UICollectionViewCell { //final class ComposeStatusPollExpiresOptionCollectionViewCell: UICollectionViewCell {
//
var disposeBag = Set<AnyCancellable>() // var disposeBag = Set<AnyCancellable>()
weak var delegate: ComposeStatusPollExpiresOptionCollectionViewCellDelegate? // weak var delegate: ComposeStatusPollExpiresOptionCollectionViewCellDelegate?
//
let durationButton: UIButton = { // let durationButton: UIButton = {
let button = HighlightDimmableButton() // let button = HighlightDimmableButton()
button.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 12)) // button.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 12))
button.expandEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: -20, right: -20) // button.expandEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: -20, right: -20)
button.setTitle(L10n.Scene.Compose.Poll.durationTime(L10n.Scene.Compose.Poll.thirtyMinutes), for: .normal) // button.setTitle(L10n.Scene.Compose.Poll.durationTime(L10n.Scene.Compose.Poll.thirtyMinutes), for: .normal)
button.setTitleColor(Asset.Colors.brand.color, for: .normal) // button.setTitleColor(Asset.Colors.brand.color, for: .normal)
return button // return button
}() // }()
//
override init(frame: CGRect) { // override init(frame: CGRect) {
super.init(frame: frame) // super.init(frame: frame)
_init() // _init()
} // }
//
required init?(coder: NSCoder) { // required init?(coder: NSCoder) {
super.init(coder: coder) // super.init(coder: coder)
_init() // _init()
} // }
//
} //}
//
extension ComposeStatusPollExpiresOptionCollectionViewCell { //extension ComposeStatusPollExpiresOptionCollectionViewCell {
//
private typealias ExpiresOption = ComposeStatusPollItem.PollExpiresOptionAttribute.ExpiresOption // private typealias ExpiresOption = ComposeStatusPollItem.PollExpiresOptionAttribute.ExpiresOption
//
private func _init() { // private func _init() {
durationButton.translatesAutoresizingMaskIntoConstraints = false // durationButton.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(durationButton) // contentView.addSubview(durationButton)
NSLayoutConstraint.activate([ // NSLayoutConstraint.activate([
durationButton.topAnchor.constraint(equalTo: contentView.topAnchor), // durationButton.topAnchor.constraint(equalTo: contentView.topAnchor),
durationButton.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor, constant: PollOptionView.checkmarkBackgroundLeadingMargin), // durationButton.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor, constant: PollOptionView.checkmarkBackgroundLeadingMargin),
durationButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), // durationButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
]) // ])
//
let children = ExpiresOption.allCases.map { expiresOption -> UIAction in // let children = ExpiresOption.allCases.map { expiresOption -> UIAction in
UIAction(title: expiresOption.title, image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] action in // UIAction(title: expiresOption.title, image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] action in
guard let self = self else { return } // guard let self = self else { return }
self.expiresOptionActionHandler(action, expiresOption: expiresOption) // self.expiresOptionActionHandler(action, expiresOption: expiresOption)
} // }
} // }
durationButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: children) // durationButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: children)
durationButton.showsMenuAsPrimaryAction = true // durationButton.showsMenuAsPrimaryAction = true
} // }
//
} //}
//
extension ComposeStatusPollExpiresOptionCollectionViewCell { //extension ComposeStatusPollExpiresOptionCollectionViewCell {
//
private func expiresOptionActionHandler(_ sender: UIAction, expiresOption: ExpiresOption) { // private func expiresOptionActionHandler(_ sender: UIAction, expiresOption: ExpiresOption) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: select %s", ((#file as NSString).lastPathComponent), #line, #function, expiresOption.title) // os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: select %s", ((#file as NSString).lastPathComponent), #line, #function, expiresOption.title)
delegate?.composeStatusPollExpiresOptionCollectionViewCell(self, didSelectExpiresOption: expiresOption) // delegate?.composeStatusPollExpiresOptionCollectionViewCell(self, didSelectExpiresOption: expiresOption)
} // }
//
} //}

File diff suppressed because it is too large Load Diff

View File

@ -18,497 +18,473 @@ import MastodonSDK
extension ComposeViewModel { extension ComposeViewModel {
func setupDataSource( // func setupDataSource(
tableView: UITableView, // tableView: UITableView,
metaTextDelegate: MetaTextDelegate, // metaTextDelegate: MetaTextDelegate,
metaTextViewDelegate: UITextViewDelegate, // metaTextViewDelegate: UITextViewDelegate,
customEmojiPickerInputViewModel: CustomEmojiPickerInputViewModel, // customEmojiPickerInputViewModel: CustomEmojiPickerInputViewModel,
composeStatusAttachmentCollectionViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate, // composeStatusAttachmentCollectionViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate,
composeStatusPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionCollectionViewCellDelegate, // composeStatusPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionCollectionViewCellDelegate,
composeStatusPollOptionAppendEntryCollectionViewCellDelegate: ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate, // composeStatusPollOptionAppendEntryCollectionViewCellDelegate: ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate,
composeStatusPollExpiresOptionCollectionViewCellDelegate: ComposeStatusPollExpiresOptionCollectionViewCellDelegate // composeStatusPollExpiresOptionCollectionViewCellDelegate: ComposeStatusPollExpiresOptionCollectionViewCellDelegate
) { // ) {
// UI // // UI
bind() // bind()
//
// content // // content
bind(cell: composeStatusContentTableViewCell, tableView: tableView) // bind(cell: composeStatusContentTableViewCell, tableView: tableView)
composeStatusContentTableViewCell.metaText.delegate = metaTextDelegate // composeStatusContentTableViewCell.metaText.delegate = metaTextDelegate
composeStatusContentTableViewCell.metaText.textView.delegate = metaTextViewDelegate // composeStatusContentTableViewCell.metaText.textView.delegate = metaTextViewDelegate
//
// attachment // // attachment
bind(cell: composeStatusAttachmentTableViewCell, tableView: tableView) // bind(cell: composeStatusAttachmentTableViewCell, tableView: tableView)
composeStatusAttachmentTableViewCell.composeStatusAttachmentCollectionViewCellDelegate = composeStatusAttachmentCollectionViewCellDelegate // composeStatusAttachmentTableViewCell.composeStatusAttachmentCollectionViewCellDelegate = composeStatusAttachmentCollectionViewCellDelegate
//
// poll // // poll
bind(cell: composeStatusPollTableViewCell, tableView: tableView) // bind(cell: composeStatusPollTableViewCell, tableView: tableView)
composeStatusPollTableViewCell.delegate = self // composeStatusPollTableViewCell.delegate = self
composeStatusPollTableViewCell.customEmojiPickerInputViewModel = customEmojiPickerInputViewModel // composeStatusPollTableViewCell.customEmojiPickerInputViewModel = customEmojiPickerInputViewModel
composeStatusPollTableViewCell.composeStatusPollOptionCollectionViewCellDelegate = composeStatusPollOptionCollectionViewCellDelegate // composeStatusPollTableViewCell.composeStatusPollOptionCollectionViewCellDelegate = composeStatusPollOptionCollectionViewCellDelegate
composeStatusPollTableViewCell.composeStatusPollOptionAppendEntryCollectionViewCellDelegate = composeStatusPollOptionAppendEntryCollectionViewCellDelegate // composeStatusPollTableViewCell.composeStatusPollOptionAppendEntryCollectionViewCellDelegate = composeStatusPollOptionAppendEntryCollectionViewCellDelegate
composeStatusPollTableViewCell.composeStatusPollExpiresOptionCollectionViewCellDelegate = composeStatusPollExpiresOptionCollectionViewCellDelegate // composeStatusPollTableViewCell.composeStatusPollExpiresOptionCollectionViewCellDelegate = composeStatusPollExpiresOptionCollectionViewCellDelegate
//
// setup data source // // setup data source
tableView.dataSource = self // tableView.dataSource = self
} // }
//
func setupCustomEmojiPickerDiffableDataSource( // func setupCustomEmojiPickerDiffableDataSource(
for collectionView: UICollectionView, // for collectionView: UICollectionView,
dependency: NeedsDependency // dependency: NeedsDependency
) { // ) {
let diffableDataSource = CustomEmojiPickerSection.collectionViewDiffableDataSource( // let diffableDataSource = CustomEmojiPickerSection.collectionViewDiffableDataSource(
for: collectionView, // for: collectionView,
dependency: dependency // dependency: dependency
) // )
self.customEmojiPickerDiffableDataSource = diffableDataSource // self.customEmojiPickerDiffableDataSource = diffableDataSource
//
let _domain = customEmojiViewModel?.domain // let _domain = customEmojiViewModel?.domain
customEmojiViewModel?.emojis // customEmojiViewModel?.emojis
.receive(on: DispatchQueue.main) // .receive(on: DispatchQueue.main)
.sink { [weak self, weak diffableDataSource] emojis in // .sink { [weak self, weak diffableDataSource] emojis in
guard let _ = self else { return } // guard let _ = self else { return }
guard let diffableDataSource = diffableDataSource else { return } // guard let diffableDataSource = diffableDataSource else { return }
//
var snapshot = NSDiffableDataSourceSnapshot<CustomEmojiPickerSection, CustomEmojiPickerItem>() // var snapshot = NSDiffableDataSourceSnapshot<CustomEmojiPickerSection, CustomEmojiPickerItem>()
let domain = _domain?.uppercased() ?? " " // let domain = _domain?.uppercased() ?? " "
let customEmojiSection = CustomEmojiPickerSection.emoji(name: domain) // let customEmojiSection = CustomEmojiPickerSection.emoji(name: domain)
snapshot.appendSections([customEmojiSection]) // snapshot.appendSections([customEmojiSection])
let items: [CustomEmojiPickerItem] = { // let items: [CustomEmojiPickerItem] = {
var items = [CustomEmojiPickerItem]() // var items = [CustomEmojiPickerItem]()
for emoji in emojis where emoji.visibleInPicker { // for emoji in emojis where emoji.visibleInPicker {
let attribute = CustomEmojiPickerItem.CustomEmojiAttribute(emoji: emoji) // let attribute = CustomEmojiPickerItem.CustomEmojiAttribute(emoji: emoji)
let item = CustomEmojiPickerItem.emoji(attribute: attribute) // let item = CustomEmojiPickerItem.emoji(attribute: attribute)
items.append(item) // items.append(item)
} // }
return items // return items
}() // }()
snapshot.appendItems(items, toSection: customEmojiSection) // snapshot.appendItems(items, toSection: customEmojiSection)
//
diffableDataSource.apply(snapshot) // diffableDataSource.apply(snapshot)
} // }
.store(in: &disposeBag) // .store(in: &disposeBag)
} // }
} }
// MARK: - UITableViewDataSource //// MARK: - UITableViewDataSource
extension ComposeViewModel: UITableViewDataSource { //extension ComposeViewModel: UITableViewDataSource {
enum Section: CaseIterable { // func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
case repliedTo // switch Section.allCases[indexPath.section] {
case status // case .repliedTo:
case attachment // let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeRepliedToStatusContentTableViewCell.self), for: indexPath) as! ComposeRepliedToStatusContentTableViewCell
case poll // guard case let .reply(record) = composeKind else { return cell }
} //
// // bind frame publisher
// cell.framePublisher
// .receive(on: DispatchQueue.main)
// .assign(to: \.repliedToCellFrame, on: self)
// .store(in: &cell.disposeBag)
//
// // set initial width
// if cell.statusView.frame.width == .zero {
// cell.statusView.frame.size.width = tableView.frame.width
// }
//
// // configure status
// context.managedObjectContext.performAndWait {
// guard let replyTo = record.object(in: context.managedObjectContext) else { return }
// cell.statusView.configure(status: replyTo)
// }
//
// return cell
// case .status:
// return composeStatusContentTableViewCell
// case .attachment:
// return composeStatusAttachmentTableViewCell
// case .poll:
// return composeStatusPollTableViewCell
// }
// }
//}
func numberOfSections(in tableView: UITableView) -> Int { //// MARK: - ComposeStatusPollTableViewCellDelegate
return Section.allCases.count //extension ComposeViewModel: ComposeStatusPollTableViewCellDelegate {
} // func composeStatusPollTableViewCell(_ cell: ComposeStatusPollTableViewCell, pollOptionAttributesDidReorder options: [ComposeStatusPollItem.PollOptionAttribute]) {
// os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { //
switch Section.allCases[section] { // self.pollOptionAttributes = options
case .repliedTo: // }
switch composeKind { //}
case .reply: return 1 //
default: return 0 //extension ComposeViewModel {
} // private func bind() {
case .status: return 1 // $isCustomEmojiComposing
case .attachment: return 1 // .assign(to: \.value, on: customEmojiPickerInputViewModel.isCustomEmojiComposing)
case .poll: return 1 // .store(in: &disposeBag)
} //
} // $isContentWarningComposing
// .assign(to: \.isContentWarningComposing, on: composeStatusAttribute)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // .store(in: &disposeBag)
switch Section.allCases[indexPath.section] { //
case .repliedTo: // // bind compose toolbar UI state
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeRepliedToStatusContentTableViewCell.self), for: indexPath) as! ComposeRepliedToStatusContentTableViewCell // Publishers.CombineLatest(
guard case let .reply(record) = composeKind else { return cell } // $isPollComposing,
// $attachmentServices
// bind frame publisher // )
cell.framePublisher // .receive(on: DispatchQueue.main)
.receive(on: DispatchQueue.main) // .sink(receiveValue: { [weak self] isPollComposing, attachmentServices in
.assign(to: \.repliedToCellFrame, on: self) // guard let self = self else { return }
.store(in: &cell.disposeBag) // let shouldMediaDisable = isPollComposing || attachmentServices.count >= self.maxMediaAttachments
// let shouldPollDisable = attachmentServices.count > 0
// set initial width //
if cell.statusView.frame.width == .zero { // self.isMediaToolbarButtonEnabled = !shouldMediaDisable
cell.statusView.frame.size.width = tableView.frame.width // self.isPollToolbarButtonEnabled = !shouldPollDisable
} // })
// .store(in: &disposeBag)
// configure status //
context.managedObjectContext.performAndWait { // // calculate `Idempotency-Key`
guard let replyTo = record.object(in: context.managedObjectContext) else { return } // let content = Publishers.CombineLatest3(
cell.statusView.configure(status: replyTo) // composeStatusAttribute.$isContentWarningComposing,
} // composeStatusAttribute.$contentWarningContent,
// composeStatusAttribute.$composeContent
return cell // )
case .status: // .map { isContentWarningComposing, contentWarningContent, composeContent -> String in
return composeStatusContentTableViewCell // if isContentWarningComposing {
case .attachment: // return contentWarningContent + (composeContent ?? "")
return composeStatusAttachmentTableViewCell // } else {
case .poll: // return composeContent ?? ""
return composeStatusPollTableViewCell // }
} // }
} // let attachmentIDs = $attachmentServices.map { attachments -> String in
} // let attachmentIDs = attachments.compactMap { $0.attachment.value?.id }
// return attachmentIDs.joined(separator: ",")
// MARK: - ComposeStatusPollTableViewCellDelegate // }
extension ComposeViewModel: ComposeStatusPollTableViewCellDelegate { // let pollOptionsAndDuration = Publishers.CombineLatest3(
func composeStatusPollTableViewCell(_ cell: ComposeStatusPollTableViewCell, pollOptionAttributesDidReorder options: [ComposeStatusPollItem.PollOptionAttribute]) { // $isPollComposing,
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) // $pollOptionAttributes,
// pollExpiresOptionAttribute.expiresOption
self.pollOptionAttributes = options // )
} // .map { isPollComposing, pollOptionAttributes, expiresOption -> String in
} // guard isPollComposing else {
// return ""
extension ComposeViewModel { // }
private func bind() { //
$isCustomEmojiComposing // let pollOptions = pollOptionAttributes.map { $0.option.value }.joined(separator: ",")
.assign(to: \.value, on: customEmojiPickerInputViewModel.isCustomEmojiComposing) // return pollOptions + expiresOption.rawValue
.store(in: &disposeBag) // }
//
$isContentWarningComposing // Publishers.CombineLatest4(
.assign(to: \.isContentWarningComposing, on: composeStatusAttribute) // content,
.store(in: &disposeBag) // attachmentIDs,
// pollOptionsAndDuration,
// bind compose toolbar UI state // $selectedStatusVisibility
Publishers.CombineLatest( // )
$isPollComposing, // .map { content, attachmentIDs, pollOptionsAndDuration, selectedStatusVisibility -> String in
$attachmentServices // var hasher = Hasher()
) // hasher.combine(content)
.receive(on: DispatchQueue.main) // hasher.combine(attachmentIDs)
.sink(receiveValue: { [weak self] isPollComposing, attachmentServices in // hasher.combine(pollOptionsAndDuration)
guard let self = self else { return } // hasher.combine(selectedStatusVisibility.visibility.rawValue)
let shouldMediaDisable = isPollComposing || attachmentServices.count >= self.maxMediaAttachments // let hashValue = hasher.finalize()
let shouldPollDisable = attachmentServices.count > 0 // return "\(hashValue)"
// }
self.isMediaToolbarButtonEnabled = !shouldMediaDisable // .assign(to: \.value, on: idempotencyKey)
self.isPollToolbarButtonEnabled = !shouldPollDisable // .store(in: &disposeBag)
}) //
.store(in: &disposeBag) // // bind modal dismiss state
// composeStatusAttribute.$composeContent
// calculate `Idempotency-Key` // .receive(on: DispatchQueue.main)
let content = Publishers.CombineLatest3( // .map { [weak self] content in
composeStatusAttribute.$isContentWarningComposing, // let content = content ?? ""
composeStatusAttribute.$contentWarningContent, // if content.isEmpty {
composeStatusAttribute.$composeContent // return true
) // }
.map { isContentWarningComposing, contentWarningContent, composeContent -> String in // // if preInsertedContent plus a space is equal to the content, simply dismiss the modal
if isContentWarningComposing { // if let preInsertedContent = self?.preInsertedContent {
return contentWarningContent + (composeContent ?? "") // return content == preInsertedContent
} else { // }
return composeContent ?? "" // return false
} // }
} // .assign(to: &$shouldDismiss)
let attachmentIDs = $attachmentServices.map { attachments -> String in //
let attachmentIDs = attachments.compactMap { $0.attachment.value?.id } // // bind compose bar button item UI state
return attachmentIDs.joined(separator: ",") // let isComposeContentEmpty = composeStatusAttribute.$composeContent
} // .map { ($0 ?? "").isEmpty }
let pollOptionsAndDuration = Publishers.CombineLatest3( // let isComposeContentValid = $characterCount
$isPollComposing, // .compactMap { [weak self] characterCount -> Bool in
$pollOptionAttributes, // guard let self = self else { return characterCount <= 500 }
pollExpiresOptionAttribute.expiresOption // return characterCount <= self.composeContentLimit
) // }
.map { isPollComposing, pollOptionAttributes, expiresOption -> String in // let isMediaEmpty = $attachmentServices
guard isPollComposing else { // .map { $0.isEmpty }
return "" // let isMediaUploadAllSuccess = $attachmentServices
} // .map { services in
// services.allSatisfy { $0.uploadStateMachineSubject.value is MastodonAttachmentService.UploadState.Finish }
let pollOptions = pollOptionAttributes.map { $0.option.value }.joined(separator: ",") // }
return pollOptions + expiresOption.rawValue // let isPollAttributeAllValid = $pollOptionAttributes
} // .map { pollAttributes in
// pollAttributes.allSatisfy { attribute -> Bool in
Publishers.CombineLatest4( // !attribute.option.value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
content, // }
attachmentIDs, // }
pollOptionsAndDuration, //
$selectedStatusVisibility // let isPublishBarButtonItemEnabledPrecondition1 = Publishers.CombineLatest4(
) // isComposeContentEmpty,
.map { content, attachmentIDs, pollOptionsAndDuration, selectedStatusVisibility -> String in // isComposeContentValid,
var hasher = Hasher() // isMediaEmpty,
hasher.combine(content) // isMediaUploadAllSuccess
hasher.combine(attachmentIDs) // )
hasher.combine(pollOptionsAndDuration) // .map { isComposeContentEmpty, isComposeContentValid, isMediaEmpty, isMediaUploadAllSuccess -> Bool in
hasher.combine(selectedStatusVisibility.visibility.rawValue) // if isMediaEmpty {
let hashValue = hasher.finalize() // return isComposeContentValid && !isComposeContentEmpty
return "\(hashValue)" // } else {
} // return isComposeContentValid && isMediaUploadAllSuccess
.assign(to: \.value, on: idempotencyKey) // }
.store(in: &disposeBag) // }
// .eraseToAnyPublisher()
// bind modal dismiss state //
composeStatusAttribute.$composeContent // let isPublishBarButtonItemEnabledPrecondition2 = Publishers.CombineLatest4(
.receive(on: DispatchQueue.main) // isComposeContentEmpty,
.map { [weak self] content in // isComposeContentValid,
let content = content ?? "" // $isPollComposing,
if content.isEmpty { // isPollAttributeAllValid
return true // )
} // .map { isComposeContentEmpty, isComposeContentValid, isPollComposing, isPollAttributeAllValid -> Bool in
// if preInsertedContent plus a space is equal to the content, simply dismiss the modal // if isPollComposing {
if let preInsertedContent = self?.preInsertedContent { // return isComposeContentValid && !isComposeContentEmpty && isPollAttributeAllValid
return content == preInsertedContent // } else {
} // return isComposeContentValid && !isComposeContentEmpty
return false // }
} // }
.assign(to: &$shouldDismiss) // .eraseToAnyPublisher()
//
// bind compose bar button item UI state // Publishers.CombineLatest(
let isComposeContentEmpty = composeStatusAttribute.$composeContent // isPublishBarButtonItemEnabledPrecondition1,
.map { ($0 ?? "").isEmpty } // isPublishBarButtonItemEnabledPrecondition2
let isComposeContentValid = $characterCount // )
.compactMap { [weak self] characterCount -> Bool in // .map { $0 && $1 }
guard let self = self else { return characterCount <= 500 } // .assign(to: &$isPublishBarButtonItemEnabled)
return characterCount <= self.composeContentLimit // }
} //}
let isMediaEmpty = $attachmentServices //
.map { $0.isEmpty } //extension ComposeViewModel {
let isMediaUploadAllSuccess = $attachmentServices // private func bind(
.map { services in // cell: ComposeStatusContentTableViewCell,
services.allSatisfy { $0.uploadStateMachineSubject.value is MastodonAttachmentService.UploadState.Finish } // tableView: UITableView
} // ) {
let isPollAttributeAllValid = $pollOptionAttributes // // bind status content character count
.map { pollAttributes in // Publishers.CombineLatest3(
pollAttributes.allSatisfy { attribute -> Bool in // composeStatusAttribute.$composeContent,
!attribute.option.value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty // composeStatusAttribute.$isContentWarningComposing,
} // composeStatusAttribute.$contentWarningContent
} // )
// .map { composeContent, isContentWarningComposing, contentWarningContent -> Int in
let isPublishBarButtonItemEnabledPrecondition1 = Publishers.CombineLatest4( // let composeContent = composeContent ?? ""
isComposeContentEmpty, // var count = composeContent.count
isComposeContentValid, // if isContentWarningComposing {
isMediaEmpty, // count += contentWarningContent.count
isMediaUploadAllSuccess // }
) // return count
.map { isComposeContentEmpty, isComposeContentValid, isMediaEmpty, isMediaUploadAllSuccess -> Bool in // }
if isMediaEmpty { // .assign(to: &$characterCount)
return isComposeContentValid && !isComposeContentEmpty //
} else { // // bind content warning
return isComposeContentValid && isMediaUploadAllSuccess // composeStatusAttribute.$isContentWarningComposing
} // .receive(on: DispatchQueue.main)
} // .sink { [weak cell, weak tableView] isContentWarningComposing in
.eraseToAnyPublisher() // guard let cell = cell else { return }
// guard let tableView = tableView else { return }
let isPublishBarButtonItemEnabledPrecondition2 = Publishers.CombineLatest4( //
isComposeContentEmpty, // // self size input cell
isComposeContentValid, // cell.statusContentWarningEditorView.isHidden = !isContentWarningComposing
$isPollComposing, // cell.statusContentWarningEditorView.alpha = 0
isPollAttributeAllValid // UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseOut]) {
) // cell.statusContentWarningEditorView.alpha = 1
.map { isComposeContentEmpty, isComposeContentValid, isPollComposing, isPollAttributeAllValid -> Bool in // tableView.beginUpdates()
if isPollComposing { // tableView.endUpdates()
return isComposeContentValid && !isComposeContentEmpty && isPollAttributeAllValid // } completion: { _ in
} else { // // do nothing
return isComposeContentValid && !isComposeContentEmpty // }
} // }
} // .store(in: &disposeBag)
.eraseToAnyPublisher() //
// cell.contentWarningContent
Publishers.CombineLatest( // .removeDuplicates()
isPublishBarButtonItemEnabledPrecondition1, // .receive(on: DispatchQueue.main)
isPublishBarButtonItemEnabledPrecondition2 // .sink { [weak tableView, weak self] text in
) // guard let self = self else { return }
.map { $0 && $1 } // // bind input data
.assign(to: &$isPublishBarButtonItemEnabled) // self.composeStatusAttribute.contentWarningContent = text
} //
} // // self size input cell
// guard let tableView = tableView else { return }
extension ComposeViewModel { // UIView.performWithoutAnimation {
private func bind( // tableView.beginUpdates()
cell: ComposeStatusContentTableViewCell, // tableView.endUpdates()
tableView: UITableView // }
) { // }
// bind status content character count // .store(in: &cell.disposeBag)
Publishers.CombineLatest3( //
composeStatusAttribute.$composeContent, // // configure custom emoji picker
composeStatusAttribute.$isContentWarningComposing, // ComposeStatusSection.configureCustomEmojiPicker(
composeStatusAttribute.$contentWarningContent // viewModel: customEmojiPickerInputViewModel,
) // customEmojiReplaceableTextInput: cell.metaText.textView,
.map { composeContent, isContentWarningComposing, contentWarningContent -> Int in // disposeBag: &disposeBag
let composeContent = composeContent ?? "" // )
var count = composeContent.count // ComposeStatusSection.configureCustomEmojiPicker(
if isContentWarningComposing { // viewModel: customEmojiPickerInputViewModel,
count += contentWarningContent.count // customEmojiReplaceableTextInput: cell.statusContentWarningEditorView.textView,
} // disposeBag: &disposeBag
return count // )
} // }
.assign(to: &$characterCount) //}
//
// bind content warning //extension ComposeViewModel {
composeStatusAttribute.$isContentWarningComposing // private func bind(
.receive(on: DispatchQueue.main) // cell: ComposeStatusPollTableViewCell,
.sink { [weak cell, weak tableView] isContentWarningComposing in // tableView: UITableView
guard let cell = cell else { return } // ) {
guard let tableView = tableView else { return } // Publishers.CombineLatest(
// $isPollComposing,
// self size input cell // $pollOptionAttributes
cell.statusContentWarningEditorView.isHidden = !isContentWarningComposing // )
cell.statusContentWarningEditorView.alpha = 0 // .receive(on: DispatchQueue.main)
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseOut]) { // .sink { [weak self] isPollComposing, pollOptionAttributes in
cell.statusContentWarningEditorView.alpha = 1 // guard let self = self else { return }
tableView.beginUpdates() // guard self.isViewAppeared else { return }
tableView.endUpdates() //
} completion: { _ in // let cell = self.composeStatusPollTableViewCell
// do nothing // guard let dataSource = cell.dataSource else { return }
} //
} // var snapshot = NSDiffableDataSourceSnapshot<ComposeStatusPollSection, ComposeStatusPollItem>()
.store(in: &disposeBag) // snapshot.appendSections([.main])
// var items: [ComposeStatusPollItem] = []
cell.contentWarningContent // if isPollComposing {
.removeDuplicates() // for attribute in pollOptionAttributes {
.receive(on: DispatchQueue.main) // items.append(.pollOption(attribute: attribute))
.sink { [weak tableView, weak self] text in // }
guard let self = self else { return } // if pollOptionAttributes.count < self.maxPollOptions {
// bind input data // items.append(.pollOptionAppendEntry)
self.composeStatusAttribute.contentWarningContent = text // }
// items.append(.pollExpiresOption(attribute: self.pollExpiresOptionAttribute))
// self size input cell // }
guard let tableView = tableView else { return } // snapshot.appendItems(items, toSection: .main)
UIView.performWithoutAnimation { //
tableView.beginUpdates() // tableView.performBatchUpdates {
tableView.endUpdates() // if #available(iOS 15.0, *) {
} // dataSource.apply(snapshot, animatingDifferences: false)
} // } else {
.store(in: &cell.disposeBag) // dataSource.apply(snapshot, animatingDifferences: true)
// }
// configure custom emoji picker // }
ComposeStatusSection.configureCustomEmojiPicker( // }
viewModel: customEmojiPickerInputViewModel, // .store(in: &disposeBag)
customEmojiReplaceableTextInput: cell.metaText.textView, //
disposeBag: &disposeBag // // bind delegate
) // $pollOptionAttributes
ComposeStatusSection.configureCustomEmojiPicker( // .sink { [weak self] pollAttributes in
viewModel: customEmojiPickerInputViewModel, // guard let self = self else { return }
customEmojiReplaceableTextInput: cell.statusContentWarningEditorView.textView, // pollAttributes.forEach { $0.delegate = self }
disposeBag: &disposeBag // }
) // .store(in: &disposeBag)
} // }
} //}
//
extension ComposeViewModel { //extension ComposeViewModel {
private func bind( // private func bind(
cell: ComposeStatusPollTableViewCell, // cell: ComposeStatusAttachmentTableViewCell,
tableView: UITableView // tableView: UITableView
) { // ) {
Publishers.CombineLatest( // cell.collectionViewHeightDidUpdate
$isPollComposing, // .receive(on: DispatchQueue.main)
$pollOptionAttributes // .sink { [weak self] _ in
) // guard let _ = self else { return }
.receive(on: DispatchQueue.main) // tableView.beginUpdates()
.sink { [weak self] isPollComposing, pollOptionAttributes in // tableView.endUpdates()
guard let self = self else { return } // }
guard self.isViewAppeared else { return } // .store(in: &disposeBag)
//
let cell = self.composeStatusPollTableViewCell // $attachmentServices
guard let dataSource = cell.dataSource else { return } // .removeDuplicates()
// .receive(on: DispatchQueue.main)
var snapshot = NSDiffableDataSourceSnapshot<ComposeStatusPollSection, ComposeStatusPollItem>() // .sink { [weak self] attachmentServices in
snapshot.appendSections([.main]) // guard let self = self else { return }
var items: [ComposeStatusPollItem] = [] // guard self.isViewAppeared else { return }
if isPollComposing { //
for attribute in pollOptionAttributes { // let cell = self.composeStatusAttachmentTableViewCell
items.append(.pollOption(attribute: attribute)) // guard let dataSource = cell.dataSource else { return }
} //
if pollOptionAttributes.count < self.maxPollOptions { // var snapshot = NSDiffableDataSourceSnapshot<ComposeStatusAttachmentSection, ComposeStatusAttachmentItem>()
items.append(.pollOptionAppendEntry) // snapshot.appendSections([.main])
} // let items = attachmentServices.map { ComposeStatusAttachmentItem.attachment(attachmentService: $0) }
items.append(.pollExpiresOption(attribute: self.pollExpiresOptionAttribute)) // snapshot.appendItems(items, toSection: .main)
} //
snapshot.appendItems(items, toSection: .main) // if #available(iOS 15.0, *) {
// dataSource.applySnapshotUsingReloadData(snapshot)
tableView.performBatchUpdates { // } else {
if #available(iOS 15.0, *) { // dataSource.apply(snapshot, animatingDifferences: false)
dataSource.apply(snapshot, animatingDifferences: false) // }
} else { // }
dataSource.apply(snapshot, animatingDifferences: true) // .store(in: &disposeBag)
} //
} // // setup attribute updater
} // $attachmentServices
.store(in: &disposeBag) // .receive(on: DispatchQueue.main)
// .debounce(for: 0.3, scheduler: DispatchQueue.main)
// bind delegate // .sink { attachmentServices in
$pollOptionAttributes // // drive service upload state
.sink { [weak self] pollAttributes in // // make image upload in the queue
guard let self = self else { return } // for attachmentService in attachmentServices {
pollAttributes.forEach { $0.delegate = self } // // skip when prefix N task when task finish OR fail OR uploading
} // guard let currentState = attachmentService.uploadStateMachine.currentState else { break }
.store(in: &disposeBag) // if currentState is MastodonAttachmentService.UploadState.Fail {
} // continue
} // }
// if currentState is MastodonAttachmentService.UploadState.Finish {
extension ComposeViewModel { // continue
private func bind( // }
cell: ComposeStatusAttachmentTableViewCell, // if currentState is MastodonAttachmentService.UploadState.Processing {
tableView: UITableView // continue
) { // }
cell.collectionViewHeightDidUpdate // if currentState is MastodonAttachmentService.UploadState.Uploading {
.receive(on: DispatchQueue.main) // break
.sink { [weak self] _ in // }
guard let _ = self else { return } // // trigger uploading one by one
tableView.beginUpdates() // if currentState is MastodonAttachmentService.UploadState.Initial {
tableView.endUpdates() // attachmentService.uploadStateMachine.enter(MastodonAttachmentService.UploadState.Uploading.self)
} // break
.store(in: &disposeBag) // }
// }
$attachmentServices // }
.removeDuplicates() // .store(in: &disposeBag)
.receive(on: DispatchQueue.main) //
.sink { [weak self] attachmentServices in // // bind delegate
guard let self = self else { return } // $attachmentServices
guard self.isViewAppeared else { return } // .sink { [weak self] attachmentServices in
// guard let self = self else { return }
let cell = self.composeStatusAttachmentTableViewCell // attachmentServices.forEach { $0.delegate = self }
guard let dataSource = cell.dataSource else { return } // }
// .store(in: &disposeBag)
var snapshot = NSDiffableDataSourceSnapshot<ComposeStatusAttachmentSection, ComposeStatusAttachmentItem>() // }
snapshot.appendSections([.main]) //}
let items = attachmentServices.map { ComposeStatusAttachmentItem.attachment(attachmentService: $0) }
snapshot.appendItems(items, toSection: .main)
if #available(iOS 15.0, *) {
dataSource.applySnapshotUsingReloadData(snapshot)
} else {
dataSource.apply(snapshot, animatingDifferences: false)
}
}
.store(in: &disposeBag)
// setup attribute updater
$attachmentServices
.receive(on: DispatchQueue.main)
.debounce(for: 0.3, scheduler: DispatchQueue.main)
.sink { attachmentServices in
// drive service upload state
// make image upload in the queue
for attachmentService in attachmentServices {
// skip when prefix N task when task finish OR fail OR uploading
guard let currentState = attachmentService.uploadStateMachine.currentState else { break }
if currentState is MastodonAttachmentService.UploadState.Fail {
continue
}
if currentState is MastodonAttachmentService.UploadState.Finish {
continue
}
if currentState is MastodonAttachmentService.UploadState.Processing {
continue
}
if currentState is MastodonAttachmentService.UploadState.Uploading {
break
}
// trigger uploading one by one
if currentState is MastodonAttachmentService.UploadState.Initial {
attachmentService.uploadStateMachine.enter(MastodonAttachmentService.UploadState.Uploading.self)
break
}
}
}
.store(in: &disposeBag)
// bind delegate
$attachmentServices
.sink { [weak self] attachmentServices in
guard let self = self else { return }
attachmentServices.forEach { $0.delegate = self }
}
.store(in: &disposeBag)
}
}

View File

@ -12,153 +12,153 @@ import CoreDataStack
import GameplayKit import GameplayKit
import MastodonSDK import MastodonSDK
extension ComposeViewModel { //extension ComposeViewModel {
class PublishState: GKState { // class PublishState: GKState {
weak var viewModel: ComposeViewModel? // weak var viewModel: ComposeViewModel?
//
init(viewModel: ComposeViewModel) { // init(viewModel: ComposeViewModel) {
self.viewModel = viewModel // self.viewModel = viewModel
} // }
//
override func didEnter(from previousState: GKState?) { // override func didEnter(from previousState: GKState?) {
os_log("%{public}s[%{public}ld], %{public}s: enter %s, previous: %s", ((#file as NSString).lastPathComponent), #line, #function, self.debugDescription, previousState.debugDescription) // os_log("%{public}s[%{public}ld], %{public}s: enter %s, previous: %s", ((#file as NSString).lastPathComponent), #line, #function, self.debugDescription, previousState.debugDescription)
viewModel?.publishStateMachinePublisher.value = self // viewModel?.publishStateMachinePublisher.value = self
} // }
} // }
} //}
extension ComposeViewModel.PublishState { //extension ComposeViewModel.PublishState {
class Initial: ComposeViewModel.PublishState { // class Initial: ComposeViewModel.PublishState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool { // override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass == Publishing.self // return stateClass == Publishing.self
} // }
} // }
//
class Publishing: ComposeViewModel.PublishState { // class Publishing: ComposeViewModel.PublishState {
//
var publishingSubscription: AnyCancellable? // var publishingSubscription: AnyCancellable?
//
override func isValidNextState(_ stateClass: AnyClass) -> Bool { // override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass == Fail.self || stateClass == Finish.self // return stateClass == Fail.self || stateClass == Finish.self
} // }
//
override func didEnter(from previousState: GKState?) { // override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) // super.didEnter(from: previousState)
guard let viewModel = viewModel, let stateMachine = stateMachine else { return } // guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
//
viewModel.updatePublishDate() // viewModel.updatePublishDate()
//
let authenticationBox = viewModel.authenticationBox // let authenticationBox = viewModel.authenticationBox
let domain = authenticationBox.domain // let domain = authenticationBox.domain
let attachmentServices = viewModel.attachmentServices // let attachmentServices = viewModel.attachmentServices
let mediaIDs = attachmentServices.compactMap { attachmentService in // let mediaIDs = attachmentServices.compactMap { attachmentService in
attachmentService.attachment.value?.id // attachmentService.attachment.value?.id
} // }
let pollOptions: [String]? = { // let pollOptions: [String]? = {
guard viewModel.isPollComposing else { return nil } // guard viewModel.isPollComposing else { return nil }
return viewModel.pollOptionAttributes.map { attribute in attribute.option.value } // return viewModel.pollOptionAttributes.map { attribute in attribute.option.value }
}() // }()
let pollExpiresIn: Int? = { // let pollExpiresIn: Int? = {
guard viewModel.isPollComposing else { return nil } // guard viewModel.isPollComposing else { return nil }
return viewModel.pollExpiresOptionAttribute.expiresOption.value.seconds // return viewModel.pollExpiresOptionAttribute.expiresOption.value.seconds
}() // }()
let inReplyToID: Mastodon.Entity.Status.ID? = { // let inReplyToID: Mastodon.Entity.Status.ID? = {
guard case let .reply(status) = viewModel.composeKind else { return nil } // guard case let .reply(status) = viewModel.composeKind else { return nil }
var id: Mastodon.Entity.Status.ID? // var id: Mastodon.Entity.Status.ID?
viewModel.context.managedObjectContext.performAndWait { // viewModel.context.managedObjectContext.performAndWait {
guard let replyTo = status.object(in: viewModel.context.managedObjectContext) else { return } // guard let replyTo = status.object(in: viewModel.context.managedObjectContext) else { return }
id = replyTo.id // id = replyTo.id
} // }
return id // return id
}() // }()
let sensitive: Bool = viewModel.isContentWarningComposing // let sensitive: Bool = viewModel.isContentWarningComposing
let spoilerText: String? = { // let spoilerText: String? = {
let text = viewModel.composeStatusAttribute.contentWarningContent.trimmingCharacters(in: .whitespacesAndNewlines) // let text = viewModel.composeStatusAttribute.contentWarningContent.trimmingCharacters(in: .whitespacesAndNewlines)
guard !text.isEmpty else { // guard !text.isEmpty else {
return nil // return nil
} // }
return text // return text
}() // }()
let visibility = viewModel.selectedStatusVisibility.visibility // let visibility = viewModel.selectedStatusVisibility.visibility
//
let updateMediaQuerySubscriptions: [AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error>] = { // let updateMediaQuerySubscriptions: [AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error>] = {
var subscriptions: [AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error>] = [] // var subscriptions: [AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error>] = []
for attachmentService in attachmentServices { // for attachmentService in attachmentServices {
guard let attachmentID = attachmentService.attachment.value?.id else { continue } // guard let attachmentID = attachmentService.attachment.value?.id else { continue }
let description = attachmentService.description.value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" // let description = attachmentService.description.value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
guard !description.isEmpty else { continue } // guard !description.isEmpty else { continue }
let query = Mastodon.API.Media.UpdateMediaQuery( // let query = Mastodon.API.Media.UpdateMediaQuery(
file: nil, // file: nil,
thumbnail: nil, // thumbnail: nil,
description: description, // description: description,
focus: nil // focus: nil
) // )
let subscription = viewModel.context.apiService.updateMedia( // let subscription = viewModel.context.apiService.updateMedia(
domain: domain, // domain: domain,
attachmentID: attachmentID, // attachmentID: attachmentID,
query: query, // query: query,
mastodonAuthenticationBox: authenticationBox // mastodonAuthenticationBox: authenticationBox
) // )
subscriptions.append(subscription) // subscriptions.append(subscription)
} // }
return subscriptions // return subscriptions
}() // }()
//
let idempotencyKey = viewModel.idempotencyKey.value // let idempotencyKey = viewModel.idempotencyKey.value
//
publishingSubscription = Publishers.MergeMany(updateMediaQuerySubscriptions) // publishingSubscription = Publishers.MergeMany(updateMediaQuerySubscriptions)
.collect() // .collect()
.asyncMap { attachments -> Mastodon.Response.Content<Mastodon.Entity.Status> in // .asyncMap { attachments -> Mastodon.Response.Content<Mastodon.Entity.Status> in
let query = Mastodon.API.Statuses.PublishStatusQuery( // let query = Mastodon.API.Statuses.PublishStatusQuery(
status: viewModel.composeStatusAttribute.composeContent, // status: viewModel.composeStatusAttribute.composeContent,
mediaIDs: mediaIDs.isEmpty ? nil : mediaIDs, // mediaIDs: mediaIDs.isEmpty ? nil : mediaIDs,
pollOptions: pollOptions, // pollOptions: pollOptions,
pollExpiresIn: pollExpiresIn, // pollExpiresIn: pollExpiresIn,
inReplyToID: inReplyToID, // inReplyToID: inReplyToID,
sensitive: sensitive, // sensitive: sensitive,
spoilerText: spoilerText, // spoilerText: spoilerText,
visibility: visibility // visibility: visibility
) // )
return try await viewModel.context.apiService.publishStatus( // return try await viewModel.context.apiService.publishStatus(
domain: domain, // domain: domain,
idempotencyKey: idempotencyKey, // idempotencyKey: idempotencyKey,
query: query, // query: query,
authenticationBox: authenticationBox // authenticationBox: authenticationBox
) // )
} // }
.receive(on: DispatchQueue.main) // .receive(on: DispatchQueue.main)
.sink { completion in // .sink { completion in
switch completion { // switch completion {
case .failure(let error): // case .failure(let error):
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: publish status %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription) // os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: publish status %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
stateMachine.enter(Fail.self) // stateMachine.enter(Fail.self)
case .finished: // case .finished:
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: publish status success", ((#file as NSString).lastPathComponent), #line, #function) // os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: publish status success", ((#file as NSString).lastPathComponent), #line, #function)
stateMachine.enter(Finish.self) // stateMachine.enter(Finish.self)
} // }
} receiveValue: { response in // } receiveValue: { response in
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: status %s published: %s", ((#file as NSString).lastPathComponent), #line, #function, response.value.id, response.value.uri) // os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: status %s published: %s", ((#file as NSString).lastPathComponent), #line, #function, response.value.id, response.value.uri)
} // }
} // }
} // }
//
class Fail: ComposeViewModel.PublishState { // class Fail: ComposeViewModel.PublishState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool { // override func isValidNextState(_ stateClass: AnyClass) -> Bool {
// allow discard publishing // // allow discard publishing
return stateClass == Publishing.self || stateClass == Discard.self // return stateClass == Publishing.self || stateClass == Discard.self
} // }
} // }
//
class Discard: ComposeViewModel.PublishState { // class Discard: ComposeViewModel.PublishState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool { // override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return false // return false
} // }
} // }
//
class Finish: ComposeViewModel.PublishState { // class Finish: ComposeViewModel.PublishState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool { // override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return false // return false
} // }
} // }
//
} //}

View File

@ -28,159 +28,159 @@ final class ComposeViewModel: NSObject {
// input // input
let context: AppContext let context: AppContext
let composeKind: ComposeStatusSection.ComposeKind
let authContext: AuthContext let authContext: AuthContext
let kind: ComposeContentViewModel.Kind
var authenticationBox: MastodonAuthenticationBox { // var authenticationBox: MastodonAuthenticationBox {
authContext.mastodonAuthenticationBox // authContext.mastodonAuthenticationBox
} // }
//
@Published var isPollComposing = false // @Published var isPollComposing = false
@Published var isCustomEmojiComposing = false // @Published var isCustomEmojiComposing = false
@Published var isContentWarningComposing = false // @Published var isContentWarningComposing = false
//
@Published var selectedStatusVisibility: ComposeToolbarView.VisibilitySelectionType // @Published var selectedStatusVisibility: ComposeToolbarView.VisibilitySelectionType
@Published var repliedToCellFrame: CGRect = .zero // @Published var repliedToCellFrame: CGRect = .zero
@Published var autoCompleteRetryLayoutTimes = 0 // @Published var autoCompleteRetryLayoutTimes = 0
@Published var autoCompleteInfo: ComposeViewController.AutoCompleteInfo? = nil // @Published var autoCompleteInfo: ComposeViewController.AutoCompleteInfo? = nil
let traitCollectionDidChangePublisher = CurrentValueSubject<Void, Never>(Void()) // use CurrentValueSubject to make initial event emit // let traitCollectionDidChangePublisher = CurrentValueSubject<Void, Never>(Void()) // use CurrentValueSubject to make initial event emit
var isViewAppeared = false // var isViewAppeared = false
// output // output
let instanceConfiguration: Mastodon.Entity.Instance.Configuration? // let instanceConfiguration: Mastodon.Entity.Instance.Configuration?
var composeContentLimit: Int { // var composeContentLimit: Int {
guard let maxCharacters = instanceConfiguration?.statuses?.maxCharacters else { return 500 } // guard let maxCharacters = instanceConfiguration?.statuses?.maxCharacters else { return 500 }
return max(1, maxCharacters) // return max(1, maxCharacters)
} // }
var maxMediaAttachments: Int { // var maxMediaAttachments: Int {
guard let maxMediaAttachments = instanceConfiguration?.statuses?.maxMediaAttachments else { // guard let maxMediaAttachments = instanceConfiguration?.statuses?.maxMediaAttachments else {
return 4 // return 4
} // }
// FIXME: update timeline media preview UI // // FIXME: update timeline media preview UI
return min(4, max(1, maxMediaAttachments)) // return min(4, max(1, maxMediaAttachments))
// return max(1, maxMediaAttachments) // // return max(1, maxMediaAttachments)
} // }
var maxPollOptions: Int { // var maxPollOptions: Int {
guard let maxOptions = instanceConfiguration?.polls?.maxOptions else { return 4 } // guard let maxOptions = instanceConfiguration?.polls?.maxOptions else { return 4 }
return max(2, maxOptions) // return max(2, maxOptions)
} // }
//
let composeStatusAttribute = ComposeStatusItem.ComposeStatusAttribute() // let composeStatusAttribute = ComposeStatusItem.ComposeStatusAttribute()
let composeStatusContentTableViewCell = ComposeStatusContentTableViewCell() // let composeStatusContentTableViewCell = ComposeStatusContentTableViewCell()
let composeStatusAttachmentTableViewCell = ComposeStatusAttachmentTableViewCell() // let composeStatusAttachmentTableViewCell = ComposeStatusAttachmentTableViewCell()
let composeStatusPollTableViewCell = ComposeStatusPollTableViewCell() // let composeStatusPollTableViewCell = ComposeStatusPollTableViewCell()
//
// var dataSource: UITableViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>? // // var dataSource: UITableViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>?
var customEmojiPickerDiffableDataSource: UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem>? // var customEmojiPickerDiffableDataSource: UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem>?
private(set) lazy var publishStateMachine: GKStateMachine = { // private(set) lazy var publishStateMachine: GKStateMachine = {
// exclude timeline middle fetcher state // // exclude timeline middle fetcher state
let stateMachine = GKStateMachine(states: [ // let stateMachine = GKStateMachine(states: [
PublishState.Initial(viewModel: self), // PublishState.Initial(viewModel: self),
PublishState.Publishing(viewModel: self), // PublishState.Publishing(viewModel: self),
PublishState.Fail(viewModel: self), // PublishState.Fail(viewModel: self),
PublishState.Discard(viewModel: self), // PublishState.Discard(viewModel: self),
PublishState.Finish(viewModel: self), // PublishState.Finish(viewModel: self),
]) // ])
stateMachine.enter(PublishState.Initial.self) // stateMachine.enter(PublishState.Initial.self)
return stateMachine // return stateMachine
}() // }()
private(set) lazy var publishStateMachinePublisher = CurrentValueSubject<PublishState?, Never>(nil) // private(set) lazy var publishStateMachinePublisher = CurrentValueSubject<PublishState?, Never>(nil)
private(set) var publishDate = Date() // update it when enter Publishing state // private(set) var publishDate = Date() // update it when enter Publishing state
//
// TODO: group post material into Hashable class // // TODO: group post material into Hashable class
var idempotencyKey = CurrentValueSubject<String, Never>(UUID().uuidString) // var idempotencyKey = CurrentValueSubject<String, Never>(UUID().uuidString)
//
// UI & UX // // UI & UX
@Published var title: String // @Published var title: String
@Published var shouldDismiss = true // @Published var shouldDismiss = true
@Published var isPublishBarButtonItemEnabled = false // @Published var isPublishBarButtonItemEnabled = false
@Published var isMediaToolbarButtonEnabled = true // @Published var isMediaToolbarButtonEnabled = true
@Published var isPollToolbarButtonEnabled = true // @Published var isPollToolbarButtonEnabled = true
@Published var characterCount = 0 // @Published var characterCount = 0
@Published var collectionViewState: CollectionViewState = .fold // @Published var collectionViewState: CollectionViewState = .fold
//
// for hashtag: "#<hashtag> " // // for hashtag: "#<hashtag> "
// for mention: "@<mention> " // // for mention: "@<mention> "
var preInsertedContent: String? // var preInsertedContent: String?
//
// custom emojis // // custom emojis
let customEmojiViewModel: EmojiService.CustomEmojiViewModel? // let customEmojiViewModel: EmojiService.CustomEmojiViewModel?
let customEmojiPickerInputViewModel = CustomEmojiPickerInputViewModel() // let customEmojiPickerInputViewModel = CustomEmojiPickerInputViewModel()
@Published var isLoadingCustomEmoji = false // @Published var isLoadingCustomEmoji = false
//
// attachment // // attachment
@Published var attachmentServices: [MastodonAttachmentService] = [] // @Published var attachmentServices: [MastodonAttachmentService] = []
//
// polls // // polls
@Published var pollOptionAttributes: [ComposeStatusPollItem.PollOptionAttribute] = [] // @Published var pollOptionAttributes: [ComposeStatusPollItem.PollOptionAttribute] = []
let pollExpiresOptionAttribute = ComposeStatusPollItem.PollExpiresOptionAttribute() // let pollExpiresOptionAttribute = ComposeStatusPollItem.PollExpiresOptionAttribute()
init( init(
context: AppContext, context: AppContext,
composeKind: ComposeStatusSection.ComposeKind, authContext: AuthContext,
authContext: AuthContext kind: ComposeContentViewModel.Kind
) { ) {
self.context = context self.context = context
self.composeKind = composeKind
self.authContext = authContext self.authContext = authContext
self.kind = kind
self.title = { // self.title = {
switch composeKind { // switch composeKind {
case .post, .hashtag, .mention: return L10n.Scene.Compose.Title.newPost // case .post, .hashtag, .mention: return L10n.Scene.Compose.Title.newPost
case .reply: return L10n.Scene.Compose.Title.newReply // case .reply: return L10n.Scene.Compose.Title.newReply
} // }
}() // }()
self.selectedStatusVisibility = { // self.selectedStatusVisibility = {
// default private when user locked // // default private when user locked
var visibility: ComposeToolbarView.VisibilitySelectionType = { // var visibility: ComposeToolbarView.VisibilitySelectionType = {
guard let author = authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext)?.user // guard let author = authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext)?.user
else { // else {
return .public // return .public
} // }
return author.locked ? .private : .public // return author.locked ? .private : .public
}() // }()
// set visibility for reply post // // set visibility for reply post
switch composeKind { // switch composeKind {
case .reply(let record): // case .reply(let record):
context.managedObjectContext.performAndWait { // context.managedObjectContext.performAndWait {
guard let status = record.object(in: context.managedObjectContext) else { // guard let status = record.object(in: context.managedObjectContext) else {
assertionFailure() // assertionFailure()
return // return
} // }
let repliedStatusVisibility = status.visibility // let repliedStatusVisibility = status.visibility
switch repliedStatusVisibility { // switch repliedStatusVisibility {
case .public, .unlisted: // case .public, .unlisted:
// keep default // // keep default
break // break
case .private: // case .private:
visibility = .private // visibility = .private
case .direct: // case .direct:
visibility = .direct // visibility = .direct
case ._other: // case ._other:
assertionFailure() // assertionFailure()
break // break
} // }
} // }
default: // default:
break // break
} // }
return visibility // return visibility
}() // }()
// set limit // // set limit
self.instanceConfiguration = { // self.instanceConfiguration = {
var configuration: Mastodon.Entity.Instance.Configuration? = nil // var configuration: Mastodon.Entity.Instance.Configuration? = nil
context.managedObjectContext.performAndWait { // context.managedObjectContext.performAndWait {
guard let authentication = authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext) else { return } // guard let authentication = authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext) else { return }
configuration = authentication.instance?.configuration // configuration = authentication.instance?.configuration
} // }
return configuration // return configuration
}() // }()
self.customEmojiViewModel = context.emojiService.dequeueCustomEmojiViewModel(for: authContext.mastodonAuthenticationBox.domain) // self.customEmojiViewModel = context.emojiService.dequeueCustomEmojiViewModel(for: authContext.mastodonAuthenticationBox.domain)
super.init() // super.init()
// end init // // end init
//
setup(cell: composeStatusContentTableViewCell) // setup(cell: composeStatusContentTableViewCell)
} }
deinit { deinit {
@ -190,199 +190,192 @@ final class ComposeViewModel: NSObject {
} }
extension ComposeViewModel { extension ComposeViewModel {
enum CollectionViewState { // func createNewPollOptionIfPossible() {
case fold // snap to input // guard pollOptionAttributes.count < maxPollOptions else { return }
case expand // snap to reply //
} // let attribute = ComposeStatusPollItem.PollOptionAttribute()
// pollOptionAttributes = pollOptionAttributes + [attribute]
// }
//
// func updatePublishDate() {
// publishDate = Date()
// }
} }
extension ComposeViewModel { //extension ComposeViewModel {
func createNewPollOptionIfPossible() { //
guard pollOptionAttributes.count < maxPollOptions else { return } // enum AttachmentPrecondition: Error, LocalizedError {
// case videoAttachWithPhoto
let attribute = ComposeStatusPollItem.PollOptionAttribute() // case moreThanOneVideo
pollOptionAttributes = pollOptionAttributes + [attribute] //
} // var errorDescription: String? {
// return L10n.Common.Alerts.PublishPostFailure.title
func updatePublishDate() { // }
publishDate = Date() //
} // var failureReason: String? {
} // switch self {
// case .videoAttachWithPhoto:
extension ComposeViewModel { // return L10n.Common.Alerts.PublishPostFailure.AttachmentsMessage.videoAttachWithPhoto
// case .moreThanOneVideo:
enum AttachmentPrecondition: Error, LocalizedError { // return L10n.Common.Alerts.PublishPostFailure.AttachmentsMessage.moreThanOneVideo
case videoAttachWithPhoto // }
case moreThanOneVideo // }
// }
var errorDescription: String? { //
return L10n.Common.Alerts.PublishPostFailure.title // // check exclusive limit:
} // // - up to 1 video
// // - up to N photos
var failureReason: String? { // func checkAttachmentPrecondition() throws {
switch self { // let attachmentServices = self.attachmentServices
case .videoAttachWithPhoto: // guard !attachmentServices.isEmpty else { return }
return L10n.Common.Alerts.PublishPostFailure.AttachmentsMessage.videoAttachWithPhoto // var photoAttachmentServices: [MastodonAttachmentService] = []
case .moreThanOneVideo: // var videoAttachmentServices: [MastodonAttachmentService] = []
return L10n.Common.Alerts.PublishPostFailure.AttachmentsMessage.moreThanOneVideo // attachmentServices.forEach { service in
} // guard let file = service.file.value else {
} // assertionFailure()
} // return
// }
// check exclusive limit: // switch file {
// - up to 1 video // case .jpeg, .png, .gif:
// - up to N photos // photoAttachmentServices.append(service)
func checkAttachmentPrecondition() throws { // case .other:
let attachmentServices = self.attachmentServices // videoAttachmentServices.append(service)
guard !attachmentServices.isEmpty else { return } // }
var photoAttachmentServices: [MastodonAttachmentService] = [] // }
var videoAttachmentServices: [MastodonAttachmentService] = [] //
attachmentServices.forEach { service in // if !videoAttachmentServices.isEmpty {
guard let file = service.file.value else { // guard videoAttachmentServices.count == 1 else {
assertionFailure() // throw AttachmentPrecondition.moreThanOneVideo
return // }
} // guard photoAttachmentServices.isEmpty else {
switch file { // throw AttachmentPrecondition.videoAttachWithPhoto
case .jpeg, .png, .gif: // }
photoAttachmentServices.append(service) // }
case .other: // }
videoAttachmentServices.append(service) //
} //}
} //
//// MARK: - MastodonAttachmentServiceDelegate
if !videoAttachmentServices.isEmpty { //extension ComposeViewModel: MastodonAttachmentServiceDelegate {
guard videoAttachmentServices.count == 1 else { // func mastodonAttachmentService(_ service: MastodonAttachmentService, uploadStateDidChange state: MastodonAttachmentService.UploadState?) {
throw AttachmentPrecondition.moreThanOneVideo // // trigger new output event
} // attachmentServices = attachmentServices
guard photoAttachmentServices.isEmpty else { // }
throw AttachmentPrecondition.videoAttachWithPhoto //}
} //
} //// MARK: - ComposePollAttributeDelegate
} //extension ComposeViewModel: ComposePollAttributeDelegate {
// func composePollAttribute(_ attribute: ComposeStatusPollItem.PollOptionAttribute, pollOptionDidChange: String?) {
} // // trigger update
// pollOptionAttributes = pollOptionAttributes
// MARK: - MastodonAttachmentServiceDelegate // }
extension ComposeViewModel: MastodonAttachmentServiceDelegate { //}
func mastodonAttachmentService(_ service: MastodonAttachmentService, uploadStateDidChange state: MastodonAttachmentService.UploadState?) { //
// trigger new output event //extension ComposeViewModel {
attachmentServices = attachmentServices // private func setup(
} // cell: ComposeStatusContentTableViewCell
} // ) {
// setupStatusHeader(cell: cell)
// MARK: - ComposePollAttributeDelegate // setupStatusAuthor(cell: cell)
extension ComposeViewModel: ComposePollAttributeDelegate { // setupStatusContent(cell: cell)
func composePollAttribute(_ attribute: ComposeStatusPollItem.PollOptionAttribute, pollOptionDidChange: String?) { // }
// trigger update //
pollOptionAttributes = pollOptionAttributes // private func setupStatusHeader(
} // cell: ComposeStatusContentTableViewCell
} // ) {
// // configure header
extension ComposeViewModel { // let managedObjectContext = context.managedObjectContext
private func setup( // managedObjectContext.performAndWait {
cell: ComposeStatusContentTableViewCell // guard case let .reply(record) = self.composeKind,
) { // let replyTo = record.object(in: managedObjectContext)
setupStatusHeader(cell: cell) // else {
setupStatusAuthor(cell: cell) // cell.statusView.viewModel.header = .none
setupStatusContent(cell: cell) // return
} // }
//
private func setupStatusHeader( // let info: StatusView.ViewModel.Header.ReplyInfo
cell: ComposeStatusContentTableViewCell // do {
) { // let content = MastodonContent(
// configure header // content: replyTo.author.displayNameWithFallback,
let managedObjectContext = context.managedObjectContext // emojis: replyTo.author.emojis.asDictionary
managedObjectContext.performAndWait { // )
guard case let .reply(record) = self.composeKind, // let metaContent = try MastodonMetaContent.convert(document: content)
let replyTo = record.object(in: managedObjectContext) // info = .init(header: metaContent)
else { // } catch {
cell.statusView.viewModel.header = .none // let metaContent = PlaintextMetaContent(string: replyTo.author.displayNameWithFallback)
return // info = .init(header: metaContent)
} // }
// cell.statusView.viewModel.header = .reply(info: info)
let info: StatusView.ViewModel.Header.ReplyInfo // }
do { // }
let content = MastodonContent( //
content: replyTo.author.displayNameWithFallback, // private func setupStatusAuthor(
emojis: replyTo.author.emojis.asDictionary // cell: ComposeStatusContentTableViewCell
) // ) {
let metaContent = try MastodonMetaContent.convert(document: content) // self.context.managedObjectContext.performAndWait {
info = .init(header: metaContent) // guard let author = authenticationBox.authenticationRecord.object(in: self.context.managedObjectContext)?.user else { return }
} catch { // cell.statusView.configureAuthor(author: author)
let metaContent = PlaintextMetaContent(string: replyTo.author.displayNameWithFallback) // }
info = .init(header: metaContent) // }
} //
cell.statusView.viewModel.header = .reply(info: info) // private func setupStatusContent(
} // cell: ComposeStatusContentTableViewCell
} // ) {
// switch composeKind {
private func setupStatusAuthor( // case .reply(let record):
cell: ComposeStatusContentTableViewCell // context.managedObjectContext.performAndWait {
) { // guard let status = record.object(in: context.managedObjectContext) else { return }
self.context.managedObjectContext.performAndWait { // let author = self.authenticationBox.authenticationRecord.object(in: context.managedObjectContext)?.user
guard let author = authenticationBox.authenticationRecord.object(in: self.context.managedObjectContext)?.user else { return } //
cell.statusView.configureAuthor(author: author) // var mentionAccts: [String] = []
} // if author?.id != status.author.id {
} // mentionAccts.append("@" + status.author.acct)
// }
private func setupStatusContent( // let mentions = status.mentions
cell: ComposeStatusContentTableViewCell // .filter { author?.id != $0.id }
) { // for mention in mentions {
switch composeKind { // let acct = "@" + mention.acct
case .reply(let record): // guard !mentionAccts.contains(acct) else { continue }
context.managedObjectContext.performAndWait { // mentionAccts.append(acct)
guard let status = record.object(in: context.managedObjectContext) else { return } // }
let author = self.authenticationBox.authenticationRecord.object(in: context.managedObjectContext)?.user // for acct in mentionAccts {
// UITextChecker.learnWord(acct)
var mentionAccts: [String] = [] // }
if author?.id != status.author.id { // if let spoilerText = status.spoilerText, !spoilerText.isEmpty {
mentionAccts.append("@" + status.author.acct) // self.isContentWarningComposing = true
} // self.composeStatusAttribute.contentWarningContent = spoilerText
let mentions = status.mentions // }
.filter { author?.id != $0.id } //
for mention in mentions { // let initialComposeContent = mentionAccts.joined(separator: " ")
let acct = "@" + mention.acct // let preInsertedContent: String? = initialComposeContent.isEmpty ? nil : initialComposeContent + " "
guard !mentionAccts.contains(acct) else { continue } // self.preInsertedContent = preInsertedContent
mentionAccts.append(acct) // self.composeStatusAttribute.composeContent = preInsertedContent
} // }
for acct in mentionAccts { // case .hashtag(let hashtag):
UITextChecker.learnWord(acct) // let initialComposeContent = "#" + hashtag
} // UITextChecker.learnWord(initialComposeContent)
if let spoilerText = status.spoilerText, !spoilerText.isEmpty { // let preInsertedContent = initialComposeContent + " "
self.isContentWarningComposing = true // self.preInsertedContent = preInsertedContent
self.composeStatusAttribute.contentWarningContent = spoilerText // self.composeStatusAttribute.composeContent = preInsertedContent
} // case .mention(let record):
// context.managedObjectContext.performAndWait {
let initialComposeContent = mentionAccts.joined(separator: " ") // guard let user = record.object(in: context.managedObjectContext) else { return }
let preInsertedContent: String? = initialComposeContent.isEmpty ? nil : initialComposeContent + " " // let initialComposeContent = "@" + user.acct
self.preInsertedContent = preInsertedContent // UITextChecker.learnWord(initialComposeContent)
self.composeStatusAttribute.composeContent = preInsertedContent // let preInsertedContent = initialComposeContent + " "
} // self.preInsertedContent = preInsertedContent
case .hashtag(let hashtag): // self.composeStatusAttribute.composeContent = preInsertedContent
let initialComposeContent = "#" + hashtag // }
UITextChecker.learnWord(initialComposeContent) // case .post:
let preInsertedContent = initialComposeContent + " " // self.preInsertedContent = nil
self.preInsertedContent = preInsertedContent // }
self.composeStatusAttribute.composeContent = preInsertedContent //
case .mention(let record): // // configure content warning
context.managedObjectContext.performAndWait { // if let composeContent = composeStatusAttribute.composeContent {
guard let user = record.object(in: context.managedObjectContext) else { return } // cell.metaText.textView.text = composeContent
let initialComposeContent = "@" + user.acct // }
UITextChecker.learnWord(initialComposeContent) //
let preInsertedContent = initialComposeContent + " " // // configure content warning
self.preInsertedContent = preInsertedContent // cell.statusContentWarningEditorView.textView.text = composeStatusAttribute.contentWarningContent
self.composeStatusAttribute.composeContent = preInsertedContent // }
} //}
case .post:
self.preInsertedContent = nil
}
// configure content warning
if let composeContent = composeStatusAttribute.composeContent {
cell.metaText.textView.text = composeContent
}
// configure content warning
cell.statusContentWarningEditorView.textView.text = composeStatusAttribute.contentWarningContent
}
}

View File

@ -1,171 +0,0 @@
//
// ComposeStatusAttachmentTableViewCell.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-6-29.
//
import UIKit
import SwiftUI
import Combine
import AlamofireImage
import MastodonAsset
import MastodonLocalization
import UIHostingConfigurationBackport
final class ComposeStatusAttachmentTableViewCell: UITableViewCell {
private(set) var dataSource: UICollectionViewDiffableDataSource<ComposeStatusAttachmentSection, ComposeStatusAttachmentItem>!
weak var composeStatusAttachmentCollectionViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate?
var observations = Set<NSKeyValueObservation>()
private static func createLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsetsReference = .readableContent
return UICollectionViewCompositionalLayout(section: section)
}
private(set) var collectionViewHeightLayoutConstraint: NSLayoutConstraint!
let collectionView: UICollectionView = {
let collectionViewLayout = ComposeStatusAttachmentTableViewCell.createLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.register(ComposeStatusAttachmentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self))
collectionView.backgroundColor = .clear
collectionView.alwaysBounceVertical = true
collectionView.isScrollEnabled = false
return collectionView
}()
let collectionViewHeightDidUpdate = PassthroughSubject<Void, Never>()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
_init()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
_init()
}
}
extension ComposeStatusAttachmentTableViewCell {
private func _init() {
backgroundColor = .clear
contentView.backgroundColor = .clear
collectionView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(collectionView)
collectionViewHeightLayoutConstraint = collectionView.heightAnchor.constraint(equalToConstant: 200).priority(.defaultHigh)
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
collectionViewHeightLayoutConstraint,
])
collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, _ in
guard let self = self else { return }
self.collectionViewHeightLayoutConstraint.constant = collectionView.contentSize.height
self.collectionViewHeightDidUpdate.send()
}
.store(in: &observations)
self.dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) {
[weak self] collectionView, indexPath, item -> UICollectionViewCell? in
guard let _ = self else { return UICollectionViewCell() }
switch item {
case .attachment:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self), for: indexPath) as! ComposeStatusAttachmentCollectionViewCell
cell.contentConfiguration = UIHostingConfigurationBackport {
HStack {
Image(systemName: "star")
Text("Favorites")
Spacer()
}
}
// cell.attachmentContainerView.descriptionTextView.text = attachmentService.description.value
// cell.delegate = self.composeStatusAttachmentCollectionViewCellDelegate
// attachmentService.thumbnailImage
// .receive(on: DispatchQueue.main)
// .sink { [weak cell] thumbnailImage in
// guard let cell = cell else { return }
// let size = cell.attachmentContainerView.previewImageView.frame.size != .zero ? cell.attachmentContainerView.previewImageView.frame.size : CGSize(width: 1, height: 1)
// guard let image = thumbnailImage else {
// let placeholder = UIImage.placeholder(
// size: size,
// color: ThemeService.shared.currentTheme.value.systemGroupedBackgroundColor
// )
// .af.imageRounded(
// withCornerRadius: AttachmentContainerView.containerViewCornerRadius
// )
// cell.attachmentContainerView.previewImageView.image = placeholder
// return
// }
// // cannot get correct size. set corner radius on layer
// cell.attachmentContainerView.previewImageView.image = image
// }
// .store(in: &cell.disposeBag)
// Publishers.CombineLatest(
// attachmentService.uploadStateMachineSubject.eraseToAnyPublisher(),
// attachmentService.error.eraseToAnyPublisher()
// )
// .receive(on: DispatchQueue.main)
// .sink { [weak cell, weak attachmentService] uploadState, error in
// guard let cell = cell else { return }
// guard let attachmentService = attachmentService else { return }
// cell.attachmentContainerView.emptyStateView.isHidden = error == nil
// cell.attachmentContainerView.descriptionBackgroundView.isHidden = error != nil
// if let error = error {
// cell.attachmentContainerView.activityIndicatorView.stopAnimating()
// cell.attachmentContainerView.emptyStateView.label.text = error.localizedDescription
// } else {
// guard let uploadState = uploadState else { return }
// switch uploadState {
// case is MastodonAttachmentService.UploadState.Finish:
// cell.attachmentContainerView.activityIndicatorView.stopAnimating()
// case is MastodonAttachmentService.UploadState.Fail:
// cell.attachmentContainerView.activityIndicatorView.stopAnimating()
// // FIXME: not display
// cell.attachmentContainerView.emptyStateView.label.text = {
// if let file = attachmentService.file.value {
// switch file {
// case .jpeg, .png, .gif:
// return L10n.Scene.Compose.Attachment.attachmentBroken(L10n.Scene.Compose.Attachment.photo)
// case .other:
// return L10n.Scene.Compose.Attachment.attachmentBroken(L10n.Scene.Compose.Attachment.video)
// }
// } else {
// return L10n.Scene.Compose.Attachment.attachmentBroken(L10n.Scene.Compose.Attachment.photo)
// }
// }()
// default:
// break
// }
// }
// }
// .store(in: &cell.disposeBag)
// NotificationCenter.default.publisher(
// for: UITextView.textDidChangeNotification,
// object: cell.attachmentContainerView.descriptionTextView
// )
// .receive(on: DispatchQueue.main)
// .sink { notification in
// guard let textField = notification.object as? UITextView else { return }
// let text = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines)
// attachmentService.description.value = text
// }
// .store(in: &cell.disposeBag)
return cell
}
}
}
}

View File

@ -1,172 +0,0 @@
//
// ComposeStatusContentTableViewCell.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-6-28.
//
import os.log
import UIKit
import Combine
import MetaTextKit
import UITextView_Placeholder
import MastodonAsset
import MastodonLocalization
import MastodonUI
protocol ComposeStatusContentTableViewCellDelegate: AnyObject {
func composeStatusContentTableViewCell(_ cell: ComposeStatusContentTableViewCell, textViewShouldBeginEditing textView: UITextView) -> Bool
}
final class ComposeStatusContentTableViewCell: UITableViewCell {
let logger = Logger(subsystem: "ComposeStatusContentTableViewCell", category: "View")
var disposeBag = Set<AnyCancellable>()
weak var delegate: ComposeStatusContentTableViewCellDelegate?
let statusView = StatusView()
let statusContentWarningEditorView = StatusContentWarningEditorView()
let textEditorViewContainerView = UIView()
static let metaTextViewTag: Int = 333
let metaText: MetaText = {
let metaText = MetaText()
metaText.textView.backgroundColor = .clear
metaText.textView.isScrollEnabled = false
metaText.textView.keyboardType = .twitter
metaText.textView.textDragInteraction?.isEnabled = false // disable drag for link and attachment
metaText.textView.textContainer.lineFragmentPadding = 10 // leading inset
metaText.textView.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
metaText.textView.attributedPlaceholder = {
var attributes = metaText.textAttributes
attributes[.foregroundColor] = Asset.Colors.Label.secondary.color
return NSAttributedString(
string: L10n.Scene.Compose.contentInputPlaceholder,
attributes: attributes
)
}()
metaText.paragraphStyle = {
let style = NSMutableParagraphStyle()
style.lineSpacing = 5
style.paragraphSpacing = 0
return style
}()
metaText.textAttributes = [
.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)),
.foregroundColor: Asset.Colors.Label.primary.color,
]
metaText.linkAttributes = [
.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold)),
.foregroundColor: Asset.Colors.brand.color,
]
return metaText
}()
// output
let contentWarningContent = PassthroughSubject<String, Never>()
override func prepareForReuse() {
super.prepareForReuse()
metaText.delegate = nil
metaText.textView.delegate = nil
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
_init()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
_init()
}
}
extension ComposeStatusContentTableViewCell {
private func _init() {
selectionStyle = .none
layer.zPosition = 999
backgroundColor = .clear
preservesSuperviewLayoutMargins = true
let containerStackView = UIStackView()
containerStackView.axis = .vertical
containerStackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(containerStackView)
NSLayoutConstraint.activate([
containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor),
containerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
containerStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
containerStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
])
containerStackView.preservesSuperviewLayoutMargins = true
containerStackView.addArrangedSubview(statusContentWarningEditorView)
statusContentWarningEditorView.setContentHuggingPriority(.required - 1, for: .vertical)
let statusContainerView = UIView()
statusContainerView.preservesSuperviewLayoutMargins = true
containerStackView.addArrangedSubview(statusContainerView)
statusView.translatesAutoresizingMaskIntoConstraints = false
statusContainerView.addSubview(statusView)
NSLayoutConstraint.activate([
statusView.topAnchor.constraint(equalTo: statusContainerView.topAnchor, constant: 20),
statusView.leadingAnchor.constraint(equalTo: statusContainerView.leadingAnchor),
statusView.trailingAnchor.constraint(equalTo: statusContainerView.trailingAnchor),
statusView.bottomAnchor.constraint(equalTo: statusContainerView.bottomAnchor),
])
statusView.setup(style: .composeStatusAuthor)
containerStackView.addArrangedSubview(textEditorViewContainerView)
metaText.textView.translatesAutoresizingMaskIntoConstraints = false
textEditorViewContainerView.addSubview(metaText.textView)
NSLayoutConstraint.activate([
metaText.textView.topAnchor.constraint(equalTo: textEditorViewContainerView.topAnchor),
metaText.textView.leadingAnchor.constraint(equalTo: textEditorViewContainerView.layoutMarginsGuide.leadingAnchor),
metaText.textView.trailingAnchor.constraint(equalTo: textEditorViewContainerView.layoutMarginsGuide.trailingAnchor),
metaText.textView.bottomAnchor.constraint(equalTo: textEditorViewContainerView.bottomAnchor),
metaText.textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 64).priority(.defaultHigh),
])
statusContentWarningEditorView.textView.delegate = self
}
}
// MARK: - UITextViewDelegate
extension ComposeStatusContentTableViewCell: UITextViewDelegate {
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
return delegate?.composeStatusContentTableViewCell(self, textViewShouldBeginEditing: textView) ?? true
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
switch textView {
case statusContentWarningEditorView.textView:
// disable input line break
guard text != "\n" else { return false }
return true
default:
assertionFailure()
return true
}
}
func textViewDidChange(_ textView: UITextView) {
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): text: \(textView.text ?? "<nil>")")
guard textView === statusContentWarningEditorView.textView else { return }
// replace line break with space
// needs check input state to prevent break the IME
if textView.markedTextRange == nil {
textView.text = textView.text.replacingOccurrences(of: "\n", with: " ")
}
contentWarningContent.send(textView.text)
}
}

View File

@ -1,209 +0,0 @@
//
// ComposeStatusPollTableViewCell.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-6-29.
//
import os.log
import UIKit
import Combine
import MastodonAsset
import MastodonLocalization
protocol ComposeStatusPollTableViewCellDelegate: AnyObject {
func composeStatusPollTableViewCell(_ cell: ComposeStatusPollTableViewCell, pollOptionAttributesDidReorder options: [ComposeStatusPollItem.PollOptionAttribute])
}
final class ComposeStatusPollTableViewCell: UITableViewCell {
let logger = Logger(subsystem: "ComposeStatusPollTableViewCell", category: "UI")
private(set) var dataSource: UICollectionViewDiffableDataSource<ComposeStatusPollSection, ComposeStatusPollItem>!
var observations = Set<NSKeyValueObservation>()
weak var customEmojiPickerInputViewModel: CustomEmojiPickerInputViewModel?
weak var delegate: ComposeStatusPollTableViewCellDelegate?
weak var composeStatusPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionCollectionViewCellDelegate?
weak var composeStatusPollOptionAppendEntryCollectionViewCellDelegate: ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate?
weak var composeStatusPollExpiresOptionCollectionViewCellDelegate: ComposeStatusPollExpiresOptionCollectionViewCellDelegate?
private static func createLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsetsReference = .readableContent
return UICollectionViewCompositionalLayout(section: section)
}
private(set) var collectionViewHeightLayoutConstraint: NSLayoutConstraint!
let collectionView: UICollectionView = {
let collectionViewLayout = ComposeStatusPollTableViewCell.createLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.register(ComposeStatusPollOptionCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollOptionCollectionViewCell.self))
collectionView.register(ComposeStatusPollOptionAppendEntryCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollOptionAppendEntryCollectionViewCell.self))
collectionView.register(ComposeStatusPollExpiresOptionCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollExpiresOptionCollectionViewCell.self))
collectionView.backgroundColor = .clear
collectionView.alwaysBounceVertical = true
collectionView.isScrollEnabled = false
collectionView.dragInteractionEnabled = true
return collectionView
}()
let collectionViewHeightDidUpdate = PassthroughSubject<Void, Never>()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
_init()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
_init()
}
}
extension ComposeStatusPollTableViewCell {
private func _init() {
backgroundColor = .clear
contentView.backgroundColor = .clear
collectionView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(collectionView)
collectionViewHeightLayoutConstraint = collectionView.heightAnchor.constraint(equalToConstant: 300).priority(.defaultHigh)
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
collectionViewHeightLayoutConstraint,
])
collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, _ in
guard let self = self else { return }
self.collectionViewHeightLayoutConstraint.constant = collectionView.contentSize.height
self.collectionViewHeightDidUpdate.send()
}
.store(in: &observations)
self.dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { [
weak self
] collectionView, indexPath, item -> UICollectionViewCell? in
guard let self = self else { return UICollectionViewCell() }
switch item {
case .pollOption(let attribute):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollOptionCollectionViewCell.self), for: indexPath) as! ComposeStatusPollOptionCollectionViewCell
cell.pollOptionView.optionTextField.text = attribute.option.value
cell.pollOptionView.optionTextField.placeholder = L10n.Scene.Compose.Poll.optionNumber(indexPath.item + 1)
cell.pollOption
.receive(on: DispatchQueue.main)
.assign(to: \.value, on: attribute.option)
.store(in: &cell.disposeBag)
cell.delegate = self.composeStatusPollOptionCollectionViewCellDelegate
if let customEmojiPickerInputViewModel = self.customEmojiPickerInputViewModel {
ComposeStatusSection.configureCustomEmojiPicker(viewModel: customEmojiPickerInputViewModel, customEmojiReplaceableTextInput: cell.pollOptionView.optionTextField, disposeBag: &cell.disposeBag)
}
return cell
case .pollOptionAppendEntry:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollOptionAppendEntryCollectionViewCell.self), for: indexPath) as! ComposeStatusPollOptionAppendEntryCollectionViewCell
cell.delegate = self.composeStatusPollOptionAppendEntryCollectionViewCellDelegate
return cell
case .pollExpiresOption(let attribute):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollExpiresOptionCollectionViewCell.self), for: indexPath) as! ComposeStatusPollExpiresOptionCollectionViewCell
cell.durationButton.setTitle(L10n.Scene.Compose.Poll.durationTime(attribute.expiresOption.value.title), for: .normal)
attribute.expiresOption
.receive(on: DispatchQueue.main)
.sink { [weak cell] expiresOption in
guard let cell = cell else { return }
cell.durationButton.setTitle(L10n.Scene.Compose.Poll.durationTime(expiresOption.title), for: .normal)
}
.store(in: &cell.disposeBag)
cell.delegate = self.composeStatusPollExpiresOptionCollectionViewCellDelegate
return cell
}
}
collectionView.dragDelegate = self
collectionView.dropDelegate = self
}
}
// MARK: - UICollectionViewDragDelegate
extension ComposeStatusPollTableViewCell: UICollectionViewDragDelegate {
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
guard let item = dataSource.itemIdentifier(for: indexPath) else { return [] }
switch item {
case .pollOption:
let itemProvider = NSItemProvider(object: String(item.hashValue) as NSString)
let dragItem = UIDragItem(itemProvider: itemProvider)
dragItem.localObject = item
return [dragItem]
default:
return []
}
}
func collectionView(_ collectionView: UICollectionView, dragSessionIsRestrictedToDraggingApplication session: UIDragSession) -> Bool {
// drag to app should be the same app
return true
}
}
// MARK: - UICollectionViewDropDelegate
extension ComposeStatusPollTableViewCell: UICollectionViewDropDelegate {
// didUpdate
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
guard collectionView.hasActiveDrag,
let destinationIndexPath = destinationIndexPath,
let item = dataSource.itemIdentifier(for: destinationIndexPath)
else {
return UICollectionViewDropProposal(operation: .forbidden)
}
switch item {
case .pollOption:
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
default:
return UICollectionViewDropProposal(operation: .cancel)
}
}
// performDrop
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
guard let dropItem = coordinator.items.first,
let item = dropItem.dragItem.localObject as? ComposeStatusPollItem,
case .pollOption = item
else { return }
guard coordinator.proposal.operation == .move else { return }
guard let destinationIndexPath = coordinator.destinationIndexPath,
let _ = collectionView.cellForItem(at: destinationIndexPath) as? ComposeStatusPollOptionCollectionViewCell
else { return }
var snapshot = dataSource.snapshot()
guard destinationIndexPath.row < snapshot.itemIdentifiers.count else { return }
let anchorItem = snapshot.itemIdentifiers[destinationIndexPath.row]
snapshot.moveItem(item, afterItem: anchorItem)
dataSource.apply(snapshot)
coordinator.drop(dropItem.dragItem, toItemAt: destinationIndexPath)
}
}
extension ComposeStatusPollTableViewCell: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(originalIndexPath.debugDescription) -> \(proposedIndexPath.debugDescription)")
guard let _ = collectionView.cellForItem(at: proposedIndexPath) as? ComposeStatusPollOptionCollectionViewCell else {
return originalIndexPath
}
return proposedIndexPath
}
}

View File

@ -8,6 +8,8 @@
import UIKit import UIKit
import Combine import Combine
import MetaTextKit import MetaTextKit
import MastodonCore
import MastodonUI
final class CustomEmojiPickerInputViewModel { final class CustomEmojiPickerInputViewModel {

View File

@ -11,15 +11,11 @@ import GameplayKit
import MastodonSDK import MastodonSDK
extension DiscoveryCommunityViewModel { extension DiscoveryCommunityViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "DiscoveryCommunityViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "DiscoveryCommunityViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: DiscoveryCommunityViewModel? weak var viewModel: DiscoveryCommunityViewModel?
@ -29,8 +25,10 @@ extension DiscoveryCommunityViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? DiscoveryCommunityViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -39,7 +37,7 @@ extension DiscoveryCommunityViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -11,16 +11,12 @@ import GameplayKit
import MastodonSDK import MastodonSDK
extension DiscoveryNewsViewModel { extension DiscoveryNewsViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "DiscoveryNewsViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "DiscoveryNewsViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: DiscoveryNewsViewModel? weak var viewModel: DiscoveryNewsViewModel?
init(viewModel: DiscoveryNewsViewModel) { init(viewModel: DiscoveryNewsViewModel) {
@ -29,8 +25,10 @@ extension DiscoveryNewsViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? DiscoveryNewsViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -39,7 +37,7 @@ extension DiscoveryNewsViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -12,16 +12,12 @@ import MastodonSDK
import MastodonCore import MastodonCore
extension DiscoveryPostsViewModel { extension DiscoveryPostsViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "DiscoveryPostsViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "DiscoveryPostsViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: DiscoveryPostsViewModel? weak var viewModel: DiscoveryPostsViewModel?
init(viewModel: DiscoveryPostsViewModel) { init(viewModel: DiscoveryPostsViewModel) {
@ -30,8 +26,10 @@ extension DiscoveryPostsViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? DiscoveryPostsViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -40,7 +38,7 @@ extension DiscoveryPostsViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -13,6 +13,7 @@ import GameplayKit
import CoreData import CoreData
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
final class HashtagTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class HashtagTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
@ -168,10 +169,10 @@ extension HashtagTimelineViewController {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: context, context: context,
composeKind: .hashtag(hashtag: viewModel.hashtag), authContext: viewModel.authContext,
authContext: viewModel.authContext kind: .hashtag(hashtag: viewModel.hashtag)
) )
coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil)) _ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
} }
} }

View File

@ -11,7 +11,7 @@ import GameplayKit
import CoreDataStack import CoreDataStack
extension HashtagTimelineViewModel { extension HashtagTimelineViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "HashtagTimelineViewModel.LoadOldestState", category: "StateMachine") let logger = Logger(subsystem: "HashtagTimelineViewModel.LoadOldestState", category: "StateMachine")
@ -28,10 +28,11 @@ extension HashtagTimelineViewModel {
} }
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
let previousState = previousState as? HashtagTimelineViewModel.State super.didEnter(from: previousState)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")")
let from = previousState.flatMap { String(describing: $0) } ?? "nil"
viewModel?.loadOldestStateMachinePublisher.send(self) let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor

View File

@ -51,7 +51,6 @@ final class HashtagTimelineViewModel {
stateMachine.enter(State.Initial.self) stateMachine.enter(State.Initial.self)
return stateMachine return stateMachine
}() }()
lazy var loadOldestStateMachinePublisher = CurrentValueSubject<State?, Never>(nil)
init(context: AppContext, authContext: AuthContext, hashtag: String) { init(context: AppContext, authContext: AuthContext, hashtag: String) {
self.context = context self.context = context

View File

@ -9,6 +9,7 @@ import os.log
import UIKit import UIKit
import CoreData import CoreData
import CoreDataStack import CoreDataStack
import MastodonUI
extension HomeTimelineViewModel { extension HomeTimelineViewModel {

View File

@ -11,15 +11,11 @@ import GameplayKit
import MastodonSDK import MastodonSDK
extension HomeTimelineViewModel { extension HomeTimelineViewModel {
class LoadOldestState: GKState, NamingState { class LoadOldestState: GKState {
let logger = Logger(subsystem: "HomeTimelineViewModel.LoadOldestState", category: "StateMachine") let logger = Logger(subsystem: "HomeTimelineViewModel.LoadOldestState", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: HomeTimelineViewModel? weak var viewModel: HomeTimelineViewModel?
@ -29,10 +25,10 @@ extension HomeTimelineViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? HomeTimelineViewModel.LoadOldestState
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")")
viewModel?.loadOldestStateMachinePublisher.send(self) let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -41,7 +37,7 @@ extension HomeTimelineViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -16,6 +16,7 @@ import GameplayKit
import AlamofireImage import AlamofireImage
import DateToolsSwift import DateToolsSwift
import MastodonCore import MastodonCore
import MastodonUI
final class HomeTimelineViewModel: NSObject { final class HomeTimelineViewModel: NSObject {

View File

@ -12,16 +12,12 @@ import MastodonSDK
import os.log import os.log
extension NotificationTimelineViewModel { extension NotificationTimelineViewModel {
class LoadOldestState: GKState, NamingState { class LoadOldestState: GKState {
let logger = Logger(subsystem: "NotificationTimelineViewModel.LoadOldestState", category: "StateMachine") let logger = Logger(subsystem: "NotificationTimelineViewModel.LoadOldestState", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: NotificationTimelineViewModel? weak var viewModel: NotificationTimelineViewModel?
init(viewModel: NotificationTimelineViewModel) { init(viewModel: NotificationTimelineViewModel) {
@ -30,8 +26,10 @@ extension NotificationTimelineViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? NotificationTimelineViewModel.LoadOldestState
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -40,7 +38,7 @@ extension NotificationTimelineViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -12,7 +12,7 @@ import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization import MastodonLocalization
final class PickServerLoaderTableViewCell: TimelineLoaderTableViewCell { public final class PickServerLoaderTableViewCell: TimelineLoaderTableViewCell {
let containerView: UIView = { let containerView: UIView = {
let view = UIView() let view = UIView()
@ -30,7 +30,7 @@ final class PickServerLoaderTableViewCell: TimelineLoaderTableViewCell {
return label return label
}() }()
override func _init() { public override func _init() {
super._init() super._init()

View File

@ -9,6 +9,7 @@ import os.log
import UIKit import UIKit
import Tabman import Tabman
import MastodonAsset import MastodonAsset
import MastodonUI
import MastodonLocalization import MastodonLocalization
protocol PickServerServerSectionTableHeaderViewDelegate: AnyObject { protocol PickServerServerSectionTableHeaderViewDelegate: AnyObject {

View File

@ -12,6 +12,7 @@ import Combine
import GameplayKit import GameplayKit
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
final class BookmarkViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class BookmarkViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {

View File

@ -12,16 +12,12 @@ import MastodonSDK
import MastodonCore import MastodonCore
extension BookmarkViewModel { extension BookmarkViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "BookmarkViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "BookmarkViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: BookmarkViewModel? weak var viewModel: BookmarkViewModel?
init(viewModel: BookmarkViewModel) { init(viewModel: BookmarkViewModel) {
@ -30,8 +26,10 @@ extension BookmarkViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? BookmarkViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -40,7 +38,7 @@ extension BookmarkViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -15,6 +15,7 @@ import Combine
import GameplayKit import GameplayKit
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
final class FavoriteViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class FavoriteViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {

View File

@ -12,15 +12,11 @@ import MastodonCore
import MastodonSDK import MastodonSDK
extension FavoriteViewModel { extension FavoriteViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "FavoriteViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "FavoriteViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: FavoriteViewModel? weak var viewModel: FavoriteViewModel?
@ -30,8 +26,10 @@ extension FavoriteViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? FavoriteViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -40,7 +38,7 @@ extension FavoriteViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -10,6 +10,7 @@ import UIKit
import GameplayKit import GameplayKit
import Combine import Combine
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
final class FollowerListViewController: UIViewController, NeedsDependency { final class FollowerListViewController: UIViewController, NeedsDependency {

View File

@ -12,7 +12,7 @@ import MastodonSDK
import MastodonCore import MastodonCore
extension FollowerListViewModel { extension FollowerListViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "FollowerListViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "FollowerListViewModel.State", category: "StateMachine")
@ -30,8 +30,10 @@ extension FollowerListViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? FollowerListViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -40,7 +42,7 @@ extension FollowerListViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -11,6 +11,7 @@ import GameplayKit
import Combine import Combine
import MastodonLocalization import MastodonLocalization
import MastodonCore import MastodonCore
import MastodonUI
final class FollowingListViewController: UIViewController, NeedsDependency { final class FollowingListViewController: UIViewController, NeedsDependency {

View File

@ -11,15 +11,11 @@ import GameplayKit
import MastodonSDK import MastodonSDK
extension FollowingListViewModel { extension FollowingListViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "FollowingListViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "FollowingListViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: FollowingListViewModel? weak var viewModel: FollowingListViewModel?
@ -29,8 +25,10 @@ extension FollowingListViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? FollowingListViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -39,7 +37,7 @@ extension FollowingListViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -499,7 +499,7 @@ extension ProfileViewController {
user: record user: record
) )
guard let activityViewController = _activityViewController else { return } guard let activityViewController = _activityViewController else { return }
self.coordinator.present( _ = self.coordinator.present(
scene: .activityViewController( scene: .activityViewController(
activityViewController: activityViewController, activityViewController: activityViewController,
sourceView: nil, sourceView: nil,
@ -514,13 +514,13 @@ extension ProfileViewController {
@objc private func favoriteBarButtonItemPressed(_ sender: UIBarButtonItem) { @objc private func favoriteBarButtonItemPressed(_ sender: UIBarButtonItem) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
let favoriteViewModel = FavoriteViewModel(context: context, authContext: viewModel.authContext) let favoriteViewModel = FavoriteViewModel(context: context, authContext: viewModel.authContext)
coordinator.present(scene: .favorite(viewModel: favoriteViewModel), from: self, transition: .show) _ = coordinator.present(scene: .favorite(viewModel: favoriteViewModel), from: self, transition: .show)
} }
@objc private func bookmarkBarButtonItemPressed(_ sender: UIBarButtonItem) { @objc private func bookmarkBarButtonItemPressed(_ sender: UIBarButtonItem) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
let bookmarkViewModel = BookmarkViewModel(context: context, authContext: viewModel.authContext) let bookmarkViewModel = BookmarkViewModel(context: context, authContext: viewModel.authContext)
coordinator.present(scene: .bookmark(viewModel: bookmarkViewModel), from: self, transition: .show) _ = coordinator.present(scene: .bookmark(viewModel: bookmarkViewModel), from: self, transition: .show)
} }
@objc private func replyBarButtonItemPressed(_ sender: UIBarButtonItem) { @objc private func replyBarButtonItemPressed(_ sender: UIBarButtonItem) {
@ -528,10 +528,10 @@ extension ProfileViewController {
guard let mastodonUser = viewModel.user else { return } guard let mastodonUser = viewModel.user else { return }
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: context, context: context,
composeKind: .mention(user: .init(objectID: mastodonUser.objectID)), authContext: viewModel.authContext,
authContext: viewModel.authContext kind: .mention(user: mastodonUser.asRecrod)
) )
coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil)) _ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
} }
@objc private func refreshControlValueChanged(_ sender: UIRefreshControl) { @objc private func refreshControlValueChanged(_ sender: UIRefreshControl) {

View File

@ -12,16 +12,12 @@ import MastodonCore
import MastodonSDK import MastodonSDK
extension UserTimelineViewModel { extension UserTimelineViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "UserTimelineViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "UserTimelineViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: UserTimelineViewModel? weak var viewModel: UserTimelineViewModel?
init(viewModel: UserTimelineViewModel) { init(viewModel: UserTimelineViewModel) {
@ -30,8 +26,10 @@ extension UserTimelineViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? UserTimelineViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -40,7 +38,7 @@ extension UserTimelineViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -11,15 +11,11 @@ import GameplayKit
import MastodonSDK import MastodonSDK
extension UserListViewModel { extension UserListViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "UserListViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "UserListViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: UserListViewModel? weak var viewModel: UserListViewModel?
@ -29,8 +25,10 @@ extension UserListViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? UserListViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -39,7 +37,7 @@ extension UserListViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -11,6 +11,7 @@ import Combine
import CoreDataStack import CoreDataStack
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
protocol ReportStatusViewControllerDelegate: AnyObject { protocol ReportStatusViewControllerDelegate: AnyObject {

View File

@ -10,6 +10,7 @@ import UIKit
import Combine import Combine
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
protocol ReportSupplementaryViewControllerDelegate: AnyObject { protocol ReportSupplementaryViewControllerDelegate: AnyObject {

View File

@ -372,8 +372,8 @@ extension MainTabBarController {
guard let authContext = self.authContext else { return } guard let authContext = self.authContext else { return }
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: context, context: context,
composeKind: .post, authContext: authContext,
authContext: authContext kind: .post
) )
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil)) _ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
} }
@ -741,8 +741,8 @@ extension MainTabBarController {
guard let authContext = self.authContext else { return } guard let authContext = self.authContext else { return }
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: context, context: context,
composeKind: .post, authContext: authContext,
authContext: authContext kind: .post
) )
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil)) _ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
} }

View File

@ -207,8 +207,8 @@ extension SidebarViewController: UICollectionViewDelegate {
case .compose: case .compose:
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: context, context: context,
composeKind: .post, authContext: authContext,
authContext: authContext kind: .post
) )
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil)) _ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
default: default:

View File

@ -12,15 +12,12 @@ import MastodonSDK
import MastodonCore import MastodonCore
extension SearchResultViewModel { extension SearchResultViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "SearchResultViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "SearchResultViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: SearchResultViewModel? weak var viewModel: SearchResultViewModel?
init(viewModel: SearchResultViewModel) { init(viewModel: SearchResultViewModel) {
@ -29,8 +26,10 @@ extension SearchResultViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? SearchResultViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -39,7 +38,7 @@ extension SearchResultViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -13,6 +13,7 @@ import OSLog
import UIKit import UIKit
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
class SuggestionAccountViewController: UIViewController, NeedsDependency { class SuggestionAccountViewController: UIViewController, NeedsDependency {

View File

@ -13,6 +13,7 @@ import AVKit
import MastodonMeta import MastodonMeta
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
final class ThreadViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class ThreadViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
@ -114,8 +115,8 @@ extension ThreadViewController {
guard case let .root(threadContext) = viewModel.root else { return } guard case let .root(threadContext) = viewModel.root else { return }
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: context, context: context,
composeKind: .reply(status: threadContext.status), authContext: viewModel.authContext,
authContext: viewModel.authContext kind: .reply(status: threadContext.status)
) )
_ = coordinator.present( _ = coordinator.present(
scene: .compose(viewModel: composeViewModel), scene: .compose(viewModel: composeViewModel),

View File

@ -10,6 +10,7 @@ import Combine
import CoreData import CoreData
import CoreDataStack import CoreDataStack
import MastodonCore import MastodonCore
import MastodonUI
import MastodonSDK import MastodonSDK
extension ThreadViewModel { extension ThreadViewModel {

View File

@ -13,15 +13,11 @@ import CoreDataStack
import MastodonSDK import MastodonSDK
extension ThreadViewModel { extension ThreadViewModel {
class LoadThreadState: GKState, NamingState { class LoadThreadState: GKState {
let logger = Logger(subsystem: "ThreadViewModel.LoadThreadState", category: "StateMachine") let logger = Logger(subsystem: "ThreadViewModel.LoadThreadState", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: ThreadViewModel? weak var viewModel: ThreadViewModel?
@ -31,8 +27,10 @@ extension ThreadViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? ThreadViewModel.LoadThreadState
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -41,7 +39,7 @@ extension ThreadViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -149,10 +149,10 @@ extension SceneDelegate {
if let authContext = coordinator?.authContext { if let authContext = coordinator?.authContext {
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: AppContext.shared, context: AppContext.shared,
composeKind: .post, authContext: authContext,
authContext: authContext kind: .post
) )
coordinator?.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil)) _ = coordinator?.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): present compose scene") logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): present compose scene")
} else { } else {
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): not authenticated") logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): not authenticated")

View File

@ -11,7 +11,7 @@ import MastodonAsset
import MastodonLocalization import MastodonLocalization
extension Mastodon.API.Subscriptions.Policy { extension Mastodon.API.Subscriptions.Policy {
var title: String { public var title: String {
switch self { switch self {
case .all: return L10n.Scene.Settings.Section.Notifications.Trigger.anyone case .all: return L10n.Scene.Settings.Section.Notifications.Trigger.anyone
case .follower: return L10n.Scene.Settings.Section.Notifications.Trigger.follower case .follower: return L10n.Scene.Settings.Section.Notifications.Trigger.follower

View File

@ -7,14 +7,15 @@
import Foundation import Foundation
import MastodonSDK import MastodonSDK
import MastodonMeta
extension Mastodon.Entity.Account { extension Mastodon.Entity.Account: Hashable {
public var displayNameWithFallback: String { public func hash(into hasher: inout Hasher) {
if displayName.isEmpty { hasher.combine(id)
return username }
} else {
return displayName public static func == (lhs: Mastodon.Entity.Account, rhs: Mastodon.Entity.Account) -> Bool {
} return lhs.id == rhs.id
} }
} }
@ -28,3 +29,21 @@ extension Mastodon.Entity.Account {
return avatarImageURL() ?? URL(string: "https://\(domain)/avatars/original/missing.png")! return avatarImageURL() ?? URL(string: "https://\(domain)/avatars/original/missing.png")!
} }
} }
extension Mastodon.Entity.Account {
public var displayNameWithFallback: String {
return !displayName.isEmpty ? displayName : username
}
}
extension Mastodon.Entity.Account {
public var emojiMeta: MastodonContent.Emojis {
let isAnimated = !UserDefaults.shared.preferredStaticEmoji
var dict = MastodonContent.Emojis()
for emoji in emojis ?? [] {
dict[emoji.shortcode] = isAnimated ? emoji.url : emoji.staticURL
}
return dict
}
}

View File

@ -36,7 +36,7 @@ extension Mastodon.Entity.Error.Detail: LocalizedError {
extension Mastodon.Entity.Error.Detail { extension Mastodon.Entity.Error.Detail {
enum Item: String { public enum Item: String {
case username case username
case email case email
case password case password
@ -82,32 +82,32 @@ extension Mastodon.Entity.Error.Detail {
} }
} }
var usernameErrorDescriptions: [String] { public var usernameErrorDescriptions: [String] {
guard let username = username, !username.isEmpty else { return [] } guard let username = username, !username.isEmpty else { return [] }
return username.map { Mastodon.Entity.Error.Detail.localizeError(item: .username, for: $0) } return username.map { Mastodon.Entity.Error.Detail.localizeError(item: .username, for: $0) }
} }
var emailErrorDescriptions: [String] { public var emailErrorDescriptions: [String] {
guard let email = email, !email.isEmpty else { return [] } guard let email = email, !email.isEmpty else { return [] }
return email.map { Mastodon.Entity.Error.Detail.localizeError(item: .email, for: $0) } return email.map { Mastodon.Entity.Error.Detail.localizeError(item: .email, for: $0) }
} }
var passwordErrorDescriptions: [String] { public var passwordErrorDescriptions: [String] {
guard let password = password, !password.isEmpty else { return [] } guard let password = password, !password.isEmpty else { return [] }
return password.map { Mastodon.Entity.Error.Detail.localizeError(item: .password, for: $0) } return password.map { Mastodon.Entity.Error.Detail.localizeError(item: .password, for: $0) }
} }
var agreementErrorDescriptions: [String] { public var agreementErrorDescriptions: [String] {
guard let agreement = agreement, !agreement.isEmpty else { return [] } guard let agreement = agreement, !agreement.isEmpty else { return [] }
return agreement.map { Mastodon.Entity.Error.Detail.localizeError(item: .agreement, for: $0) } return agreement.map { Mastodon.Entity.Error.Detail.localizeError(item: .agreement, for: $0) }
} }
var localeErrorDescriptions: [String] { public var localeErrorDescriptions: [String] {
guard let locale = locale, !locale.isEmpty else { return [] } guard let locale = locale, !locale.isEmpty else { return [] }
return locale.map { Mastodon.Entity.Error.Detail.localizeError(item: .locale, for: $0) } return locale.map { Mastodon.Entity.Error.Detail.localizeError(item: .locale, for: $0) }
} }
var reasonErrorDescriptions: [String] { public var reasonErrorDescriptions: [String] {
guard let reason = reason, !reason.isEmpty else { return [] } guard let reason = reason, !reason.isEmpty else { return [] }
return reason.map { Mastodon.Entity.Error.Detail.localizeError(item: .reason, for: $0) } return reason.map { Mastodon.Entity.Error.Detail.localizeError(item: .reason, for: $0) }
} }

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import MastodonSDK import MastodonSDK
enum AutoCompleteItem { public enum AutoCompleteItem {
case hashtag(tag: Mastodon.Entity.Tag) case hashtag(tag: Mastodon.Entity.Tag)
case hashtagV1(tag: String) case hashtagV1(tag: String)
case account(account: Mastodon.Entity.Account) case account(account: Mastodon.Entity.Account)
@ -17,7 +17,7 @@ enum AutoCompleteItem {
} }
extension AutoCompleteItem: Equatable { extension AutoCompleteItem: Equatable {
static func == (lhs: AutoCompleteItem, rhs: AutoCompleteItem) -> Bool { public static func == (lhs: AutoCompleteItem, rhs: AutoCompleteItem) -> Bool {
switch (lhs, rhs) { switch (lhs, rhs) {
case (.hashtag(let tagLeft), hashtag(let tagRight)): case (.hashtag(let tagLeft), hashtag(let tagRight)):
return tagLeft.name == tagRight.name return tagLeft.name == tagRight.name
@ -36,7 +36,7 @@ extension AutoCompleteItem: Equatable {
} }
extension AutoCompleteItem: Hashable { extension AutoCompleteItem: Hashable {
func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
switch self { switch self {
case .hashtag(let tag): case .hashtag(let tag):
hasher.combine(tag.name) hasher.combine(tag.name)

View File

@ -0,0 +1,16 @@
//
// AutoCompleteSection.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-5-17.
//
import UIKit
import MastodonSDK
import MastodonMeta
import MastodonAsset
import MastodonLocalization
public enum AutoCompleteSection: Equatable, Hashable {
case main
}

View File

@ -6,7 +6,6 @@
// //
import Foundation import Foundation
import MastodonCore
enum ComposeStatusAttachmentItem { enum ComposeStatusAttachmentItem {
case attachment(attachmentService: MastodonAttachmentService) case attachment(attachmentService: MastodonAttachmentService)

View File

@ -8,7 +8,6 @@
import Foundation import Foundation
import Combine import Combine
import CoreData import CoreData
import MastodonCore
import MastodonMeta import MastodonMeta
import CoreDataStack import CoreDataStack

View File

@ -13,7 +13,7 @@ import MetaTextKit
import MastodonMeta import MastodonMeta
import AlamofireImage import AlamofireImage
enum ComposeStatusSection: Equatable, Hashable { public enum ComposeStatusSection: Equatable, Hashable {
case replyTo case replyTo
case status case status
case attachment case attachment
@ -21,20 +21,11 @@ enum ComposeStatusSection: Equatable, Hashable {
} }
extension ComposeStatusSection { extension ComposeStatusSection {
public enum ComposeKind {
case post
case hashtag(hashtag: String)
case mention(user: ManagedObjectRecord<MastodonUser>)
case reply(status: ManagedObjectRecord<Status>)
}
}
extension ComposeStatusSection { // static func configure(
// cell: ComposeStatusContentTableViewCell,
static func configure( // attribute: ComposeStatusItem.ComposeStatusAttribute
cell: ComposeStatusContentTableViewCell, // ) {
attribute: ComposeStatusItem.ComposeStatusAttribute
) {
// cell.prepa // cell.prepa
// // set avatar // // set avatar
// attribute.avatarURL // attribute.avatarURL
@ -62,18 +53,18 @@ extension ComposeStatusSection {
// cell.statusView.usernameLabel.text = username.flatMap { "@" + $0 } ?? " " // cell.statusView.usernameLabel.text = username.flatMap { "@" + $0 } ?? " "
// } // }
// .store(in: &cell.disposeBag) // .store(in: &cell.disposeBag)
} // }
} }
protocol CustomEmojiReplaceableTextInput: UITextInput & UIResponder { public protocol CustomEmojiReplaceableTextInput: UITextInput & UIResponder {
var inputView: UIView? { get set } var inputView: UIView? { get set }
} }
class CustomEmojiReplaceableTextInputReference { public class CustomEmojiReplaceableTextInputReference {
weak var value: CustomEmojiReplaceableTextInput? public weak var value: CustomEmojiReplaceableTextInput?
init(value: CustomEmojiReplaceableTextInput? = nil) { public init(value: CustomEmojiReplaceableTextInput? = nil) {
self.value = value self.value = value
} }
} }
@ -83,21 +74,21 @@ extension UITextView: CustomEmojiReplaceableTextInput { }
extension ComposeStatusSection { extension ComposeStatusSection {
static func configureCustomEmojiPicker( // static func configureCustomEmojiPicker(
viewModel: CustomEmojiPickerInputViewModel?, // viewModel: CustomEmojiPickerInputViewModel?,
customEmojiReplaceableTextInput: CustomEmojiReplaceableTextInput, // customEmojiReplaceableTextInput: CustomEmojiReplaceableTextInput,
disposeBag: inout Set<AnyCancellable> // disposeBag: inout Set<AnyCancellable>
) { // ) {
guard let viewModel = viewModel else { return } // guard let viewModel = viewModel else { return }
viewModel.isCustomEmojiComposing // viewModel.isCustomEmojiComposing
.receive(on: DispatchQueue.main) // .receive(on: DispatchQueue.main)
.sink { [weak viewModel] isCustomEmojiComposing in // .sink { [weak viewModel] isCustomEmojiComposing in
guard let viewModel = viewModel else { return } // guard let viewModel = viewModel else { return }
customEmojiReplaceableTextInput.inputView = isCustomEmojiComposing ? viewModel.customEmojiPickerInputView : nil // customEmojiReplaceableTextInput.inputView = isCustomEmojiComposing ? viewModel.customEmojiPickerInputView : nil
customEmojiReplaceableTextInput.reloadInputViews() // customEmojiReplaceableTextInput.reloadInputViews()
viewModel.append(customEmojiReplaceableTextInput: customEmojiReplaceableTextInput) // viewModel.append(customEmojiReplaceableTextInput: customEmojiReplaceableTextInput)
} // }
.store(in: &disposeBag) // .store(in: &disposeBag)
} // }
} }

View File

@ -0,0 +1,12 @@
//
// CustomEmojiPickerSection.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-3-24.
//
import Foundation
public enum CustomEmojiPickerSection: Equatable, Hashable {
case emoji(name: String)
}

View File

@ -12,7 +12,6 @@ import MastodonSDK
import CoreData import CoreData
import CoreDataStack import CoreDataStack
import CommonOSLog import CommonOSLog
import MastodonCore
extension APIService { extension APIService {

View File

@ -79,7 +79,6 @@ extension AuthenticationService {
public func activeMastodonUser(domain: String, userID: MastodonUser.ID) async throws -> Bool { public func activeMastodonUser(domain: String, userID: MastodonUser.ID) async throws -> Bool {
var isActive = false var isActive = false
var _mastodonAuthentication: MastodonAuthentication?
let managedObjectContext = backgroundManagedObjectContext let managedObjectContext = backgroundManagedObjectContext
@ -91,7 +90,6 @@ extension AuthenticationService {
return return
} }
mastodonAuthentication.update(activedAt: Date()) mastodonAuthentication.update(activedAt: Date())
_mastodonAuthentication = mastodonAuthentication
isActive = true isActive = true
} }

View File

@ -1,25 +1,20 @@
// //
// AutoCompleteSection.swift // AutoCompleteSection+Diffable.swift
// Mastodon //
// //
// Created by MainasuK Cirno on 2021-5-17. // Created by MainasuK on 22/10/10.
// //
import UIKit import UIKit
import MastodonSDK
import MastodonMeta
import MastodonAsset
import MastodonLocalization
import MastodonCore import MastodonCore
import MastodonSDK
enum AutoCompleteSection: Equatable, Hashable { import MastodonLocalization
case main import MastodonMeta
}
extension AutoCompleteSection { extension AutoCompleteSection {
static func tableViewDiffableDataSource( public static func tableViewDiffableDataSource(
for tableView: UITableView tableView: UITableView
) -> UITableViewDiffableDataSource<AutoCompleteSection, AutoCompleteItem> { ) -> UITableViewDiffableDataSource<AutoCompleteSection, AutoCompleteItem> {
UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item in UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item in
switch item { switch item {

View File

@ -0,0 +1,59 @@
//
// CustomEmojiPickerSection+Diffable.swift
//
//
// Created by MainasuK on 22/10/10.
//
import Foundation
import MastodonCore
extension CustomEmojiPickerSection {
// static func collectionViewDiffableDataSource(
// collectionView: UICollectionView,
// dependency: NeedsDependency
// ) -> UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem> {
// let dataSource = UICollectionViewDiffableDataSource<CustomEmojiPickerSection, CustomEmojiPickerItem>(collectionView: collectionView) { [weak dependency] collectionView, indexPath, item -> UICollectionViewCell? in
// guard let _ = dependency else { return nil }
// switch item {
// case .emoji(let attribute):
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: CustomEmojiPickerItemCollectionViewCell.self), for: indexPath) as! CustomEmojiPickerItemCollectionViewCell
// let placeholder = UIImage.placeholder(size: CustomEmojiPickerItemCollectionViewCell.itemSize, color: .systemFill)
// .af.imageRounded(withCornerRadius: 4)
//
// let isAnimated = !UserDefaults.shared.preferredStaticEmoji
// let url = URL(string: isAnimated ? attribute.emoji.url : attribute.emoji.staticURL)
// cell.emojiImageView.sd_setImage(
// with: url,
// placeholderImage: placeholder,
// options: [],
// context: nil
// )
// cell.accessibilityLabel = attribute.emoji.shortcode
// return cell
// }
// }
//
// dataSource.supplementaryViewProvider = { [weak dataSource] collectionView, kind, indexPath -> UICollectionReusableView? in
// guard let dataSource = dataSource else { return nil }
// let sections = dataSource.snapshot().sectionIdentifiers
// guard indexPath.section < sections.count else { return nil }
// let section = sections[indexPath.section]
//
// switch kind {
// case String(describing: CustomEmojiPickerHeaderCollectionReusableView.self):
// let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: CustomEmojiPickerHeaderCollectionReusableView.self), for: indexPath) as! CustomEmojiPickerHeaderCollectionReusableView
// switch section {
// case .emoji(let name):
// header.titleLabel.text = name
// }
// return header
// default:
// assertionFailure()
// return nil
// }
// }
//
// return dataSource
// }
}

View File

@ -88,7 +88,7 @@ extension AutoCompleteViewController {
]) ])
tableView.delegate = self tableView.delegate = self
viewModel.setupDiffableDataSource(for: tableView) // viewModel.setupDiffableDataSource(tableView: tableView)
// bind to layout chevron // bind to layout chevron
viewModel.symbolBoundingRect viewModel.symbolBoundingRect

View File

@ -0,0 +1,22 @@
//
// AutoCompleteViewModel+Diffable.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-5-17.
//
import UIKit
extension AutoCompleteViewModel {
// func setupDiffableDataSource(
// tableView: UITableView
// ) {
// diffableDataSource = AutoCompleteSection.tableViewDiffableDataSource(for: tableView)
//
// var snapshot = NSDiffableDataSourceSnapshot<AutoCompleteSection, AutoCompleteItem>()
// snapshot.appendSections([.main])
// diffableDataSource?.apply(snapshot)
// }
}

View File

@ -12,16 +12,12 @@ import MastodonSDK
import MastodonCore import MastodonCore
extension AutoCompleteViewModel { extension AutoCompleteViewModel {
class State: GKState, NamingState { class State: GKState {
let logger = Logger(subsystem: "AutoCompleteViewModel.State", category: "StateMachine") let logger = Logger(subsystem: "AutoCompleteViewModel.State", category: "StateMachine")
let id = UUID() let id = UUID()
var name: String {
String(describing: Self.self)
}
weak var viewModel: AutoCompleteViewModel? weak var viewModel: AutoCompleteViewModel?
init(viewModel: AutoCompleteViewModel) { init(viewModel: AutoCompleteViewModel) {
@ -30,8 +26,10 @@ extension AutoCompleteViewModel {
override func didEnter(from previousState: GKState?) { override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState) super.didEnter(from: previousState)
let previousState = previousState as? AutoCompleteViewModel.State
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")") let from = previousState.flatMap { String(describing: $0) } ?? "nil"
let to = String(describing: self)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(from) -> \(to)")
} }
@MainActor @MainActor
@ -40,7 +38,7 @@ extension AutoCompleteViewModel {
} }
deinit { deinit {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(self.name)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] \(String(describing: self))")
} }
} }
} }

View File

@ -10,10 +10,8 @@ import FLAnimatedImage
import MetaTextKit import MetaTextKit
import MastodonAsset import MastodonAsset
import MastodonLocalization import MastodonLocalization
import MastodonUI
public final class AutoCompleteTableViewCell: UITableViewCell {
final class AutoCompleteTableViewCell: UITableViewCell {
static let avatarImageSize = CGSize(width: 42, height: 42) static let avatarImageSize = CGSize(width: 42, height: 42)
static let avatarImageCornerRadius: CGFloat = 4 static let avatarImageCornerRadius: CGFloat = 4
@ -51,17 +49,17 @@ final class AutoCompleteTableViewCell: UITableViewCell {
let separatorLine = UIView.separatorLine let separatorLine = UIView.separatorLine
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
_init() _init()
} }
required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
_init() _init()
} }
override func setHighlighted(_ highlighted: Bool, animated: Bool) { public override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated) super.setHighlighted(highlighted, animated: animated)
// workaround for hitTest trigger highlighted issue // workaround for hitTest trigger highlighted issue

View File

@ -8,11 +8,93 @@
import SwiftUI import SwiftUI
public struct ComposeContentView: View { public struct ComposeContentView: View {
@ObservedObject var viewModel: ComposeContentViewModel
@State var contentOffsetDelta: CGFloat = .zero
public var body: some View { public var body: some View {
ScrollView { ScrollView {
VStack { VStack(spacing: .zero) {
Text("Hello") GeometryReader { geometry in
} Color.clear.preference(
} key: ScrollOffsetPreferenceKey.self,
} value: geometry.frame(in: .named("scrollView")).origin
)
}.frame(width: 0, height: 0)
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { offset in
print("contentOffset: \(offset)")
}
VStack {
Text("Reply")
}
.frame(height: 100)
.frame(maxWidth: .infinity)
.background(Color.blue)
.background(
GeometryReader { geometry in
Color.clear.preference(
key: ViewFramePreferenceKey.self,
value: geometry.frame(in: .named("scrollView"))
)
}
.onPreferenceChange(ViewFramePreferenceKey.self) { frame in
print("reply frame: \(frame)")
}
)
VStack {
Text("Content")
}
.frame(maxWidth: .infinity)
.background(Color.orange)
} // end VStack
.offset(y: contentOffsetDelta)
} // end ScrollView
.coordinateSpace(name: "scrollView")
} // end body
} }
private struct ScrollOffsetPreferenceKey: PreferenceKey {
static var defaultValue: CGPoint = .zero
static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) { }
}
private struct ViewFramePreferenceKey: PreferenceKey {
static var defaultValue: CGRect = .zero
static func reduce(value: inout CGRect, nextValue: () -> CGRect) { }
}
//struct ScrollView<Content: View>: View {
// let axes: Axis.Set
// let showsIndicators: Bool
// let offsetChanged: (CGPoint) -> Void
// let content: Content
//
// init(
// axes: Axis.Set = .vertical,
// showsIndicators: Bool = true,
// offsetChanged: @escaping (CGPoint) -> Void = { _ in },
// @ViewBuilder content: () -> Content
// ) {
// self.axes = axes
// self.showsIndicators = showsIndicators
// self.offsetChanged = offsetChanged
// self.content = content()
// }
//
// var body: some View {
// SwiftUI.ScrollView(axes, showsIndicators: showsIndicators) {
// GeometryReader { geometry in
// Color.clear.preference(
// key: ScrollOffsetPreferenceKey.self,
// value: geometry.frame(in: .named("scrollView")).origin
// )
// }.frame(width: 0, height: 0)
// content
// }
// .coordinateSpace(name: "scrollView")
// .onPreferenceChange(ScrollOffsetPreferenceKey.self, perform: offsetChanged)
// }
//}

View File

@ -7,18 +7,47 @@
import os.log import os.log
import UIKit import UIKit
import SwiftUI
public final class ComposeContentViewController: UIViewController { public final class ComposeContentViewController: UIViewController {
let logger = Logger(subsystem: "ComposeContentViewController", category: "ViewController") let logger = Logger(subsystem: "ComposeContentViewController", category: "ViewController")
public var viewModel: ComposeContentViewModel!
let tableView: ComposeTableView = {
let tableView = ComposeTableView()
tableView.alwaysBounceVertical = true
tableView.separatorStyle = .none
tableView.tableFooterView = UIView()
return tableView
}()
deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
}
} }
extension ComposeContentViewController { extension ComposeContentViewController {
public override func viewDidLoad() { public override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
tableView.delegate = self
viewModel.setupDataSource(tableView: tableView)
} }
} }
// MARK: - UITableViewDelegate
extension ComposeContentViewController: UITableViewDelegate { }

View File

@ -0,0 +1,90 @@
//
// ComposeContentViewModel+DataSource.swift
//
//
// Created by MainasuK on 22/10/10.
//
import UIKit
import MastodonCore
import CoreDataStack
extension ComposeContentViewModel {
func setupDataSource(
tableView: UITableView
) {
tableView.dataSource = self
setupTableViewCell(tableView: tableView)
}
}
extension ComposeContentViewModel {
enum Section: CaseIterable {
case replyTo
case status
case attachment
case poll
}
private func setupTableViewCell(tableView: UITableView) {
switch kind {
case .post:
break
case .reply(let status):
let cell = composeReplyToTableViewCell
// bind frame publisher
// cell.framePublisher
// .receive(on: DispatchQueue.main)
// .assign(to: \.repliedToCellFrame, on: self)
// .store(in: &cell.disposeBag)
// set initial width
cell.statusView.frame.size.width = tableView.frame.width
// configure status
context.managedObjectContext.performAndWait {
guard let replyTo = status.object(in: context.managedObjectContext) else { return }
cell.statusView.configure(status: replyTo)
}
case .hashtag(let hashtag):
break
case .mention(let user):
break
}
}
}
extension ComposeContentViewModel: UITableViewDataSource {
public func numberOfSections(in tableView: UITableView) -> Int {
return Section.allCases.count
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch Section.allCases[section] {
case .replyTo:
switch kind {
case .reply: return 1
default: return 0
}
case .status: return 1
case .attachment: return 1
case .poll: return 1
}
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch Section.allCases[indexPath.section] {
case .replyTo:
return composeReplyToTableViewCell
case .status:
return UITableViewCell()
case .attachment:
return UITableViewCell()
case .poll:
return UITableViewCell()
}
}
}

View File

@ -6,15 +6,41 @@
// //
import Foundation import Foundation
import CoreDataStack
import MastodonCore import MastodonCore
final class ComposeContentViewModel: ObservableObject { public final class ComposeContentViewModel: NSObject, ObservableObject {
// tableViewCell
let composeReplyToTableViewCell = ComposeReplyToTableViewCell()
// input // input
let context: AppContext let context: AppContext
let kind: Kind
init(context: AppContext) {
public init(
context: AppContext,
kind: Kind
) {
self.context = context self.context = context
self.kind = kind
super.init()
// end init
} }
} }
extension ComposeContentViewModel {
public enum Kind {
case post
case hashtag(hashtag: String)
case mention(user: ManagedObjectRecord<MastodonUser>)
case reply(status: ManagedObjectRecord<Status>)
}
public enum ViewState {
case fold // snap to input
case expand // snap to reply
}
}

View File

@ -1,5 +1,5 @@
// //
// ComposeRepliedToStatusContentTableViewCell.swift // ComposeReplyToTableViewCell.swift
// Mastodon // Mastodon
// //
// Created by MainasuK Cirno on 2021-6-28. // Created by MainasuK Cirno on 2021-6-28.
@ -7,15 +7,14 @@
import UIKit import UIKit
import Combine import Combine
import MastodonUI
final class ComposeRepliedToStatusContentTableViewCell: UITableViewCell { final class ComposeReplyToTableViewCell: UITableViewCell {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
let statusView = StatusView() let statusView = StatusView()
let framePublisher = PassthroughSubject<CGRect, Never>() @Published var framePublisher: CGRect = .zero
override func prepareForReuse() { override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
@ -36,12 +35,12 @@ final class ComposeRepliedToStatusContentTableViewCell: UITableViewCell {
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
framePublisher.send(bounds) framePublisher = bounds
} }
} }
extension ComposeRepliedToStatusContentTableViewCell { extension ComposeReplyToTableViewCell {
private func _init() { private func _init() {
selectionStyle = .none selectionStyle = .none

View File

@ -0,0 +1,172 @@
//
// ComposeStatusAttachmentTableViewCell.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-6-29.
//
import UIKit
import SwiftUI
import Combine
import AlamofireImage
import MastodonAsset
import MastodonCore
import MastodonLocalization
import UIHostingConfigurationBackport
//final class ComposeStatusAttachmentTableViewCell: UITableViewCell {
//
// private(set) var dataSource: UICollectionViewDiffableDataSource<ComposeStatusAttachmentSection, ComposeStatusAttachmentItem>!
// weak var composeStatusAttachmentCollectionViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate?
// var observations = Set<NSKeyValueObservation>()
//
// private static func createLayout() -> UICollectionViewLayout {
// let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
// let item = NSCollectionLayoutItem(layoutSize: itemSize)
// let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
// let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
// let section = NSCollectionLayoutSection(group: group)
// section.contentInsetsReference = .readableContent
// return UICollectionViewCompositionalLayout(section: section)
// }
//
// private(set) var collectionViewHeightLayoutConstraint: NSLayoutConstraint!
// let collectionView: UICollectionView = {
// let collectionViewLayout = ComposeStatusAttachmentTableViewCell.createLayout()
// let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
// collectionView.register(ComposeStatusAttachmentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self))
// collectionView.backgroundColor = .clear
// collectionView.alwaysBounceVertical = true
// collectionView.isScrollEnabled = false
// return collectionView
// }()
// let collectionViewHeightDidUpdate = PassthroughSubject<Void, Never>()
//
// override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
// super.init(style: style, reuseIdentifier: reuseIdentifier)
// _init()
// }
//
// required init?(coder: NSCoder) {
// super.init(coder: coder)
// _init()
// }
//
//}
//
//extension ComposeStatusAttachmentTableViewCell {
//
// private func _init() {
// backgroundColor = .clear
// contentView.backgroundColor = .clear
//
// collectionView.translatesAutoresizingMaskIntoConstraints = false
// contentView.addSubview(collectionView)
// collectionViewHeightLayoutConstraint = collectionView.heightAnchor.constraint(equalToConstant: 200).priority(.defaultHigh)
// NSLayoutConstraint.activate([
// collectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
// collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
// collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
// collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
// collectionViewHeightLayoutConstraint,
// ])
//
// collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, _ in
// guard let self = self else { return }
// self.collectionViewHeightLayoutConstraint.constant = collectionView.contentSize.height
// self.collectionViewHeightDidUpdate.send()
// }
// .store(in: &observations)
//
// self.dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) {
// [weak self] collectionView, indexPath, item -> UICollectionViewCell? in
// guard let _ = self else { return UICollectionViewCell() }
// switch item {
// case .attachment:
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self), for: indexPath) as! ComposeStatusAttachmentCollectionViewCell
// cell.contentConfiguration = UIHostingConfigurationBackport {
// HStack {
// Image(systemName: "star")
// Text("Favorites")
// Spacer()
// }
// }
//// cell.attachmentContainerView.descriptionTextView.text = attachmentService.description.value
//// cell.delegate = self.composeStatusAttachmentCollectionViewCellDelegate
//// attachmentService.thumbnailImage
//// .receive(on: DispatchQueue.main)
//// .sink { [weak cell] thumbnailImage in
//// guard let cell = cell else { return }
//// let size = cell.attachmentContainerView.previewImageView.frame.size != .zero ? cell.attachmentContainerView.previewImageView.frame.size : CGSize(width: 1, height: 1)
//// guard let image = thumbnailImage else {
//// let placeholder = UIImage.placeholder(
//// size: size,
//// color: ThemeService.shared.currentTheme.value.systemGroupedBackgroundColor
//// )
//// .af.imageRounded(
//// withCornerRadius: AttachmentContainerView.containerViewCornerRadius
//// )
//// cell.attachmentContainerView.previewImageView.image = placeholder
//// return
//// }
//// // cannot get correct size. set corner radius on layer
//// cell.attachmentContainerView.previewImageView.image = image
//// }
//// .store(in: &cell.disposeBag)
//// Publishers.CombineLatest(
//// attachmentService.uploadStateMachineSubject.eraseToAnyPublisher(),
//// attachmentService.error.eraseToAnyPublisher()
//// )
//// .receive(on: DispatchQueue.main)
//// .sink { [weak cell, weak attachmentService] uploadState, error in
//// guard let cell = cell else { return }
//// guard let attachmentService = attachmentService else { return }
//// cell.attachmentContainerView.emptyStateView.isHidden = error == nil
//// cell.attachmentContainerView.descriptionBackgroundView.isHidden = error != nil
//// if let error = error {
//// cell.attachmentContainerView.activityIndicatorView.stopAnimating()
//// cell.attachmentContainerView.emptyStateView.label.text = error.localizedDescription
//// } else {
//// guard let uploadState = uploadState else { return }
//// switch uploadState {
//// case is MastodonAttachmentService.UploadState.Finish:
//// cell.attachmentContainerView.activityIndicatorView.stopAnimating()
//// case is MastodonAttachmentService.UploadState.Fail:
//// cell.attachmentContainerView.activityIndicatorView.stopAnimating()
//// // FIXME: not display
//// cell.attachmentContainerView.emptyStateView.label.text = {
//// if let file = attachmentService.file.value {
//// switch file {
//// case .jpeg, .png, .gif:
//// return L10n.Scene.Compose.Attachment.attachmentBroken(L10n.Scene.Compose.Attachment.photo)
//// case .other:
//// return L10n.Scene.Compose.Attachment.attachmentBroken(L10n.Scene.Compose.Attachment.video)
//// }
//// } else {
//// return L10n.Scene.Compose.Attachment.attachmentBroken(L10n.Scene.Compose.Attachment.photo)
//// }
//// }()
//// default:
//// break
//// }
//// }
//// }
//// .store(in: &cell.disposeBag)
//// NotificationCenter.default.publisher(
//// for: UITextView.textDidChangeNotification,
//// object: cell.attachmentContainerView.descriptionTextView
//// )
//// .receive(on: DispatchQueue.main)
//// .sink { notification in
//// guard let textField = notification.object as? UITextView else { return }
//// let text = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines)
//// attachmentService.description.value = text
//// }
//// .store(in: &cell.disposeBag)
// return cell
// }
// }
// }
//
//}
//

View File

@ -0,0 +1,172 @@
//
// ComposeStatusContentTableViewCell.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-6-28.
//
import os.log
import UIKit
import Combine
import MetaTextKit
import UITextView_Placeholder
import MastodonAsset
import MastodonLocalization
import MastodonUI
//protocol ComposeStatusContentTableViewCellDelegate: AnyObject {
// func composeStatusContentTableViewCell(_ cell: ComposeStatusContentTableViewCell, textViewShouldBeginEditing textView: UITextView) -> Bool
//}
final class ComposeStatusContentTableViewCell: UITableViewCell {
// let logger = Logger(subsystem: "ComposeStatusContentTableViewCell", category: "View")
//
// var disposeBag = Set<AnyCancellable>()
// weak var delegate: ComposeStatusContentTableViewCellDelegate?
//
// let statusView = StatusView()
//
// let statusContentWarningEditorView = StatusContentWarningEditorView()
//
// let textEditorViewContainerView = UIView()
//
// static let metaTextViewTag: Int = 333
// let metaText: MetaText = {
// let metaText = MetaText()
// metaText.textView.backgroundColor = .clear
// metaText.textView.isScrollEnabled = false
// metaText.textView.keyboardType = .twitter
// metaText.textView.textDragInteraction?.isEnabled = false // disable drag for link and attachment
// metaText.textView.textContainer.lineFragmentPadding = 10 // leading inset
// metaText.textView.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
// metaText.textView.attributedPlaceholder = {
// var attributes = metaText.textAttributes
// attributes[.foregroundColor] = Asset.Colors.Label.secondary.color
// return NSAttributedString(
// string: L10n.Scene.Compose.contentInputPlaceholder,
// attributes: attributes
// )
// }()
// metaText.paragraphStyle = {
// let style = NSMutableParagraphStyle()
// style.lineSpacing = 5
// style.paragraphSpacing = 0
// return style
// }()
// metaText.textAttributes = [
// .font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)),
// .foregroundColor: Asset.Colors.Label.primary.color,
// ]
// metaText.linkAttributes = [
// .font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold)),
// .foregroundColor: Asset.Colors.brand.color,
// ]
// return metaText
// }()
//
// // output
// let contentWarningContent = PassthroughSubject<String, Never>()
//
// override func prepareForReuse() {
// super.prepareForReuse()
//
// metaText.delegate = nil
// metaText.textView.delegate = nil
// }
//
// override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
// super.init(style: style, reuseIdentifier: reuseIdentifier)
// _init()
// }
//
// required init?(coder: NSCoder) {
// super.init(coder: coder)
// _init()
// }
}
extension ComposeStatusContentTableViewCell {
// private func _init() {
// selectionStyle = .none
// layer.zPosition = 999
// backgroundColor = .clear
// preservesSuperviewLayoutMargins = true
//
// let containerStackView = UIStackView()
// containerStackView.axis = .vertical
// containerStackView.translatesAutoresizingMaskIntoConstraints = false
// contentView.addSubview(containerStackView)
// NSLayoutConstraint.activate([
// containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor),
// containerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
// containerStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
// containerStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
// ])
// containerStackView.preservesSuperviewLayoutMargins = true
//
// containerStackView.addArrangedSubview(statusContentWarningEditorView)
// statusContentWarningEditorView.setContentHuggingPriority(.required - 1, for: .vertical)
//
// let statusContainerView = UIView()
// statusContainerView.preservesSuperviewLayoutMargins = true
// containerStackView.addArrangedSubview(statusContainerView)
// statusView.translatesAutoresizingMaskIntoConstraints = false
// statusContainerView.addSubview(statusView)
// NSLayoutConstraint.activate([
// statusView.topAnchor.constraint(equalTo: statusContainerView.topAnchor, constant: 20),
// statusView.leadingAnchor.constraint(equalTo: statusContainerView.leadingAnchor),
// statusView.trailingAnchor.constraint(equalTo: statusContainerView.trailingAnchor),
// statusView.bottomAnchor.constraint(equalTo: statusContainerView.bottomAnchor),
// ])
// statusView.setup(style: .composeStatusAuthor)
//
// containerStackView.addArrangedSubview(textEditorViewContainerView)
// metaText.textView.translatesAutoresizingMaskIntoConstraints = false
// textEditorViewContainerView.addSubview(metaText.textView)
// NSLayoutConstraint.activate([
// metaText.textView.topAnchor.constraint(equalTo: textEditorViewContainerView.topAnchor),
// metaText.textView.leadingAnchor.constraint(equalTo: textEditorViewContainerView.layoutMarginsGuide.leadingAnchor),
// metaText.textView.trailingAnchor.constraint(equalTo: textEditorViewContainerView.layoutMarginsGuide.trailingAnchor),
// metaText.textView.bottomAnchor.constraint(equalTo: textEditorViewContainerView.bottomAnchor),
// metaText.textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 64).priority(.defaultHigh),
// ])
// statusContentWarningEditorView.textView.delegate = self
// }
}
// MARK: - UITextViewDelegate
//extension ComposeStatusContentTableViewCell: UITextViewDelegate {
//
// func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
// return delegate?.composeStatusContentTableViewCell(self, textViewShouldBeginEditing: textView) ?? true
// }
//
// func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// switch textView {
// case statusContentWarningEditorView.textView:
// // disable input line break
// guard text != "\n" else { return false }
// return true
// default:
// assertionFailure()
// return true
// }
// }
//
// func textViewDidChange(_ textView: UITextView) {
// logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): text: \(textView.text ?? "<nil>")")
// guard textView === statusContentWarningEditorView.textView else { return }
// // replace line break with space
// // needs check input state to prevent break the IME
// if textView.markedTextRange == nil {
// textView.text = textView.text.replacingOccurrences(of: "\n", with: " ")
// }
// contentWarningContent.send(textView.text)
// }
//
//}

View File

@ -0,0 +1,209 @@
//
// ComposeStatusPollTableViewCell.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-6-29.
//
import os.log
import UIKit
import Combine
import MastodonAsset
import MastodonLocalization
//protocol ComposeStatusPollTableViewCellDelegate: AnyObject {
// func composeStatusPollTableViewCell(_ cell: ComposeStatusPollTableViewCell, pollOptionAttributesDidReorder options: [ComposeStatusPollItem.PollOptionAttribute])
//}
//
//final class ComposeStatusPollTableViewCell: UITableViewCell {
//
// let logger = Logger(subsystem: "ComposeStatusPollTableViewCell", category: "UI")
//
// private(set) var dataSource: UICollectionViewDiffableDataSource<ComposeStatusPollSection, ComposeStatusPollItem>!
// var observations = Set<NSKeyValueObservation>()
//
// weak var customEmojiPickerInputViewModel: CustomEmojiPickerInputViewModel?
// weak var delegate: ComposeStatusPollTableViewCellDelegate?
// weak var composeStatusPollOptionCollectionViewCellDelegate: ComposeStatusPollOptionCollectionViewCellDelegate?
// weak var composeStatusPollOptionAppendEntryCollectionViewCellDelegate: ComposeStatusPollOptionAppendEntryCollectionViewCellDelegate?
// weak var composeStatusPollExpiresOptionCollectionViewCellDelegate: ComposeStatusPollExpiresOptionCollectionViewCellDelegate?
//
// private static func createLayout() -> UICollectionViewLayout {
// let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
// let item = NSCollectionLayoutItem(layoutSize: itemSize)
// let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
// let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
// let section = NSCollectionLayoutSection(group: group)
// section.contentInsetsReference = .readableContent
// return UICollectionViewCompositionalLayout(section: section)
// }
//
// private(set) var collectionViewHeightLayoutConstraint: NSLayoutConstraint!
// let collectionView: UICollectionView = {
// let collectionViewLayout = ComposeStatusPollTableViewCell.createLayout()
// let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
// collectionView.register(ComposeStatusPollOptionCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollOptionCollectionViewCell.self))
// collectionView.register(ComposeStatusPollOptionAppendEntryCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollOptionAppendEntryCollectionViewCell.self))
// collectionView.register(ComposeStatusPollExpiresOptionCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusPollExpiresOptionCollectionViewCell.self))
// collectionView.backgroundColor = .clear
// collectionView.alwaysBounceVertical = true
// collectionView.isScrollEnabled = false
// collectionView.dragInteractionEnabled = true
// return collectionView
// }()
// let collectionViewHeightDidUpdate = PassthroughSubject<Void, Never>()
//
// override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
// super.init(style: style, reuseIdentifier: reuseIdentifier)
// _init()
// }
//
// required init?(coder: NSCoder) {
// super.init(coder: coder)
// _init()
// }
//
//}
//
//extension ComposeStatusPollTableViewCell {
//
// private func _init() {
// backgroundColor = .clear
// contentView.backgroundColor = .clear
//
// collectionView.translatesAutoresizingMaskIntoConstraints = false
// contentView.addSubview(collectionView)
// collectionViewHeightLayoutConstraint = collectionView.heightAnchor.constraint(equalToConstant: 300).priority(.defaultHigh)
// NSLayoutConstraint.activate([
// collectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
// collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
// collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
// collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
// collectionViewHeightLayoutConstraint,
// ])
//
// collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, _ in
// guard let self = self else { return }
// self.collectionViewHeightLayoutConstraint.constant = collectionView.contentSize.height
// self.collectionViewHeightDidUpdate.send()
// }
// .store(in: &observations)
//
// self.dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { [
// weak self
// ] collectionView, indexPath, item -> UICollectionViewCell? in
// guard let self = self else { return UICollectionViewCell() }
//
// switch item {
// case .pollOption(let attribute):
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollOptionCollectionViewCell.self), for: indexPath) as! ComposeStatusPollOptionCollectionViewCell
// cell.pollOptionView.optionTextField.text = attribute.option.value
// cell.pollOptionView.optionTextField.placeholder = L10n.Scene.Compose.Poll.optionNumber(indexPath.item + 1)
// cell.pollOption
// .receive(on: DispatchQueue.main)
// .assign(to: \.value, on: attribute.option)
// .store(in: &cell.disposeBag)
// cell.delegate = self.composeStatusPollOptionCollectionViewCellDelegate
// if let customEmojiPickerInputViewModel = self.customEmojiPickerInputViewModel {
// ComposeStatusSection.configureCustomEmojiPicker(viewModel: customEmojiPickerInputViewModel, customEmojiReplaceableTextInput: cell.pollOptionView.optionTextField, disposeBag: &cell.disposeBag)
// }
// return cell
// case .pollOptionAppendEntry:
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollOptionAppendEntryCollectionViewCell.self), for: indexPath) as! ComposeStatusPollOptionAppendEntryCollectionViewCell
// cell.delegate = self.composeStatusPollOptionAppendEntryCollectionViewCellDelegate
// return cell
// case .pollExpiresOption(let attribute):
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusPollExpiresOptionCollectionViewCell.self), for: indexPath) as! ComposeStatusPollExpiresOptionCollectionViewCell
// cell.durationButton.setTitle(L10n.Scene.Compose.Poll.durationTime(attribute.expiresOption.value.title), for: .normal)
// attribute.expiresOption
// .receive(on: DispatchQueue.main)
// .sink { [weak cell] expiresOption in
// guard let cell = cell else { return }
// cell.durationButton.setTitle(L10n.Scene.Compose.Poll.durationTime(expiresOption.title), for: .normal)
// }
// .store(in: &cell.disposeBag)
// cell.delegate = self.composeStatusPollExpiresOptionCollectionViewCellDelegate
// return cell
// }
// }
//
// collectionView.dragDelegate = self
// collectionView.dropDelegate = self
// }
//
//}
//
//// MARK: - UICollectionViewDragDelegate
//extension ComposeStatusPollTableViewCell: UICollectionViewDragDelegate {
//
// func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
// guard let item = dataSource.itemIdentifier(for: indexPath) else { return [] }
// switch item {
// case .pollOption:
// let itemProvider = NSItemProvider(object: String(item.hashValue) as NSString)
// let dragItem = UIDragItem(itemProvider: itemProvider)
// dragItem.localObject = item
// return [dragItem]
// default:
// return []
// }
// }
//
// func collectionView(_ collectionView: UICollectionView, dragSessionIsRestrictedToDraggingApplication session: UIDragSession) -> Bool {
// // drag to app should be the same app
// return true
// }
//}
//
//// MARK: - UICollectionViewDropDelegate
//extension ComposeStatusPollTableViewCell: UICollectionViewDropDelegate {
// // didUpdate
// func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
// guard collectionView.hasActiveDrag,
// let destinationIndexPath = destinationIndexPath,
// let item = dataSource.itemIdentifier(for: destinationIndexPath)
// else {
// return UICollectionViewDropProposal(operation: .forbidden)
// }
//
// switch item {
// case .pollOption:
// return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
// default:
// return UICollectionViewDropProposal(operation: .cancel)
// }
// }
//
// // performDrop
// func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
// guard let dropItem = coordinator.items.first,
// let item = dropItem.dragItem.localObject as? ComposeStatusPollItem,
// case .pollOption = item
// else { return }
//
// guard coordinator.proposal.operation == .move else { return }
// guard let destinationIndexPath = coordinator.destinationIndexPath,
// let _ = collectionView.cellForItem(at: destinationIndexPath) as? ComposeStatusPollOptionCollectionViewCell
// else { return }
//
// var snapshot = dataSource.snapshot()
// guard destinationIndexPath.row < snapshot.itemIdentifiers.count else { return }
// let anchorItem = snapshot.itemIdentifiers[destinationIndexPath.row]
// snapshot.moveItem(item, afterItem: anchorItem)
// dataSource.apply(snapshot)
//
// coordinator.drop(dropItem.dragItem, toItemAt: destinationIndexPath)
// }
//}
//
//extension ComposeStatusPollTableViewCell: UICollectionViewDelegate {
// func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath {
// logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(originalIndexPath.debugDescription) -> \(proposedIndexPath.debugDescription)")
//
// guard let _ = collectionView.cellForItem(at: proposedIndexPath) as? ComposeStatusPollOptionCollectionViewCell else {
// return originalIndexPath
// }
//
// return proposedIndexPath
// }
//}

View File

@ -17,9 +17,9 @@ import UIKit
// they feel broken. Feel free to add your own exceptions if you have custom // they feel broken. Feel free to add your own exceptions if you have custom
// controls that require swiping or dragging to function. // controls that require swiping or dragging to function.
final class ControlContainableScrollView: UIScrollView { public final class ControlContainableScrollView: UIScrollView {
override func touchesShouldCancel(in view: UIView) -> Bool { public override func touchesShouldCancel(in view: UIView) -> Bool {
if view is UIControl if view is UIControl
&& !(view is UITextInput) && !(view is UITextInput)
&& !(view is UISlider) && !(view is UISlider)
@ -32,9 +32,9 @@ final class ControlContainableScrollView: UIScrollView {
} }
final class ControlContainableTableView: UITableView { public final class ControlContainableTableView: UITableView {
override func touchesShouldCancel(in view: UIView) -> Bool { public override func touchesShouldCancel(in view: UIView) -> Bool {
if view is UIControl if view is UIControl
&& !(view is UITextInput) && !(view is UITextInput)
&& !(view is UISlider) && !(view is UISlider)
@ -47,9 +47,9 @@ final class ControlContainableTableView: UITableView {
} }
final class ControlContainableCollectionView: UICollectionView { public final class ControlContainableCollectionView: UICollectionView {
override func touchesShouldCancel(in view: UIView) -> Bool { public override func touchesShouldCancel(in view: UIView) -> Bool {
if view is UIControl if view is UIControl
&& !(view is UITextInput) && !(view is UITextInput)
&& !(view is UISlider) && !(view is UISlider)

View File

@ -9,6 +9,7 @@
import UIKit import UIKit
import Combine import Combine
import CoreData import CoreData
import CoreDataStack
import Photos import Photos
import AlamofireImage import AlamofireImage
import MastodonCore import MastodonCore
@ -178,3 +179,59 @@ extension MediaView.Configuration {
} }
} }
extension MediaView {
public static func configuration(status: Status) -> [MediaView.Configuration] {
func videoInfo(from attachment: MastodonAttachment) -> MediaView.Configuration.VideoInfo {
MediaView.Configuration.VideoInfo(
aspectRadio: attachment.size,
assetURL: attachment.assetURL,
previewURL: attachment.previewURL,
durationMS: attachment.durationMS
)
}
let status = status.reblog ?? status
let attachments = status.attachments
let configurations = attachments.map { attachment -> MediaView.Configuration in
let configuration: MediaView.Configuration = {
switch attachment.kind {
case .image:
let info = MediaView.Configuration.ImageInfo(
aspectRadio: attachment.size,
assetURL: attachment.assetURL
)
return .init(
info: .image(info: info),
blurhash: attachment.blurhash
)
case .video:
let info = videoInfo(from: attachment)
return .init(
info: .video(info: info),
blurhash: attachment.blurhash
)
case .gifv:
let info = videoInfo(from: attachment)
return .init(
info: .gif(info: info),
blurhash: attachment.blurhash
)
case .audio:
let info = videoInfo(from: attachment)
return .init(
info: .video(info: info),
blurhash: attachment.blurhash
)
} // end switch
}()
configuration.load()
configuration.isReveal = status.isMediaSensitive ? status.isSensitiveToggled : true
return configuration
}
return configurations
}
}

View File

@ -7,11 +7,9 @@
import UIKit import UIKit
import Combine import Combine
import MastodonUI
import CoreDataStack import CoreDataStack
import MastodonSDK import MastodonSDK
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
import MastodonMeta import MastodonMeta
import Meta import Meta
@ -125,11 +123,12 @@ extension StatusView {
if let authenticationBox = viewModel.authContext?.mastodonAuthenticationBox { if let authenticationBox = viewModel.authContext?.mastodonAuthenticationBox {
Just(inReplyToAccountID) Just(inReplyToAccountID)
.asyncMap { userID in .asyncMap { userID in
return try await AppContext.shared.apiService.accountInfo( return try await Mastodon.API.Account.accountInfo(
session: .shared,
domain: authenticationBox.domain, domain: authenticationBox.domain,
userID: userID, userID: userID,
authorization: authenticationBox.userAuthorization authorization: authenticationBox.userAuthorization
) ).singleOutput()
} }
.sink { completion in .sink { completion in
// do nothing // do nothing

View File

@ -10,7 +10,7 @@ import UIKit
import Combine import Combine
import MastodonCore import MastodonCore
final class SawToothView: UIView { public final class SawToothView: UIView {
static let widthUint = 8 static let widthUint = 8
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
@ -41,7 +41,7 @@ final class SawToothView: UIView {
setNeedsDisplay() setNeedsDisplay()
} }
override func draw(_ rect: CGRect) { public override func draw(_ rect: CGRect) {
let bezierPath = UIBezierPath() let bezierPath = UIBezierPath()
let bottomY = rect.height let bottomY = rect.height
let topY = 0 let topY = 0

View File

@ -8,18 +8,17 @@
import UIKit import UIKit
import Combine import Combine
import MastodonCore import MastodonCore
import MastodonUI
final class TimelineBottomLoaderTableViewCell: TimelineLoaderTableViewCell { public final class TimelineBottomLoaderTableViewCell: TimelineLoaderTableViewCell {
override func prepareForReuse() { public override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
loadMoreLabel.isHidden = true loadMoreLabel.isHidden = true
loadMoreButton.isHidden = true loadMoreButton.isHidden = true
} }
override func _init() { public override func _init() {
super._init() super._init()
activityIndicatorView.isHidden = false activityIndicatorView.isHidden = false

View File

@ -9,23 +9,22 @@ import UIKit
import Combine import Combine
import MastodonAsset import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI
import MastodonLocalization import MastodonLocalization
class TimelineLoaderTableViewCell: UITableViewCell { open class TimelineLoaderTableViewCell: UITableViewCell {
static let buttonHeight: CGFloat = 44 public static let buttonHeight: CGFloat = 44
static let buttonMargin: CGFloat = 12 public static let buttonMargin: CGFloat = 12
static let cellHeight: CGFloat = buttonHeight + 2 * buttonMargin public static let cellHeight: CGFloat = buttonHeight + 2 * buttonMargin
static let labelFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .medium)) public static let labelFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .medium))
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
private var _disposeBag = Set<AnyCancellable>() private var _disposeBag = Set<AnyCancellable>()
let stackView = UIStackView() public let stackView = UIStackView()
let loadMoreButton: UIButton = { public let loadMoreButton: UIButton = {
let button = HighlightDimmableButton() let button = HighlightDimmableButton()
button.titleLabel?.font = TimelineLoaderTableViewCell.labelFont button.titleLabel?.font = TimelineLoaderTableViewCell.labelFont
button.setTitleColor(ThemeService.tintColor, for: .normal) button.setTitleColor(ThemeService.tintColor, for: .normal)
@ -34,49 +33,49 @@ class TimelineLoaderTableViewCell: UITableViewCell {
return button return button
}() }()
let loadMoreLabel: UILabel = { public let loadMoreLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.font = TimelineLoaderTableViewCell.labelFont label.font = TimelineLoaderTableViewCell.labelFont
return label return label
}() }()
let activityIndicatorView: UIActivityIndicatorView = { public let activityIndicatorView: UIActivityIndicatorView = {
let activityIndicatorView = UIActivityIndicatorView(style: .medium) let activityIndicatorView = UIActivityIndicatorView(style: .medium)
activityIndicatorView.tintColor = Asset.Colors.Label.secondary.color activityIndicatorView.tintColor = Asset.Colors.Label.secondary.color
activityIndicatorView.hidesWhenStopped = true activityIndicatorView.hidesWhenStopped = true
return activityIndicatorView return activityIndicatorView
}() }()
override func prepareForReuse() { public override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
disposeBag.removeAll() disposeBag.removeAll()
} }
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
_init() _init()
} }
required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
_init() _init()
} }
func startAnimating() { public func startAnimating() {
activityIndicatorView.startAnimating() activityIndicatorView.startAnimating()
self.loadMoreButton.isEnabled = false self.loadMoreButton.isEnabled = false
self.loadMoreLabel.textColor = Asset.Colors.Label.secondary.color self.loadMoreLabel.textColor = Asset.Colors.Label.secondary.color
self.loadMoreLabel.text = L10n.Common.Controls.Timeline.Loader.loadingMissingPosts self.loadMoreLabel.text = L10n.Common.Controls.Timeline.Loader.loadingMissingPosts
} }
func stopAnimating() { public func stopAnimating() {
activityIndicatorView.stopAnimating() activityIndicatorView.stopAnimating()
self.loadMoreButton.isEnabled = true self.loadMoreButton.isEnabled = true
self.loadMoreLabel.textColor = ThemeService.tintColor self.loadMoreLabel.textColor = ThemeService.tintColor
self.loadMoreLabel.text = "" self.loadMoreLabel.text = ""
} }
func _init() { open func _init() {
selectionStyle = .none selectionStyle = .none
backgroundColor = .clear backgroundColor = .clear

View File

@ -10,7 +10,7 @@ import Combine
import CoreDataStack import CoreDataStack
extension TimelineMiddleLoaderTableViewCell { extension TimelineMiddleLoaderTableViewCell {
class ViewModel { public class ViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
@Published var isFetching = false @Published var isFetching = false
@ -18,7 +18,7 @@ extension TimelineMiddleLoaderTableViewCell {
} }
extension TimelineMiddleLoaderTableViewCell.ViewModel { extension TimelineMiddleLoaderTableViewCell.ViewModel {
func bind(cell: TimelineMiddleLoaderTableViewCell) { public func bind(cell: TimelineMiddleLoaderTableViewCell) {
$isFetching $isFetching
.sink { isFetching in .sink { isFetching in
if isFetching { if isFetching {
@ -33,7 +33,7 @@ extension TimelineMiddleLoaderTableViewCell.ViewModel {
extension TimelineMiddleLoaderTableViewCell { extension TimelineMiddleLoaderTableViewCell {
func configure( public func configure(
feed: Feed, feed: Feed,
delegate: TimelineMiddleLoaderTableViewCellDelegate? delegate: TimelineMiddleLoaderTableViewCellDelegate?
) { ) {

View File

@ -9,13 +9,12 @@ import Combine
import CoreData import CoreData
import os.log import os.log
import UIKit import UIKit
import MastodonUI
protocol TimelineMiddleLoaderTableViewCellDelegate: AnyObject { public protocol TimelineMiddleLoaderTableViewCellDelegate: AnyObject {
func timelineMiddleLoaderTableViewCell(_ cell: TimelineMiddleLoaderTableViewCell, loadMoreButtonDidPressed button: UIButton) func timelineMiddleLoaderTableViewCell(_ cell: TimelineMiddleLoaderTableViewCell, loadMoreButtonDidPressed button: UIButton)
} }
final class TimelineMiddleLoaderTableViewCell: TimelineLoaderTableViewCell { public final class TimelineMiddleLoaderTableViewCell: TimelineLoaderTableViewCell {
weak var delegate: TimelineMiddleLoaderTableViewCellDelegate? weak var delegate: TimelineMiddleLoaderTableViewCellDelegate?
@ -28,7 +27,7 @@ final class TimelineMiddleLoaderTableViewCell: TimelineLoaderTableViewCell {
let topSawToothView = SawToothView() let topSawToothView = SawToothView()
let bottomSawToothView = SawToothView() let bottomSawToothView = SawToothView()
override func _init() { public override func _init() {
super._init() super._init()
loadMoreButton.isHidden = false loadMoreButton.isHidden = false

Some files were not shown because too many files have changed in this diff Show More