mirror of
git://jb55.com/damus
synced 2024-09-30 00:40:45 +00:00
Compare commits
6 Commits
b31b917b70
...
247f313b54
Author | SHA1 | Date | |
---|---|---|---|
|
247f313b54 | ||
|
79fef51f68 | ||
|
181d894df0 | ||
|
250efd9755 | ||
|
671b0b67ce | ||
|
98eddf1337 |
@ -375,7 +375,7 @@
|
|||||||
4CFD502F2A2DA45800A229DB /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD502E2A2DA45800A229DB /* MediaView.swift */; };
|
4CFD502F2A2DA45800A229DB /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD502E2A2DA45800A229DB /* MediaView.swift */; };
|
||||||
4CFF8F5929C9FD1E008DB934 /* DamusPurpleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F5829C9FD1E008DB934 /* DamusPurpleView.swift */; };
|
4CFF8F5929C9FD1E008DB934 /* DamusPurpleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F5829C9FD1E008DB934 /* DamusPurpleView.swift */; };
|
||||||
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; };
|
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; };
|
||||||
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */; };
|
4CFF8F6729CC9E3A008DB934 /* FullScreenCarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* FullScreenCarouselView.swift */; };
|
||||||
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */; };
|
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */; };
|
||||||
4CFF8F6B29CD0079008DB934 /* RepostedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */; };
|
4CFF8F6B29CD0079008DB934 /* RepostedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */; };
|
||||||
4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */; };
|
4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */; };
|
||||||
@ -386,7 +386,7 @@
|
|||||||
504323A72A34915F006AE6DC /* RelayModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A62A34915F006AE6DC /* RelayModel.swift */; };
|
504323A72A34915F006AE6DC /* RelayModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A62A34915F006AE6DC /* RelayModel.swift */; };
|
||||||
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A82A3495B6006AE6DC /* RelayModelCache.swift */; };
|
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A82A3495B6006AE6DC /* RelayModelCache.swift */; };
|
||||||
5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; };
|
5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; };
|
||||||
50A16FFB2AA6C06600DFEC1F /* AVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* AVPlayerView.swift */; };
|
50A16FFB2AA6C06600DFEC1F /* DamusAVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */; };
|
||||||
50A16FFD2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */; };
|
50A16FFD2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */; };
|
||||||
50A16FFF2AA76A0900DFEC1F /* VideoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFE2AA76A0900DFEC1F /* VideoController.swift */; };
|
50A16FFF2AA76A0900DFEC1F /* VideoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFE2AA76A0900DFEC1F /* VideoController.swift */; };
|
||||||
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
||||||
@ -431,8 +431,6 @@
|
|||||||
B59CAD4D2B688D1000677E8B /* MutelistManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B533694D2B66D791008A805E /* MutelistManager.swift */; };
|
B59CAD4D2B688D1000677E8B /* MutelistManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B533694D2B66D791008A805E /* MutelistManager.swift */; };
|
||||||
B5A75C2A2B546D94007AFBC0 /* MuteItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A75C292B546D94007AFBC0 /* MuteItemTests.swift */; };
|
B5A75C2A2B546D94007AFBC0 /* MuteItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A75C292B546D94007AFBC0 /* MuteItemTests.swift */; };
|
||||||
B5B4D1432B37D47600844320 /* NdbExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B4D1422B37D47600844320 /* NdbExtensions.swift */; };
|
B5B4D1432B37D47600844320 /* NdbExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B4D1422B37D47600844320 /* NdbExtensions.swift */; };
|
||||||
BA0F0A6F2B36207E001641B2 /* CameraMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0F0A6E2B36207E001641B2 /* CameraMediaView.swift */; };
|
|
||||||
BA10192F2B449556009C57DA /* CameraPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA10192E2B449556009C57DA /* CameraPreview.swift */; };
|
|
||||||
B5C60C202B530D5100C5ECA7 /* MuteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */; };
|
B5C60C202B530D5100C5ECA7 /* MuteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */; };
|
||||||
B5C60C212B530D5600C5ECA7 /* MuteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */; };
|
B5C60C212B530D5600C5ECA7 /* MuteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */; };
|
||||||
B5C60C232B532A8700C5ECA7 /* DamusDuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C60C222B532A8700C5ECA7 /* DamusDuration.swift */; };
|
B5C60C232B532A8700C5ECA7 /* DamusDuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C60C222B532A8700C5ECA7 /* DamusDuration.swift */; };
|
||||||
@ -454,6 +452,7 @@
|
|||||||
D7100C5A2B76FD5100C59298 /* LogoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C592B76FD5100C59298 /* LogoView.swift */; };
|
D7100C5A2B76FD5100C59298 /* LogoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C592B76FD5100C59298 /* LogoView.swift */; };
|
||||||
D7100C5C2B77016700C59298 /* IAPProductStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C5B2B77016700C59298 /* IAPProductStateView.swift */; };
|
D7100C5C2B77016700C59298 /* IAPProductStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C5B2B77016700C59298 /* IAPProductStateView.swift */; };
|
||||||
D7100C5E2B7709ED00C59298 /* PurpleStoreKitManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C5D2B7709ED00C59298 /* PurpleStoreKitManager.swift */; };
|
D7100C5E2B7709ED00C59298 /* PurpleStoreKitManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C5D2B7709ED00C59298 /* PurpleStoreKitManager.swift */; };
|
||||||
|
D71AC4CC2BA8E3480076268E /* VisibilityTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71AC4CB2BA8E3480076268E /* VisibilityTracker.swift */; };
|
||||||
D71DC1EC2A9129C3006E207C /* PostViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71DC1EB2A9129C3006E207C /* PostViewTests.swift */; };
|
D71DC1EC2A9129C3006E207C /* PostViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71DC1EB2A9129C3006E207C /* PostViewTests.swift */; };
|
||||||
D72341192B6864F200E1E135 /* DamusPurpleEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */; };
|
D72341192B6864F200E1E135 /* DamusPurpleEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */; };
|
||||||
D723411A2B6864F200E1E135 /* DamusPurpleEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */; };
|
D723411A2B6864F200E1E135 /* DamusPurpleEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */; };
|
||||||
@ -1296,7 +1295,7 @@
|
|||||||
4CFD502E2A2DA45800A229DB /* MediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = "<group>"; };
|
4CFD502E2A2DA45800A229DB /* MediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F5829C9FD1E008DB934 /* DamusPurpleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleView.swift; sourceTree = "<group>"; };
|
4CFF8F5829C9FD1E008DB934 /* DamusPurpleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleView.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenuModifier.swift; sourceTree = "<group>"; };
|
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenuModifier.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
|
4CFF8F6629CC9E3A008DB934 /* FullScreenCarouselView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenCarouselView.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContainerView.swift; sourceTree = "<group>"; };
|
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContainerView.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostedEvent.swift; sourceTree = "<group>"; };
|
4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostedEvent.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideEventView.swift; sourceTree = "<group>"; };
|
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideEventView.swift; sourceTree = "<group>"; };
|
||||||
@ -1307,7 +1306,7 @@
|
|||||||
504323A62A34915F006AE6DC /* RelayModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayModel.swift; sourceTree = "<group>"; };
|
504323A62A34915F006AE6DC /* RelayModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayModel.swift; sourceTree = "<group>"; };
|
||||||
504323A82A3495B6006AE6DC /* RelayModelCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayModelCache.swift; sourceTree = "<group>"; };
|
504323A82A3495B6006AE6DC /* RelayModelCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayModelCache.swift; sourceTree = "<group>"; };
|
||||||
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = "<group>"; };
|
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = "<group>"; };
|
||||||
50A16FFA2AA6C06600DFEC1F /* AVPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerView.swift; sourceTree = "<group>"; };
|
50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusAVPlayerView.swift; sourceTree = "<group>"; };
|
||||||
50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusVideoPlayerViewModel.swift; sourceTree = "<group>"; };
|
50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusVideoPlayerViewModel.swift; sourceTree = "<group>"; };
|
||||||
50A16FFE2AA76A0900DFEC1F /* VideoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoController.swift; sourceTree = "<group>"; };
|
50A16FFE2AA76A0900DFEC1F /* VideoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoController.swift; sourceTree = "<group>"; };
|
||||||
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
||||||
@ -1351,8 +1350,6 @@
|
|||||||
B57B4C652B312C3700A232C0 /* NostrAuth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NostrAuth.swift; sourceTree = "<group>"; };
|
B57B4C652B312C3700A232C0 /* NostrAuth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NostrAuth.swift; sourceTree = "<group>"; };
|
||||||
B5A75C292B546D94007AFBC0 /* MuteItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteItemTests.swift; sourceTree = "<group>"; usesTabs = 0; };
|
B5A75C292B546D94007AFBC0 /* MuteItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteItemTests.swift; sourceTree = "<group>"; usesTabs = 0; };
|
||||||
B5B4D1422B37D47600844320 /* NdbExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbExtensions.swift; sourceTree = "<group>"; usesTabs = 0; };
|
B5B4D1422B37D47600844320 /* NdbExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbExtensions.swift; sourceTree = "<group>"; usesTabs = 0; };
|
||||||
BA0F0A6E2B36207E001641B2 /* CameraMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraMediaView.swift; sourceTree = "<group>"; };
|
|
||||||
BA10192E2B449556009C57DA /* CameraPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraPreview.swift; sourceTree = "<group>"; };
|
|
||||||
B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteItem.swift; sourceTree = "<group>"; usesTabs = 0; };
|
B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteItem.swift; sourceTree = "<group>"; usesTabs = 0; };
|
||||||
B5C60C222B532A8700C5ECA7 /* DamusDuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusDuration.swift; sourceTree = "<group>"; usesTabs = 0; };
|
B5C60C222B532A8700C5ECA7 /* DamusDuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusDuration.swift; sourceTree = "<group>"; usesTabs = 0; };
|
||||||
BA3759892ABCCDE30018D73B /* ImageResizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageResizer.swift; sourceTree = "<group>"; };
|
BA3759892ABCCDE30018D73B /* ImageResizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageResizer.swift; sourceTree = "<group>"; };
|
||||||
@ -1373,6 +1370,7 @@
|
|||||||
D7100C592B76FD5100C59298 /* LogoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogoView.swift; sourceTree = "<group>"; };
|
D7100C592B76FD5100C59298 /* LogoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogoView.swift; sourceTree = "<group>"; };
|
||||||
D7100C5B2B77016700C59298 /* IAPProductStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAPProductStateView.swift; sourceTree = "<group>"; };
|
D7100C5B2B77016700C59298 /* IAPProductStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAPProductStateView.swift; sourceTree = "<group>"; };
|
||||||
D7100C5D2B7709ED00C59298 /* PurpleStoreKitManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurpleStoreKitManager.swift; sourceTree = "<group>"; };
|
D7100C5D2B7709ED00C59298 /* PurpleStoreKitManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurpleStoreKitManager.swift; sourceTree = "<group>"; };
|
||||||
|
D71AC4CB2BA8E3480076268E /* VisibilityTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibilityTracker.swift; sourceTree = "<group>"; };
|
||||||
D71DC1EB2A9129C3006E207C /* PostViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewTests.swift; sourceTree = "<group>"; };
|
D71DC1EB2A9129C3006E207C /* PostViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewTests.swift; sourceTree = "<group>"; };
|
||||||
D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleEnvironment.swift; sourceTree = "<group>"; };
|
D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleEnvironment.swift; sourceTree = "<group>"; };
|
||||||
D723C38D2AB8D83400065664 /* ContentFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentFilters.swift; sourceTree = "<group>"; };
|
D723C38D2AB8D83400065664 /* ContentFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentFilters.swift; sourceTree = "<group>"; };
|
||||||
@ -1728,7 +1726,7 @@
|
|||||||
4C1A9A2929DDF54400516EAC /* DamusVideoPlayer.swift */,
|
4C1A9A2929DDF54400516EAC /* DamusVideoPlayer.swift */,
|
||||||
50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */,
|
50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */,
|
||||||
50A16FFE2AA76A0900DFEC1F /* VideoController.swift */,
|
50A16FFE2AA76A0900DFEC1F /* VideoController.swift */,
|
||||||
50A16FFA2AA6C06600DFEC1F /* AVPlayerView.swift */,
|
50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */,
|
||||||
);
|
);
|
||||||
path = Video;
|
path = Video;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1977,6 +1975,7 @@
|
|||||||
4C75EFA227FA576C0006080F /* Views */ = {
|
4C75EFA227FA576C0006080F /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D71AC4CA2BA8E3320076268E /* Extensions */,
|
||||||
BA3759952ABCCF360018D73B /* Camera */,
|
BA3759952ABCCF360018D73B /* Camera */,
|
||||||
F71694E82A66221E001F4053 /* Onboarding */,
|
F71694E82A66221E001F4053 /* Onboarding */,
|
||||||
4C190F232A547D1700027FD5 /* NostrScript */,
|
4C190F232A547D1700027FD5 /* NostrScript */,
|
||||||
@ -2653,7 +2652,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */,
|
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */,
|
||||||
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */,
|
4CFF8F6629CC9E3A008DB934 /* FullScreenCarouselView.swift */,
|
||||||
6439E013296790CF0020672B /* ProfilePicImageView.swift */,
|
6439E013296790CF0020672B /* ProfilePicImageView.swift */,
|
||||||
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */,
|
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */,
|
||||||
4CFD502E2A2DA45800A229DB /* MediaView.swift */,
|
4CFD502E2A2DA45800A229DB /* MediaView.swift */,
|
||||||
@ -2704,6 +2703,14 @@
|
|||||||
path = Detail;
|
path = Detail;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D71AC4CA2BA8E3320076268E /* Extensions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D71AC4CB2BA8E3480076268E /* VisibilityTracker.swift */,
|
||||||
|
);
|
||||||
|
path = Extensions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D72A2D032AD9C165002AFF62 /* Mocking */ = {
|
D72A2D032AD9C165002AFF62 /* Mocking */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -3171,7 +3178,7 @@
|
|||||||
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
||||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
||||||
D7ADD3E02B538D4200F104C4 /* DamusPurpleURLSheetView.swift in Sources */,
|
D7ADD3E02B538D4200F104C4 /* DamusPurpleURLSheetView.swift in Sources */,
|
||||||
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */,
|
4CFF8F6729CC9E3A008DB934 /* FullScreenCarouselView.swift in Sources */,
|
||||||
4CA927632A290EB10098A105 /* EventTop.swift in Sources */,
|
4CA927632A290EB10098A105 /* EventTop.swift in Sources */,
|
||||||
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */,
|
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */,
|
||||||
4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */,
|
4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */,
|
||||||
@ -3203,6 +3210,7 @@
|
|||||||
F75BA12D29A1855400E10810 /* BookmarksManager.swift in Sources */,
|
F75BA12D29A1855400E10810 /* BookmarksManager.swift in Sources */,
|
||||||
4CC14FEF2A73FCCB007AEB17 /* IdType.swift in Sources */,
|
4CC14FEF2A73FCCB007AEB17 /* IdType.swift in Sources */,
|
||||||
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */,
|
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */,
|
||||||
|
D71AC4CC2BA8E3480076268E /* VisibilityTracker.swift in Sources */,
|
||||||
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */,
|
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */,
|
||||||
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
|
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
|
||||||
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */,
|
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */,
|
||||||
@ -3409,7 +3417,7 @@
|
|||||||
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
|
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
|
||||||
4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */,
|
4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */,
|
||||||
7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */,
|
7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */,
|
||||||
50A16FFB2AA6C06600DFEC1F /* AVPlayerView.swift in Sources */,
|
50A16FFB2AA6C06600DFEC1F /* DamusAVPlayerView.swift in Sources */,
|
||||||
4CA352A82A76B37E003BB08B /* NewMutesNotify.swift in Sources */,
|
4CA352A82A76B37E003BB08B /* NewMutesNotify.swift in Sources */,
|
||||||
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */,
|
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */,
|
||||||
7527271E2A93FF0100214108 /* Block.swift in Sources */,
|
7527271E2A93FF0100214108 /* Block.swift in Sources */,
|
||||||
|
@ -77,13 +77,14 @@ class CarouselModel: ObservableObject {
|
|||||||
|
|
||||||
// MARK: - Image Carousel
|
// MARK: - Image Carousel
|
||||||
@MainActor
|
@MainActor
|
||||||
struct ImageCarousel: View {
|
struct ImageCarousel<Content: View>: View {
|
||||||
var urls: [MediaUrl]
|
var urls: [MediaUrl]
|
||||||
|
|
||||||
let evid: NoteId
|
let evid: NoteId
|
||||||
|
|
||||||
let state: DamusState
|
let state: DamusState
|
||||||
@ObservedObject var model: CarouselModel
|
@ObservedObject var model: CarouselModel
|
||||||
|
let content: ((_ dismiss: @escaping (() -> Void)) -> Content)?
|
||||||
|
|
||||||
init(state: DamusState, evid: NoteId, urls: [MediaUrl]) {
|
init(state: DamusState, evid: NoteId, urls: [MediaUrl]) {
|
||||||
self.urls = urls
|
self.urls = urls
|
||||||
@ -91,6 +92,16 @@ struct ImageCarousel: View {
|
|||||||
self.state = state
|
self.state = state
|
||||||
let media_model = state.events.get_cache_data(evid).media_metadata_model
|
let media_model = state.events.get_cache_data(evid).media_metadata_model
|
||||||
self._model = ObservedObject(initialValue: CarouselModel(image_fill: media_model.fill))
|
self._model = ObservedObject(initialValue: CarouselModel(image_fill: media_model.fill))
|
||||||
|
self.content = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
init(state: DamusState, evid: NoteId, urls: [MediaUrl], @ViewBuilder content: @escaping (_ dismiss: @escaping (() -> Void)) -> Content) {
|
||||||
|
self.urls = urls
|
||||||
|
self.evid = evid
|
||||||
|
self.state = state
|
||||||
|
let media_model = state.events.get_cache_data(evid).media_metadata_model
|
||||||
|
self._model = ObservedObject(initialValue: CarouselModel(image_fill: media_model.fill))
|
||||||
|
self.content = content
|
||||||
}
|
}
|
||||||
|
|
||||||
var filling: Bool {
|
var filling: Bool {
|
||||||
@ -132,7 +143,7 @@ struct ImageCarousel: View {
|
|||||||
model.open_sheet = true
|
model.open_sheet = true
|
||||||
}
|
}
|
||||||
case .video(let url):
|
case .video(let url):
|
||||||
DamusVideoPlayer(url: url, video_size: $model.video_size, controller: state.video)
|
DamusVideoPlayer(url: url, video_size: $model.video_size, controller: state.video, style: .preview(on_tap: { model.open_sheet = true }))
|
||||||
.onChange(of: model.video_size) { size in
|
.onChange(of: model.video_size) { size in
|
||||||
guard let size else { return }
|
guard let size else { return }
|
||||||
|
|
||||||
@ -201,7 +212,16 @@ struct ImageCarousel: View {
|
|||||||
}
|
}
|
||||||
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
||||||
.fullScreenCover(isPresented: $model.open_sheet) {
|
.fullScreenCover(isPresented: $model.open_sheet) {
|
||||||
ImageView(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex)
|
if let content {
|
||||||
|
FullScreenCarouselView<Content>(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) {
|
||||||
|
content({ // Dismiss closure
|
||||||
|
model.open_sheet = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FullScreenCarouselView<AnyView>(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(height: height)
|
.frame(height: height)
|
||||||
.onChange(of: model.selectedIndex) { value in
|
.onChange(of: model.selectedIndex) { value in
|
||||||
@ -296,7 +316,9 @@ public struct ImageFill {
|
|||||||
struct ImageCarousel_Previews: PreviewProvider {
|
struct ImageCarousel_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!)
|
let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!)
|
||||||
ImageCarousel(state: test_damus_state, evid: test_note.id, urls: [url, url])
|
let test_video_url: MediaUrl = .video(URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!)
|
||||||
|
ImageCarousel<AnyView>(state: test_damus_state, evid: test_note.id, urls: [test_video_url, url])
|
||||||
|
.environmentObject(OrientationTracker())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,12 @@ import SwiftUI
|
|||||||
|
|
||||||
struct TruncatedText: View {
|
struct TruncatedText: View {
|
||||||
let text: CompatibleText
|
let text: CompatibleText
|
||||||
let maxChars: Int = 280
|
let maxChars: Int
|
||||||
|
|
||||||
|
init(text: CompatibleText, maxChars: Int = 280) {
|
||||||
|
self.text = text
|
||||||
|
self.maxChars = maxChars
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let truncatedAttributedString: AttributedString? = text.attributed.truncateOrNil(maxLength: maxChars)
|
let truncatedAttributedString: AttributedString? = text.attributed.truncateOrNil(maxLength: maxChars)
|
||||||
|
@ -19,8 +19,11 @@ struct EventViewOptions: OptionSet {
|
|||||||
static let nested = EventViewOptions(rawValue: 1 << 7)
|
static let nested = EventViewOptions(rawValue: 1 << 7)
|
||||||
static let top_zap = EventViewOptions(rawValue: 1 << 8)
|
static let top_zap = EventViewOptions(rawValue: 1 << 8)
|
||||||
static let no_mentions = EventViewOptions(rawValue: 1 << 9)
|
static let no_mentions = EventViewOptions(rawValue: 1 << 9)
|
||||||
|
static let no_media = EventViewOptions(rawValue: 1 << 10)
|
||||||
|
static let truncate_content_very_short = EventViewOptions(rawValue: 1 << 11)
|
||||||
|
|
||||||
static let embedded: EventViewOptions = [.no_action_bar, .small_pfp, .wide, .truncate_content, .nested]
|
static let embedded: EventViewOptions = [.no_action_bar, .small_pfp, .wide, .truncate_content, .nested]
|
||||||
|
static let embedded_text_only: EventViewOptions = [.no_action_bar, .small_pfp, .wide, .truncate_content, .nested, .no_media, .truncate_content_very_short]
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TextEvent: View {
|
struct TextEvent: View {
|
||||||
|
36
damus/Views/Extensions/VisibilityTracker.swift
Normal file
36
damus/Views/Extensions/VisibilityTracker.swift
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// VisibilityTracker.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Daniel D’Aquino on 2024-03-18.
|
||||||
|
//
|
||||||
|
// Based on code examples shown in this article: https://medium.com/@jackvanderpump/how-to-detect-is-an-element-is-visible-in-swiftui-9ff58ca72339
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
func on_visibility_change(perform visibility_change_notifier: @escaping (Bool) -> Void, edge: Alignment = .center) -> some View {
|
||||||
|
self.modifier(VisibilityTracker(visibility_change_notifier: visibility_change_notifier, edge: edge))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VisibilityTracker: ViewModifier {
|
||||||
|
let visibility_change_notifier: (Bool) -> Void
|
||||||
|
let edge: Alignment
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.overlay(
|
||||||
|
LazyVStack {
|
||||||
|
Color.clear
|
||||||
|
.onAppear {
|
||||||
|
visibility_change_notifier(true)
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
visibility_change_notifier(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
alignment: edge)
|
||||||
|
}
|
||||||
|
}
|
171
damus/Views/Images/FullScreenCarouselView.swift
Normal file
171
damus/Views/Images/FullScreenCarouselView.swift
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
//
|
||||||
|
// FullScreenCarouselView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-03-23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct FullScreenCarouselView<Content: View>: View {
|
||||||
|
let video_controller: VideoController
|
||||||
|
let urls: [MediaUrl]
|
||||||
|
|
||||||
|
@Environment(\.presentationMode) var presentationMode
|
||||||
|
|
||||||
|
@State var showMenu = true
|
||||||
|
|
||||||
|
let settings: UserSettingsStore
|
||||||
|
@Binding var selectedIndex: Int
|
||||||
|
let content: (() -> Content)?
|
||||||
|
|
||||||
|
init(video_controller: VideoController, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding<Int>, @ViewBuilder content: @escaping () -> Content) {
|
||||||
|
self.video_controller = video_controller
|
||||||
|
self.urls = urls
|
||||||
|
self._showMenu = State(initialValue: showMenu)
|
||||||
|
self.settings = settings
|
||||||
|
_selectedIndex = selectedIndex
|
||||||
|
self.content = content
|
||||||
|
}
|
||||||
|
|
||||||
|
init(video_controller: VideoController, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding<Int>) {
|
||||||
|
self.video_controller = video_controller
|
||||||
|
self.urls = urls
|
||||||
|
self._showMenu = State(initialValue: showMenu)
|
||||||
|
self.settings = settings
|
||||||
|
_selectedIndex = selectedIndex
|
||||||
|
self.content = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var tabViewIndicator: some View {
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
ForEach(urls.indices, id: \.self) { index in
|
||||||
|
Capsule()
|
||||||
|
.fill(index == selectedIndex ? Color.white : Color.damusMediumGrey)
|
||||||
|
.frame(width: 7, height: 7)
|
||||||
|
.onTapGesture {
|
||||||
|
selectedIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.clipShape(Capsule())
|
||||||
|
}
|
||||||
|
|
||||||
|
var background: some ShapeStyle {
|
||||||
|
if case .video = urls[safe: selectedIndex] {
|
||||||
|
return AnyShapeStyle(Color.black)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return AnyShapeStyle(.regularMaterial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var background_color: UIColor {
|
||||||
|
return .black
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
Color(self.background_color)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
|
||||||
|
TabView(selection: $selectedIndex) {
|
||||||
|
ForEach(urls.indices, id: \.self) { index in
|
||||||
|
VStack {
|
||||||
|
if case .video = urls[safe: index] {
|
||||||
|
ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings)
|
||||||
|
.clipped() // SwiftUI hack from https://stackoverflow.com/a/74401288 to make playback controls show up within the TabView
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.padding(.top, Theme.safeAreaInsets?.top)
|
||||||
|
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
||||||
|
.modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
|
||||||
|
presentationMode.wrappedValue.dismiss()
|
||||||
|
}))
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ZoomableScrollView {
|
||||||
|
ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings)
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.padding(.top, Theme.safeAreaInsets?.top)
|
||||||
|
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
||||||
|
}
|
||||||
|
.modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
|
||||||
|
presentationMode.wrappedValue.dismiss()
|
||||||
|
}))
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}.tag(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ignoresSafeArea()
|
||||||
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
||||||
|
.gesture(TapGesture(count: 2).onEnded {
|
||||||
|
// Prevents menu from hiding on double tap
|
||||||
|
})
|
||||||
|
.gesture(TapGesture(count: 1).onEnded {
|
||||||
|
showMenu.toggle()
|
||||||
|
})
|
||||||
|
.overlay(
|
||||||
|
GeometryReader { geo in
|
||||||
|
VStack {
|
||||||
|
if showMenu {
|
||||||
|
NavDismissBarView(showBackgroundCircle: false)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if (urls.count > 1) {
|
||||||
|
tabViewIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
self.content?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.animation(.easeInOut, value: showMenu)
|
||||||
|
.padding(.bottom, geo.safeAreaInsets.bottom == 0 ? 12 : 0)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate struct FullScreenCarouselPreviewView<Content: View>: View {
|
||||||
|
@State var selectedIndex: Int = 0
|
||||||
|
let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!)
|
||||||
|
let test_video_url: MediaUrl = .video(URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!)
|
||||||
|
let custom_content: (() -> Content)?
|
||||||
|
|
||||||
|
init(content: (() -> Content)? = nil) {
|
||||||
|
self.custom_content = content
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
FullScreenCarouselView(video_controller: test_damus_state.video, urls: [test_video_url, url], settings: test_damus_state.settings, selectedIndex: $selectedIndex) {
|
||||||
|
self.custom_content?()
|
||||||
|
}
|
||||||
|
.environmentObject(OrientationTracker())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FullScreenCarouselView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
Group {
|
||||||
|
FullScreenCarouselPreviewView<AnyView>()
|
||||||
|
.previewDisplayName("No custom content on overlay")
|
||||||
|
|
||||||
|
FullScreenCarouselPreviewView(content: {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("Some content")
|
||||||
|
.padding()
|
||||||
|
.foregroundColor(.white)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}.background(.ultraThinMaterial)
|
||||||
|
})
|
||||||
|
.previewDisplayName("Custom content on overlay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,19 +43,26 @@ struct ImageContainerView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
switch url {
|
switch url {
|
||||||
case .image(let url):
|
case .image(let url):
|
||||||
Img(url: url)
|
Img(url: url)
|
||||||
case .video(let url):
|
case .video(let url):
|
||||||
DamusVideoPlayer(url: url, video_size: .constant(nil), controller: video_controller)
|
DamusVideoPlayer(url: url, video_size: .constant(nil), controller: video_controller, style: .full, visibility_tracking_method: .generic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let test_image_url = URL(string: "https://jb55.com/red-me.jpg")!
|
let test_image_url = URL(string: "https://jb55.com/red-me.jpg")!
|
||||||
|
fileprivate let test_video_url = URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!
|
||||||
|
|
||||||
struct ImageContainerView_Previews: PreviewProvider {
|
struct ImageContainerView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ImageContainerView(video_controller: test_damus_state.video, url: .image(test_image_url), settings: test_damus_state.settings)
|
Group {
|
||||||
|
ImageContainerView(video_controller: test_damus_state.video, url: .image(test_image_url), settings: test_damus_state.settings)
|
||||||
|
.previewDisplayName("Image")
|
||||||
|
ImageContainerView(video_controller: test_damus_state.video, url: .video(test_video_url), settings: test_damus_state.settings)
|
||||||
|
.previewDisplayName("Video")
|
||||||
|
}
|
||||||
|
.environmentObject(OrientationTracker())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
//
|
|
||||||
// ImageView.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-03-23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ImageView: View {
|
|
||||||
let video_controller: VideoController
|
|
||||||
let urls: [MediaUrl]
|
|
||||||
|
|
||||||
@Environment(\.presentationMode) var presentationMode
|
|
||||||
|
|
||||||
@State var showMenu = true
|
|
||||||
|
|
||||||
let settings: UserSettingsStore
|
|
||||||
@Binding var selectedIndex: Int
|
|
||||||
|
|
||||||
var tabViewIndicator: some View {
|
|
||||||
HStack(spacing: 10) {
|
|
||||||
ForEach(urls.indices, id: \.self) { index in
|
|
||||||
Capsule()
|
|
||||||
.fill(index == selectedIndex ? Color(UIColor.label) : Color.secondary)
|
|
||||||
.frame(width: 7, height: 7)
|
|
||||||
.onTapGesture {
|
|
||||||
selectedIndex = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.background(.regularMaterial)
|
|
||||||
.clipShape(Capsule())
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack {
|
|
||||||
Color(.systemBackground)
|
|
||||||
.ignoresSafeArea()
|
|
||||||
|
|
||||||
TabView(selection: $selectedIndex) {
|
|
||||||
ForEach(urls.indices, id: \.self) { index in
|
|
||||||
ZoomableScrollView {
|
|
||||||
ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings)
|
|
||||||
.aspectRatio(contentMode: .fit)
|
|
||||||
.padding(.top, Theme.safeAreaInsets?.top)
|
|
||||||
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
|
||||||
}
|
|
||||||
.modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
|
|
||||||
presentationMode.wrappedValue.dismiss()
|
|
||||||
}))
|
|
||||||
.ignoresSafeArea()
|
|
||||||
.tag(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ignoresSafeArea()
|
|
||||||
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
|
||||||
.gesture(TapGesture(count: 2).onEnded {
|
|
||||||
// Prevents menu from hiding on double tap
|
|
||||||
})
|
|
||||||
.gesture(TapGesture(count: 1).onEnded {
|
|
||||||
showMenu.toggle()
|
|
||||||
})
|
|
||||||
.overlay(
|
|
||||||
GeometryReader { geo in
|
|
||||||
VStack {
|
|
||||||
if showMenu {
|
|
||||||
NavDismissBarView()
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
if (urls.count > 1) {
|
|
||||||
tabViewIndicator
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.animation(.easeInOut, value: showMenu)
|
|
||||||
.padding(.bottom, geo.safeAreaInsets.bottom == 0 ? 12 : 0)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ImageView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!)
|
|
||||||
ImageView(video_controller: test_damus_state.video, urls: [url], settings: test_damus_state.settings, selectedIndex: Binding.constant(0))
|
|
||||||
}
|
|
||||||
}
|
|
@ -42,16 +42,27 @@ struct ProfileImageContainerView: View {
|
|||||||
struct NavDismissBarView: View {
|
struct NavDismissBarView: View {
|
||||||
|
|
||||||
@Environment(\.presentationMode) var presentationMode
|
@Environment(\.presentationMode) var presentationMode
|
||||||
|
let showBackgroundCircle: Bool
|
||||||
|
|
||||||
|
init(showBackgroundCircle: Bool = true) {
|
||||||
|
self.showBackgroundCircle = showBackgroundCircle
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
presentationMode.wrappedValue.dismiss()
|
presentationMode.wrappedValue.dismiss()
|
||||||
}, label: {
|
}, label: {
|
||||||
Image("close")
|
if showBackgroundCircle {
|
||||||
.frame(width: 33, height: 33)
|
Image("close")
|
||||||
.background(.regularMaterial)
|
.frame(width: 33, height: 33)
|
||||||
.clipShape(Circle())
|
.background(.regularMaterial)
|
||||||
|
.clipShape(Circle())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Image("close")
|
||||||
|
.frame(width: 33, height: 33)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
@ -57,6 +57,10 @@ struct NoteContentView: View {
|
|||||||
return options.contains(.truncate_content)
|
return options.contains(.truncate_content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var truncate_very_short: Bool {
|
||||||
|
return options.contains(.truncate_content_very_short)
|
||||||
|
}
|
||||||
|
|
||||||
var with_padding: Bool {
|
var with_padding: Bool {
|
||||||
return options.contains(.wide)
|
return options.contains(.wide)
|
||||||
}
|
}
|
||||||
@ -73,7 +77,11 @@ struct NoteContentView: View {
|
|||||||
|
|
||||||
func truncatedText(content: CompatibleText) -> some View {
|
func truncatedText(content: CompatibleText) -> some View {
|
||||||
Group {
|
Group {
|
||||||
if truncate {
|
if truncate_very_short {
|
||||||
|
TruncatedText(text: content, maxChars: 140)
|
||||||
|
.font(eventviewsize_to_font(size, font_size: damus_state.settings.font_size))
|
||||||
|
}
|
||||||
|
else if truncate {
|
||||||
TruncatedText(text: content)
|
TruncatedText(text: content)
|
||||||
.font(eventviewsize_to_font(size, font_size: damus_state.settings.font_size))
|
.font(eventviewsize_to_font(size, font_size: damus_state.settings.font_size))
|
||||||
} else {
|
} else {
|
||||||
@ -107,6 +115,19 @@ struct NoteContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fullscreen_preview(dismiss: @escaping () -> Void) -> some View {
|
||||||
|
VStack {
|
||||||
|
EventView(damus: damus_state, event: self.event, options: .embedded_text_only)
|
||||||
|
.padding(.top)
|
||||||
|
}
|
||||||
|
.background(.thinMaterial)
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
.onTapGesture(perform: {
|
||||||
|
damus_state.nav.push(route: Route.Thread(thread: .init(event: self.event, damus_state: damus_state)))
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func MainContent(artifacts: NoteArtifactsSeparated) -> some View {
|
func MainContent(artifacts: NoteArtifactsSeparated) -> some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
if size == .selected {
|
if size == .selected {
|
||||||
@ -135,13 +156,19 @@ struct NoteContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if artifacts.media.count > 0 {
|
if artifacts.media.count > 0 {
|
||||||
if !damus_state.settings.media_previews && !load_media {
|
if (self.options.contains(.no_media)) {
|
||||||
|
EmptyView()
|
||||||
|
} else if !damus_state.settings.media_previews && !load_media {
|
||||||
loadMediaButton(artifacts: artifacts)
|
loadMediaButton(artifacts: artifacts)
|
||||||
} else if !blur_images || (!blur_images && !damus_state.settings.media_previews && load_media) {
|
} else if !blur_images || (!blur_images && !damus_state.settings.media_previews && load_media) {
|
||||||
ImageCarousel(state: damus_state, evid: event.id, urls: artifacts.media)
|
ImageCarousel(state: damus_state, evid: event.id, urls: artifacts.media) { dismiss in
|
||||||
|
fullscreen_preview(dismiss: dismiss)
|
||||||
|
}
|
||||||
} else if blur_images || (blur_images && !damus_state.settings.media_previews && load_media) {
|
} else if blur_images || (blur_images && !damus_state.settings.media_previews && load_media) {
|
||||||
ZStack {
|
ZStack {
|
||||||
ImageCarousel(state: damus_state, evid: event.id, urls: artifacts.media)
|
ImageCarousel(state: damus_state, evid: event.id, urls: artifacts.media) { dismiss in
|
||||||
|
fullscreen_preview(dismiss: dismiss)
|
||||||
|
}
|
||||||
Blur()
|
Blur()
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
blur_images = false
|
blur_images = false
|
||||||
|
@ -9,12 +9,15 @@ import Foundation
|
|||||||
import AVKit
|
import AVKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct AVPlayerView: UIViewControllerRepresentable {
|
struct DamusAVPlayerView: UIViewControllerRepresentable {
|
||||||
|
|
||||||
let player: AVPlayer
|
let player: AVPlayer
|
||||||
|
var controller: AVPlayerViewController
|
||||||
|
let show_playback_controls: Bool
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> AVPlayerViewController {
|
func makeUIViewController(context: Context) -> AVPlayerViewController {
|
||||||
AVPlayerViewController()
|
self.controller.showsPlaybackControls = show_playback_controls
|
||||||
|
return self.controller
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
|
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
|
@ -20,10 +20,22 @@ struct DamusVideoPlayer: View {
|
|||||||
let url: URL
|
let url: URL
|
||||||
@StateObject var model: DamusVideoPlayerViewModel
|
@StateObject var model: DamusVideoPlayerViewModel
|
||||||
@EnvironmentObject private var orientationTracker: OrientationTracker
|
@EnvironmentObject private var orientationTracker: OrientationTracker
|
||||||
|
let style: Style
|
||||||
|
let visibility_tracking_method: VisibilityTrackingMethod
|
||||||
|
@State var isVisible: Bool = false
|
||||||
|
|
||||||
init(url: URL, video_size: Binding<CGSize?>, controller: VideoController) {
|
init(url: URL, video_size: Binding<CGSize?>, controller: VideoController, style: Style, visibility_tracking_method: VisibilityTrackingMethod = .y_scroll) {
|
||||||
self.url = url
|
self.url = url
|
||||||
_model = StateObject(wrappedValue: DamusVideoPlayerViewModel(url: url, video_size: video_size, controller: controller))
|
let mute: Bool?
|
||||||
|
if case .full = style {
|
||||||
|
mute = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mute = nil
|
||||||
|
}
|
||||||
|
_model = StateObject(wrappedValue: DamusVideoPlayerViewModel(url: url, video_size: video_size, controller: controller, mute: mute))
|
||||||
|
self.visibility_tracking_method = visibility_tracking_method
|
||||||
|
self.style = style
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -31,7 +43,15 @@ struct DamusVideoPlayer: View {
|
|||||||
let localFrame = geo.frame(in: .local)
|
let localFrame = geo.frame(in: .local)
|
||||||
let centerY = globalCoordinate(localX: 0, localY: localFrame.midY, localGeometry: geo).y
|
let centerY = globalCoordinate(localX: 0, localY: localFrame.midY, localGeometry: geo).y
|
||||||
ZStack {
|
ZStack {
|
||||||
AVPlayerView(player: model.player)
|
if case .full = self.style {
|
||||||
|
DamusAVPlayerView(player: model.player, controller: model.player_view_controller, show_playback_controls: true)
|
||||||
|
}
|
||||||
|
if case .preview(let on_tap) = self.style {
|
||||||
|
DamusAVPlayerView(player: model.player, controller: model.player_view_controller, show_playback_controls: false)
|
||||||
|
.simultaneousGesture(TapGesture().onEnded({
|
||||||
|
on_tap?()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
if model.is_loading {
|
if model.is_loading {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
@ -40,22 +60,35 @@ struct DamusVideoPlayer: View {
|
|||||||
.scaleEffect(CGSize(width: 1.5, height: 1.5))
|
.scaleEffect(CGSize(width: 1.5, height: 1.5))
|
||||||
}
|
}
|
||||||
|
|
||||||
if model.has_audio {
|
if case .preview = self.style {
|
||||||
mute_button
|
if model.has_audio {
|
||||||
|
mute_button
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if model.is_live {
|
if model.is_live {
|
||||||
live_indicator
|
live_indicator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: centerY) { _ in
|
.onChange(of: centerY) { _ in
|
||||||
update_is_visible(centerY: centerY)
|
if case .y_scroll = visibility_tracking_method {
|
||||||
|
update_is_visible(centerY: centerY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.on_visibility_change(perform: { new_visibility in
|
||||||
|
if case .generic = visibility_tracking_method {
|
||||||
|
model.set_view_is_visible(new_visibility)
|
||||||
|
}
|
||||||
|
})
|
||||||
.onAppear {
|
.onAppear {
|
||||||
update_is_visible(centerY: centerY)
|
if case .y_scroll = visibility_tracking_method {
|
||||||
|
update_is_visible(centerY: centerY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
model.view_did_disappear()
|
if case .y_scroll = visibility_tracking_method {
|
||||||
|
model.view_did_disappear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,9 +148,31 @@ struct DamusVideoPlayer: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Style {
|
||||||
|
/// A full video player with playback controls
|
||||||
|
case full
|
||||||
|
/// A style suitable for muted, auto-playing videos on a feed
|
||||||
|
case preview(on_tap: (() -> Void)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum VisibilityTrackingMethod {
|
||||||
|
/// Detects visibility based on its Y position relative to viewport. Ideal for long feeds
|
||||||
|
case y_scroll
|
||||||
|
/// Detects visibility based whether the view intersects with the viewport
|
||||||
|
case generic
|
||||||
|
}
|
||||||
}
|
}
|
||||||
struct DamusVideoPlayer_Previews: PreviewProvider {
|
struct DamusVideoPlayer_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), controller: VideoController())
|
Group {
|
||||||
|
DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), controller: VideoController(), style: .full)
|
||||||
|
.environmentObject(OrientationTracker())
|
||||||
|
.previewDisplayName("Full video player")
|
||||||
|
|
||||||
|
DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), controller: VideoController(), style: .preview(on_tap: nil))
|
||||||
|
.environmentObject(OrientationTracker())
|
||||||
|
.previewDisplayName("Preview video player")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import AVKit
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
@ -27,7 +28,8 @@ final class DamusVideoPlayerViewModel: ObservableObject {
|
|||||||
private let url: URL
|
private let url: URL
|
||||||
private let player_item: AVPlayerItem
|
private let player_item: AVPlayerItem
|
||||||
let player: AVPlayer
|
let player: AVPlayer
|
||||||
private let controller: VideoController
|
fileprivate let controller: VideoController
|
||||||
|
let player_view_controller = AVPlayerViewController()
|
||||||
let id = UUID()
|
let id = UUID()
|
||||||
|
|
||||||
@Published var has_audio = false
|
@Published var has_audio = false
|
||||||
@ -55,7 +57,7 @@ final class DamusVideoPlayerViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(url: URL, video_size: Binding<CGSize?>, controller: VideoController) {
|
init(url: URL, video_size: Binding<CGSize?>, controller: VideoController, mute: Bool? = nil) {
|
||||||
self.url = url
|
self.url = url
|
||||||
player_item = AVPlayerItem(url: url)
|
player_item = AVPlayerItem(url: url)
|
||||||
player = AVPlayer(playerItem: player_item)
|
player = AVPlayer(playerItem: player_item)
|
||||||
@ -66,7 +68,7 @@ final class DamusVideoPlayerViewModel: ObservableObject {
|
|||||||
await load()
|
await load()
|
||||||
}
|
}
|
||||||
|
|
||||||
is_muted = controller.should_mute_video(url: url)
|
is_muted = mute ?? controller.should_mute_video(url: url)
|
||||||
player.isMuted = is_muted
|
player.isMuted = is_muted
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
NotificationCenter.default.addObserver(
|
||||||
|
Loading…
Reference in New Issue
Block a user