Skip to content

Commit

Permalink
Merge pull request #1214 from planetary-social/feature/new-media-flag
Browse files Browse the repository at this point in the history
Add feature flag and unit test for new media display
  • Loading branch information
joshuatbrown authored Jun 6, 2024
2 parents 9edd6b8 + c8d5c22 commit 9a91990
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 23 deletions.
28 changes: 25 additions & 3 deletions Nos.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
0350F1172C0A47B20024CC15 /* contact_list.json in Resources */ = {isa = PBXBuildFile; fileRef = 0350F1162C0A47B20024CC15 /* contact_list.json */; };
0350F1212C0A490E0024CC15 /* EventProcessorIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0350F1202C0A490E0024CC15 /* EventProcessorIntegrationTests.swift */; };
0350F12B2C0A49D40024CC15 /* text_note.json in Resources */ = {isa = PBXBuildFile; fileRef = 0350F12A2C0A49D40024CC15 /* text_note.json */; };
0350F12D2C0A7EF20024CC15 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0350F12C2C0A7EF20024CC15 /* FeatureFlags.swift */; };
0357299B2BE415E5005FEE85 /* ContentWarningController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0357299A2BE415E5005FEE85 /* ContentWarningController.swift */; };
0357299F2BE41653005FEE85 /* ContentWarningControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0357299C2BE41653005FEE85 /* ContentWarningControllerTests.swift */; };
035729A02BE41653005FEE85 /* SocialGraphTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0357299D2BE41653005FEE85 /* SocialGraphTests.swift */; };
Expand All @@ -39,6 +40,13 @@
0373CE8F2C090D4D0027C856 /* NostrBuildResponseJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0373CE8E2C090D4D0027C856 /* NostrBuildResponseJSONTests.swift */; };
0373CE992C0910250027C856 /* XCTestCase+JSONData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0373CE982C0910250027C856 /* XCTestCase+JSONData.swift */; };
0378409D2BB4A2B600E5E901 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0378409C2BB4A2B600E5E901 /* PrivacyInfo.xcprivacy */; };
037975BB2C0E24D200ADDF37 /* CompactNoteViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037975BA2C0E24D200ADDF37 /* CompactNoteViewTests.swift */; };
037975BC2C0E258E00ADDF37 /* CompactNoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9DFA96A299BEE2C006929C1 /* CompactNoteView.swift */; };
037975BD2C0E25E200ADDF37 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0350F12C2C0A7EF20024CC15 /* FeatureFlags.swift */; };
037975BE2C0E265E00ADDF37 /* LinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = C905B0762A619E99009B8A78 /* LinkPreview.swift */; };
037975C72C0E26FC00ADDF37 /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = C987F85729BA981800B44E7A /* Font.swift */; };
037975D12C0E341500ADDF37 /* MockFeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037975D02C0E341500ADDF37 /* MockFeatureFlags.swift */; };
037975EA2C0E695A00ADDF37 /* MockFeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037975D02C0E341500ADDF37 /* MockFeatureFlags.swift */; };
2D06BB9D2AE249D70085F509 /* ThreadRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D06BB9C2AE249D70085F509 /* ThreadRootView.swift */; };
2D4010A22AD87DF300F93AD4 /* KnownFollowersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4010A12AD87DF300F93AD4 /* KnownFollowersView.swift */; };
3A1C296F2B2A537C0020B753 /* Moderation.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 3A1C296E2B2A537C0020B753 /* Moderation.xcstrings */; };
Expand Down Expand Up @@ -471,6 +479,7 @@
0350F1162C0A47B20024CC15 /* contact_list.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = contact_list.json; sourceTree = "<group>"; };
0350F1202C0A490E0024CC15 /* EventProcessorIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProcessorIntegrationTests.swift; sourceTree = "<group>"; };
0350F12A2C0A49D40024CC15 /* text_note.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = text_note.json; sourceTree = "<group>"; };
0350F12C2C0A7EF20024CC15 /* FeatureFlags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = "<group>"; };
0357299A2BE415E5005FEE85 /* ContentWarningController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentWarningController.swift; sourceTree = "<group>"; };
0357299C2BE41653005FEE85 /* ContentWarningControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentWarningControllerTests.swift; sourceTree = "<group>"; };
0357299D2BE41653005FEE85 /* SocialGraphTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocialGraphTests.swift; sourceTree = "<group>"; };
Expand All @@ -493,6 +502,8 @@
0373CE8E2C090D4D0027C856 /* NostrBuildResponseJSONTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrBuildResponseJSONTests.swift; sourceTree = "<group>"; };
0373CE982C0910250027C856 /* XCTestCase+JSONData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTestCase+JSONData.swift"; sourceTree = "<group>"; };
0378409C2BB4A2B600E5E901 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
037975BA2C0E24D200ADDF37 /* CompactNoteViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactNoteViewTests.swift; sourceTree = "<group>"; };
037975D02C0E341500ADDF37 /* MockFeatureFlags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFeatureFlags.swift; sourceTree = "<group>"; };
03AB2F7D2BF6609500B73DB1 /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = "<group>"; };
2D06BB9C2AE249D70085F509 /* ThreadRootView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadRootView.swift; sourceTree = "<group>"; };
2D4010A12AD87DF300F93AD4 /* KnownFollowersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KnownFollowersView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -898,18 +909,20 @@
children = (
035729B42BE416A6005FEE85 /* DirectMessageWrapperTests.swift */,
035729B52BE416A6005FEE85 /* GiftWrapperTests.swift */,
037975D02C0E341500ADDF37 /* MockFeatureFlags.swift */,
035729B62BE416A6005FEE85 /* ReportPublisherTests.swift */,
0320C0F92BFE439000C4C080 /* Relay */,
);
path = Service;
sourceTree = "<group>";
};
035729BC2BE416BD005FEE85 /* View */ = {
035729BC2BE416BD005FEE85 /* Views */ = {
isa = PBXGroup;
children = (
035729BB2BE416BD005FEE85 /* EventObservationTests.swift */,
037975BA2C0E24D200ADDF37 /* CompactNoteViewTests.swift */,
);
path = View;
path = Views;
sourceTree = "<group>";
};
037A2C242BA9D75F00FC554B /* Generated */ = {
Expand Down Expand Up @@ -1091,6 +1104,7 @@
C9B678DA29EEBF3B00303F33 /* DependencyInjection.swift */,
65D066982BD558690011C5CD /* DirectMessageWrapper.swift */,
C9BAB09A2996FBA10003A84E /* EventProcessor.swift */,
0350F12C2C0A7EF20024CC15 /* FeatureFlags.swift */,
DC5F20422A6ED75C00F8D73F /* FileStorageAPI.swift */,
C959DB752BD01DF4008F3627 /* GiftWrapper.swift */,
A3B943CE299AE00100A15A08 /* KeyChain.swift */,
Expand Down Expand Up @@ -1252,7 +1266,7 @@
035729AA2BE4167E005FEE85 /* Model */,
035729B72BE416A6005FEE85 /* Service */,
C9C9443A29F6E420002F2C7A /* Test Helpers */,
035729BC2BE416BD005FEE85 /* View */,
035729BC2BE416BD005FEE85 /* Views */,
);
path = NosTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -1760,6 +1774,7 @@
C9DEC06E2989668E0078B43A /* Relay+CoreDataClass.swift in Sources */,
C9F64D8C29ED840700563F2B /* LogHelper.swift in Sources */,
C9C2B78529E073E300548B4A /* RelaySubscription.swift in Sources */,
0350F12D2C0A7EF20024CC15 /* FeatureFlags.swift in Sources */,
3FFB1D9C29A7DF9D002A755D /* StackedAvatarsView.swift in Sources */,
C97A1C8E29E58EC7009D9E8D /* NSManagedObjectContext+Nos.swift in Sources */,
5BBA5E9C2BAE052F00D57D76 /* NiceWorkSheet.swift in Sources */,
Expand Down Expand Up @@ -1813,6 +1828,7 @@
3F170C78299D816200BC8F8B /* AppController.swift in Sources */,
C9F204672ADEDBA80029A858 /* String+Extra.swift in Sources */,
C95D68AB299E710F00429F86 /* Color+Hex.swift in Sources */,
037975EA2C0E695A00ADDF37 /* MockFeatureFlags.swift in Sources */,
C94A5E182A72C84200B6EC5D /* ReportCategory.swift in Sources */,
C9A8015E2BD0177D006E29B2 /* ReportPublisher.swift in Sources */,
C973364F2A7968220012D8B8 /* SetUpUNSBanner.swift in Sources */,
Expand Down Expand Up @@ -1995,6 +2011,7 @@
0320C1152BFE63DC00C4C080 /* MockRelaySubscriptionManager.swift in Sources */,
035729CB2BE41770005FEE85 /* ContentWarningController.swift in Sources */,
CD09A76229A5220E0063464F /* AppController.swift in Sources */,
037975BE2C0E265E00ADDF37 /* LinkPreview.swift in Sources */,
C9A0DAF929C92F4500466635 /* UNSAPI.swift in Sources */,
C97A1C8C29E45B4E009D9E8D /* RawEventController.swift in Sources */,
CD09A75F29A521FD0063464F /* RelayService.swift in Sources */,
Expand Down Expand Up @@ -2022,11 +2039,14 @@
CD09A75929A521D20063464F /* Color+Hex.swift in Sources */,
5B88051D2A2104CC00E21F06 /* SHA256Key.swift in Sources */,
C9CF23182A38A58B00EBEC31 /* ParseQueue.swift in Sources */,
037975C72C0E26FC00ADDF37 /* Font.swift in Sources */,
5B39E64429EDBF8100464830 /* NoteParser.swift in Sources */,
035729CA2BE4173E005FEE85 /* PreviewData.swift in Sources */,
C94A5E162A716A6D00B6EC5D /* EditableNoteText.swift in Sources */,
037975D12C0E341500ADDF37 /* MockFeatureFlags.swift in Sources */,
5BD08BB22A38E96F00BB926C /* JSONRelayMetadata.swift in Sources */,
C936B45A2A4C7B7C00DF1EB9 /* Nos.xcdatamodeld in Sources */,
037975BD2C0E25E200ADDF37 /* FeatureFlags.swift in Sources */,
C98CA9082B14FD8600929141 /* PagedRelaySubscription.swift in Sources */,
5B79F5B82B8E71CC002DA9BE /* NamesAPI.swift in Sources */,
C9F204812AE02D8C0029A858 /* AppDestination.swift in Sources */,
Expand Down Expand Up @@ -2059,6 +2079,7 @@
C93CA0C429AE3A1E00921183 /* JSONEvent.swift in Sources */,
C9DEC04629894BED0078B43A /* Event+CoreDataClass.swift in Sources */,
035729A02BE41653005FEE85 /* SocialGraphTests.swift in Sources */,
037975BC2C0E258E00ADDF37 /* CompactNoteView.swift in Sources */,
A32B6C7129A672BC00653FF5 /* CurrentUser.swift in Sources */,
C98A32282A05795E00E3FA13 /* Task+Timeout.swift in Sources */,
035729B12BE4167E005FEE85 /* ReportTests.swift in Sources */,
Expand Down Expand Up @@ -2103,6 +2124,7 @@
C9BAB09C2996FBA10003A84E /* EventProcessor.swift in Sources */,
C992B32B2B3613CC00704A9C /* SubscriptionCancellable.swift in Sources */,
C973AB5C2A323167002AED16 /* Follow+CoreDataProperties.swift in Sources */,
037975BB2C0E24D200ADDF37 /* CompactNoteViewTests.swift in Sources */,
659B27312BD9D6FE00BEA6CC /* VerifiableEvent.swift in Sources */,
C93005602A6AF8320098CA9E /* LoadingContent.swift in Sources */,
C97A1C8F29E58EC7009D9E8D /* NSManagedObjectContext+Nos.swift in Sources */,
Expand Down
12 changes: 12 additions & 0 deletions Nos/Assets/Localization/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -3716,6 +3716,18 @@
}
}
},
"enableNewMediaDisplay" : {
"comment" : "Setting for new media feature flag",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Enable new media display"
}
}
}
},
"enterCode" : {
"extractionState" : "manual",
"localizations" : {
Expand Down
11 changes: 11 additions & 0 deletions Nos/Service/DependencyInjection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ extension DependencyValues {
get { self[NoteParserKey.self] }
set { self[NoteParserKey.self] = newValue }
}

var featureFlags: FeatureFlags {
get { self[FeatureFlagsKey.self] }
set { self[FeatureFlagsKey.self] = newValue }
}
}

fileprivate enum AnalyticsKey: DependencyKey {
Expand Down Expand Up @@ -135,3 +140,9 @@ fileprivate enum NoteParserKey: DependencyKey {
static let testValue = NoteParser()
static let previewValue = NoteParser()
}

fileprivate enum FeatureFlagsKey: DependencyKey {
static let liveValue: any FeatureFlags = DefaultFeatureFlags.liveValue
static let testValue: any FeatureFlags = MockFeatureFlags()
static let previewValue: any FeatureFlags = MockFeatureFlags()
}
34 changes: 34 additions & 0 deletions Nos/Service/FeatureFlags.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Foundation
import Dependencies

/// The set of feature flags used by the app.
protocol FeatureFlags {
/// Whether the new media display should be enabled or not.
/// - Note: See [#1177](https://github.com/planetary-social/nos/issues/1177) for details on the new media display.
var newMediaDisplayEnabled: Bool { get }

// MARK: - Additional requirements for debug mode

#if DEBUG
/// Sets the value of `newMediaDisplayEnabled`.
func setNewMediaDisplayEnabled(_ enabled: Bool)
#endif
}

/// The default set of feature flag values for the app.
class DefaultFeatureFlags: FeatureFlags, DependencyKey {
/// The one and only instance of `DefaultFeatureFlags`.
static let liveValue = DefaultFeatureFlags()

private init() {}

private(set) var newMediaDisplayEnabled = false
}

#if DEBUG
extension DefaultFeatureFlags {
func setNewMediaDisplayEnabled(_ enabled: Bool) {
newMediaDisplayEnabled = enabled
}
}
#endif
11 changes: 9 additions & 2 deletions Nos/Views/CompactNoteView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ struct CompactNoteView: View {
/// If true links will be highlighted and open when tapped. If false the text will change to a secondary color
/// and links will not be tappable.
private var allowUserInteraction: Bool


/// The feature flags to use to determine what features are enabled.
@Dependency(\.featureFlags) private var featureFlags

/// Whether this view is currently displayed in a truncated state
@State private var isTextTruncated = true

Expand Down Expand Up @@ -145,7 +148,11 @@ struct CompactNoteView: View {
.padding(EdgeInsets(top: 0, leading: 0, bottom: 10, trailing: 0))
}
if note.kind == EventKind.text.rawValue, showLinkPreviews, !note.contentLinks.isEmpty {
LinkPreviewCarousel(links: note.contentLinks)
if featureFlags.newMediaDisplayEnabled {
EmptyView()
} else {
LinkPreviewCarousel(links: note.contentLinks)
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
Expand Down
59 changes: 41 additions & 18 deletions Nos/Views/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct SettingsView: View {
@Dependency(\.crashReporting) private var crashReporting
@Dependency(\.persistenceController) private var persistenceController
@Dependency(\.userDefaults) private var userDefaults
@Dependency(\.featureFlags) private var featureFlags
@Environment(\.managedObjectContext) private var viewContext
@Environment(AppController.self) var appController
@Environment(CurrentUser.self) private var currentUser
Expand Down Expand Up @@ -225,24 +226,7 @@ struct SettingsView: View {
)

#if DEBUG
Text(.localizable.sampleDataInstructions)
.foregroundColor(.primaryTxt)
Button(String(localized: .localizable.loadSampleData)) {
Task {
do {
try await persistenceController.loadSampleData(context: viewContext)
} catch {
print(error)
}
}
}
if let author = currentUser.author {
NavigationLink {
PublishedEventsView(author: author)
} label: {
Text(.localizable.allPublishedEvents)
}
}
debugControls
#endif
} header: {
Text(.localizable.debug)
Expand Down Expand Up @@ -288,6 +272,45 @@ struct SettingsView: View {
}
}

#if DEBUG
extension SettingsView {
private var enableNewMediaDisplay: Binding<Bool> {
Binding<Bool>(
get: { featureFlags.newMediaDisplayEnabled },
set: { featureFlags.setNewMediaDisplayEnabled($0) }
)
}

@MainActor var debugControls: some View {
Group {
Toggle(isOn: enableNewMediaDisplay) {
Text(.localizable.enableNewMediaDisplay)
.foregroundColor(.primaryTxt)
}

Text(.localizable.sampleDataInstructions)
.foregroundColor(.primaryTxt)
Button(String(localized: .localizable.loadSampleData)) {
Task {
do {
try await persistenceController.loadSampleData(context: viewContext)
} catch {
print(error)
}
}
}
if let author = currentUser.author {
NavigationLink {
PublishedEventsView(author: author)
} label: {
Text(.localizable.allPublishedEvents)
}
}
}
}
}
#endif

#Preview {
let previewData = PreviewData()

Expand Down
8 changes: 8 additions & 0 deletions NosTests/Service/MockFeatureFlags.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// A set of feature flag values used for testing that can be customized.
class MockFeatureFlags: FeatureFlags {
var newMediaDisplayEnabled = false

func setNewMediaDisplayEnabled(_ enabled: Bool) {
newMediaDisplayEnabled = enabled
}
}
29 changes: 29 additions & 0 deletions NosTests/Views/CompactNoteViewTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Dependencies
import ViewInspector
import XCTest

final class CompactNoteViewTests: CoreDataTestCase {
@MainActor func testNewMediaDisplayDisabledUsesLinkPreviewCarousel() throws {
// Arrange
let viewContext = persistenceController.viewContext
let parseContext = persistenceController.parseContext

let eventID = "123456"
let eventContent = "https://nos.social"

// Act
_ = try Event.findOrCreateStubBy(id: eventID, context: viewContext)
let fullEvent = try Event.findOrCreateStubBy(id: eventID, context: parseContext)
fullEvent.content = eventContent
fullEvent.kind = 1
fullEvent.contentLinks = [try XCTUnwrap(URL(string: eventContent))]

let subject = CompactNoteView(note: fullEvent)
ViewHosting.host(view: subject.environment(\.managedObjectContext, persistenceController.container.viewContext))
ViewHosting.host(view: subject.environmentObject(DependencyValues().router))

// Assert
let result = try subject.inspect().find(LinkPreviewCarousel.self)
XCTAssertNotNil(result)
}
}
File renamed without changes.

0 comments on commit 9a91990

Please sign in to comment.