From 06b47d37cf3649d2799e75df2126b9ab8c7a609a Mon Sep 17 00:00:00 2001 From: Trey T Date: Fri, 24 Apr 2026 17:32:36 -0500 Subject: [PATCH] Fix video action button wrapping + bundle textbook seed JSONs - VideoActionsView: stack icon over title via a custom label style so the Stream/Download/Play row fits iPhone width without wrapping mid-word ("Stre am", "Down load"). - project.yml: add textbook_data.json and textbook_vocab.json to the Conjuga target's Copy Bundle Resources. They were missing, so DataLoader.seedTextbookData early-returned every launch and the textbook never appeared in the Book tab. Regenerated Conjuga.xcodeproj. --- Conjuga/Conjuga.xcodeproj/project.pbxproj | 22 +++++++++------- .../Views/Guide/VideoActionsView.swift | 25 ++++++++++++++++--- Conjuga/project.yml | 4 +++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Conjuga/Conjuga.xcodeproj/project.pbxproj b/Conjuga/Conjuga.xcodeproj/project.pbxproj index 970fdc0..2d2bf10 100644 --- a/Conjuga/Conjuga.xcodeproj/project.pbxproj +++ b/Conjuga/Conjuga.xcodeproj/project.pbxproj @@ -67,9 +67,11 @@ 943A94A8C71919F3EFC0E8FA /* UserProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = E536AD1180FE10576EAC884A /* UserProgress.swift */; }; 97A2088134FC6CB41C507182 /* reflexive_verbs.json in Resources */ = {isa = PBXBuildFile; fileRef = 3644B5ED77F29A65877D926A /* reflexive_verbs.json */; }; 97EFCF6724CE59DC4F0274FD /* AchievementService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C42EA0EBD4CB1E10A82BA25 /* AchievementService.swift */; }; + 983988CE911C0FC5D869C516 /* youtube_videos.md in Resources */ = {isa = PBXBuildFile; fileRef = A6EC7C278E4287D91A0DB355 /* youtube_videos.md */; }; 9D9FD3853C5C969C62AE9999 /* StartupCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4B95B276C054DBFE508C4D1 /* StartupCoordinator.swift */; }; 9F0ACDC1F4ACB1E0D331283D /* CheckpointExamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34C67DD1A1CB9B8B5A2BDCED /* CheckpointExamView.swift */; }; A4DA8CF1957A4FE161830AB2 /* ReflexiveVerbStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 940826D9ED5C18D2C4E7B2C7 /* ReflexiveVerbStore.swift */; }; + A651D3E1584A34472BCE53B5 /* textbook_vocab.json in Resources */ = {isa = PBXBuildFile; fileRef = 3540936F058728CFD87B1A1E /* textbook_vocab.json */; }; A7DF435F99E66E067F2B33E1 /* ListeningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D1904DF07E0A6816134CF3 /* ListeningView.swift */; }; A9959AE6C87B4AD21554E401 /* FullTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 711CB7539EF5887F6F7B8B82 /* FullTableView.swift */; }; AAC6F85A1C3B6C1186E1656A /* TenseEndingTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D98E1564C6538056D81200 /* TenseEndingTable.swift */; }; @@ -104,6 +106,7 @@ E99473B7DF9BCAE150E9D1E1 /* WidgetDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D570252DA3DCDD9217C71863 /* WidgetDataService.swift */; }; ED0401D05A7C2B4C55057A88 /* DailyProgressWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195DA9CDA703DDFAD1B3CD5A /* DailyProgressWidget.swift */; }; F0D0778207F144D6AC3D39C3 /* CourseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833516C5D57F164C8660A479 /* CourseView.swift */; }; + F15E2E02DD6261323BB75789 /* textbook_data.json in Resources */ = {isa = PBXBuildFile; fileRef = 539736EB2AB8D149ED0F9C39 /* textbook_data.json */; }; F26F3BF58CF557D5A65EE901 /* youtube_videos.json in Resources */ = {isa = PBXBuildFile; fileRef = 6658C35E454C137B53FC05A4 /* youtube_videos.json */; }; F59655A8B8FCE6264315DD33 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A014EEC3EE08E945FBBA5335 /* Assets.xcassets */; }; F84706B47A2156B2138FB8D5 /* GrammarNotesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F1A6221A35699BD8065D064 /* GrammarNotesView.swift */; }; @@ -161,6 +164,7 @@ 2931634BEB33B93429CE254F /* VocabFlashcardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VocabFlashcardView.swift; sourceTree = ""; }; 30EF2362D9FFF9B07A45CE6D /* StreakCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreakCalendarView.swift; sourceTree = ""; }; 34C67DD1A1CB9B8B5A2BDCED /* CheckpointExamView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckpointExamView.swift; sourceTree = ""; }; + 3540936F058728CFD87B1A1E /* textbook_vocab.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = textbook_vocab.json; sourceTree = ""; }; 3644B5ED77F29A65877D926A /* reflexive_verbs.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = reflexive_verbs.json; sourceTree = ""; }; 3695075616689E72DBB26D4C /* HandwritingRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandwritingRecognizer.swift; sourceTree = ""; }; 39908548430FDF01D76201FB /* TextbookChapterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextbookChapterView.swift; sourceTree = ""; }; @@ -179,6 +183,7 @@ 4D389CA5B5C4E7A12CAEA5BC /* GrammarNote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrammarNote.swift; sourceTree = ""; }; 4EC8C4E931AD7A1D87C490BB /* StudyTimerService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyTimerService.swift; sourceTree = ""; }; 51FA0182FB27A06FFC703138 /* VideoDownloadService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDownloadService.swift; sourceTree = ""; }; + 539736EB2AB8D149ED0F9C39 /* textbook_data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = textbook_data.json; sourceTree = ""; }; 58394296923991E56BAC2B02 /* LyricsReaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LyricsReaderView.swift; sourceTree = ""; }; 5983A534E4836F30B5281ACB /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = ""; }; 5BF946245110C92F087D81E8 /* PracticeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PracticeHeaderView.swift; sourceTree = ""; }; @@ -215,6 +220,7 @@ A014EEC3EE08E945FBBA5335 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; A4B95B276C054DBFE508C4D1 /* StartupCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupCoordinator.swift; sourceTree = ""; }; A63061BBC8998DF33E3DCA2B /* VerbListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerbListView.swift; sourceTree = ""; }; + A6EC7C278E4287D91A0DB355 /* youtube_videos.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = youtube_videos.md; sourceTree = ""; }; A7CDC5F2660A3009A3ADF048 /* StoryQuizView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryQuizView.swift; sourceTree = ""; }; AC34396050805693AA4AC582 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; AFF65B05E7CEC386F121973E /* YouTubeVideoStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YouTubeVideoStore.swift; sourceTree = ""; }; @@ -274,10 +280,12 @@ BC273716CD14A99EFF8206CA /* course_data.json */, 7E6AF62A3A949630E067DC22 /* Info.plist */, 3644B5ED77F29A65877D926A /* reflexive_verbs.json */, + 539736EB2AB8D149ED0F9C39 /* textbook_data.json */, + 3540936F058728CFD87B1A1E /* textbook_vocab.json */, 6658C35E454C137B53FC05A4 /* youtube_videos.json */, + A6EC7C278E4287D91A0DB355 /* youtube_videos.md */, 353C5DE41FD410FA82E3AED7 /* Models */, 1994867BC8E985795A172854 /* Services */, - BFC1AEBE02CE22E6474FFEA6 /* Utilities */, 3C75490F53C34A37084FF478 /* ViewModels */, A81CA75762B08D35D5B7A44D /* Views */, ); @@ -511,13 +519,6 @@ path = Course; sourceTree = ""; }; - BFC1AEBE02CE22E6474FFEA6 /* Utilities */ = { - isa = PBXGroup; - children = ( - ); - path = Utilities; - sourceTree = ""; - }; F605D24E5EA11065FD18AF7E /* Products */ = { isa = PBXGroup; children = ( @@ -600,7 +601,6 @@ }; }; buildConfigurationList = F011A5DA3101F6F7CA7D2D95 /* Build configuration list for PBXProject "Conjuga" */; - compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -614,6 +614,7 @@ 548B46ED3C40F5F28A5ADCC6 /* XCLocalSwiftPackageReference "SharedModels" */, ); preferredProjectObjectVersion = 77; + productRefGroup = F605D24E5EA11065FD18AF7E /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( @@ -632,7 +633,10 @@ CF9E48ADF0501FB79F3DDB7B /* conjuga_data.json in Resources */, 2B5B2D63DC9C290F66890A4A /* course_data.json in Resources */, 97A2088134FC6CB41C507182 /* reflexive_verbs.json in Resources */, + F15E2E02DD6261323BB75789 /* textbook_data.json in Resources */, + A651D3E1584A34472BCE53B5 /* textbook_vocab.json in Resources */, F26F3BF58CF557D5A65EE901 /* youtube_videos.json in Resources */, + 983988CE911C0FC5D869C516 /* youtube_videos.md in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Conjuga/Conjuga/Views/Guide/VideoActionsView.swift b/Conjuga/Conjuga/Views/Guide/VideoActionsView.swift index b95305a..209a3ee 100644 --- a/Conjuga/Conjuga/Views/Guide/VideoActionsView.swift +++ b/Conjuga/Conjuga/Views/Guide/VideoActionsView.swift @@ -93,6 +93,7 @@ struct VideoActionsButtonRow: View { } } label: { Label("Stream", systemImage: "play.rectangle.fill") + .labelStyle(VideoActionLabelStyle()) .frame(maxWidth: .infinity) } .buttonStyle(.borderedProminent) @@ -106,14 +107,14 @@ struct VideoActionsButtonRow: View { // Download → progress/label (disabled) → Delete. if let status = activeStatus { Button {} label: { - HStack(spacing: 6) { + VStack(spacing: 4) { if let progress = status.progress { - ProgressView(value: progress).frame(width: 40) + ProgressView(value: progress).frame(width: 56) } else { ProgressView().controlSize(.small) } Text(status.label) - .font(.caption.monospacedDigit()) + .font(.caption2.monospacedDigit().weight(.semibold)) .lineLimit(1) .minimumScaleFactor(0.7) } @@ -128,6 +129,7 @@ struct VideoActionsButtonRow: View { confirmDelete = true } label: { Label("Delete", systemImage: "trash") + .labelStyle(VideoActionLabelStyle()) .frame(maxWidth: .infinity) } .buttonStyle(.bordered) @@ -138,6 +140,7 @@ struct VideoActionsButtonRow: View { Task { await startDownload() } } label: { Label("Download", systemImage: "arrow.down.to.line") + .labelStyle(VideoActionLabelStyle()) .frame(maxWidth: .infinity) } .buttonStyle(.bordered) @@ -151,6 +154,7 @@ struct VideoActionsButtonRow: View { playerVideoId = video.videoId } label: { Label("Play", systemImage: "play.fill") + .labelStyle(VideoActionLabelStyle()) .frame(maxWidth: .infinity) } .buttonStyle(.bordered) @@ -182,6 +186,21 @@ private struct LocalVideoID: Identifiable { var id: String { videoId } } +// Stacks the icon above the title so three equal-width buttons fit an iPhone +// row without wrapping mid-word. +private struct VideoActionLabelStyle: LabelStyle { + func makeBody(configuration: Configuration) -> some View { + VStack(spacing: 4) { + configuration.icon + .imageScale(.large) + configuration.title + .font(.caption2.weight(.semibold)) + .lineLimit(1) + .minimumScaleFactor(0.8) + } + } +} + // MARK: - Local playback sheet struct LocalVideoPlayerSheet: View { diff --git a/Conjuga/project.yml b/Conjuga/project.yml index 078b13f..d69b71e 100644 --- a/Conjuga/project.yml +++ b/Conjuga/project.yml @@ -54,6 +54,10 @@ targets: buildPhase: resources - path: Conjuga/youtube_videos.json buildPhase: resources + - path: Conjuga/textbook_data.json + buildPhase: resources + - path: Conjuga/textbook_vocab.json + buildPhase: resources info: path: Conjuga/Info.plist properties: