Course — Review Course Material row with bundled weekly PDFs
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,9 @@ scrape/
|
||||
*.pdf
|
||||
*.epub
|
||||
epub_extract/
|
||||
# Exception: weekly course-material PDFs are bundled into the app and must
|
||||
# travel with the repo so fresh clones build with the feature working.
|
||||
!Conjuga/Conjuga/CourseMaterials/*.pdf
|
||||
|
||||
# Textbook extraction artifacts — regenerate locally via run_pipeline.sh.
|
||||
# Scripts are committed; their generated outputs are not.
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
2B76DE203AEEBAE4536BD4F0 /* NounMultipleChoicePracticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1F30E0A920FD7AA4F952CE /* NounMultipleChoicePracticeView.swift */; };
|
||||
2C7ABAB4D88E3E3B0EAD1EF7 /* PracticeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF946245110C92F087D81E8 /* PracticeHeaderView.swift */; };
|
||||
33E885EB38C3BB0CB058871A /* HandwritingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F842EB5E566C74658D918BB /* HandwritingView.swift */; };
|
||||
345AB6723C15590031B75A01 /* Beginner_I_W2.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9518665E390E8BE4D7D957FE /* Beginner_I_W2.pdf */; };
|
||||
352A5BAA6E406AA5850653A4 /* PracticeSessionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842DB48F8570C39CDCFF2F57 /* PracticeSessionService.swift */; };
|
||||
3535A6B73D03486EB2E43823 /* Beginner_I_W1.pdf in Resources */ = {isa = PBXBuildFile; fileRef = CF62E9D108E2E564BA61A694 /* Beginner_I_W1.pdf */; };
|
||||
354631F309E625046A3A436B /* TextbookExerciseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 854EA2A8D6CF203958BA3C24 /* TextbookExerciseView.swift */; };
|
||||
35A0F6E7124D989312721F7D /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18AC3C548BDB9EF8701BE64C /* DashboardView.swift */; };
|
||||
362F2159FC4C6E2A3DCBF07F /* YouTubeKit in Frameworks */ = {isa = PBXBuildFile; productRef = 08D6313690BEE4E2F18EADC3 /* YouTubeKit */; };
|
||||
@@ -49,10 +51,13 @@
|
||||
50E0095A23E119D1AB561232 /* VerbDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1DBE662F89F02A0282F5BEE /* VerbDetailView.swift */; };
|
||||
519E68D2DF4C80AB96058C0D /* LyricsConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA01795655C444795577A22 /* LyricsConfirmationView.swift */; };
|
||||
51D072AF30F4B12CD3E8F918 /* SRSEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0E6EAFC0D24928BA956FA5 /* SRSEngine.swift */; };
|
||||
5224FD701320B7DBCEFDD95B /* Beginner_I_W4.pdf in Resources */ = {isa = PBXBuildFile; fileRef = B07767E9BC347964B98C0F3D /* Beginner_I_W4.pdf */; };
|
||||
53A0AC57EAC44B676C997374 /* QuizType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626873572466403C0288090D /* QuizType.swift */; };
|
||||
55FBABFE026058CFE8B40CB6 /* LexemeReviewStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4B8AB6EC4F3EB86C732243D /* LexemeReviewStore.swift */; };
|
||||
5A3246026E68AB6483126D0B /* WeekProgressWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1980E8E439EB76ED7330A90D /* WeekProgressWidget.swift */; };
|
||||
5BB8A0C1A8A6374B04F50781 /* VocabMultipleChoicePracticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 878D04B21589BAB7CF8EA0AF /* VocabMultipleChoicePracticeView.swift */; };
|
||||
5C1C0011594A2C06BCD777A4 /* Beginner_I_W7.pdf in Resources */ = {isa = PBXBuildFile; fileRef = E8CEEA98D1805C5BA20A4559 /* Beginner_I_W7.pdf */; };
|
||||
5CBAD967B3545EA7560761C6 /* Beginner_I_W3.pdf in Resources */ = {isa = PBXBuildFile; fileRef = EBDEA606F038A4A3DF1967E3 /* Beginner_I_W3.pdf */; };
|
||||
5EA89448E97D76CFC97DC4E2 /* VocabFlashcardPracticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB6453FB9DCFAEC1C4E42D83 /* VocabFlashcardPracticeView.swift */; };
|
||||
5EA915FFA906C5C2938FCADA /* ConjugaWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E325FE0E484DE75009672D02 /* ConjugaWidgetBundle.swift */; };
|
||||
5EE41911F3D17224CAB359ED /* StudyTimerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC8C4E931AD7A1D87C490BB /* StudyTimerService.swift */; };
|
||||
@@ -87,6 +92,7 @@
|
||||
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 */; };
|
||||
995C466AE3C95A95DB9457A1 /* Beginner_I_W6.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8B54119366CF052443A8C080 /* Beginner_I_W6.pdf */; };
|
||||
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 */; };
|
||||
@@ -106,6 +112,7 @@
|
||||
BB48230C3B26EA6E84D2D823 /* DailyProgressRing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180F9D59828C36B44A5E384F /* DailyProgressRing.swift */; };
|
||||
BF0832865857EFDA1D1CDEAD /* SharedModels in Frameworks */ = {isa = PBXBuildFile; productRef = BCCBABD74CADDB118179D8E9 /* SharedModels */; };
|
||||
C0BAEF49A6270D8F64CF13D6 /* PracticeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C359C051FB157EF447561405 /* PracticeViewModel.swift */; };
|
||||
C0DF369A6E30F01514A78CA1 /* Beginner_I_W5.pdf in Resources */ = {isa = PBXBuildFile; fileRef = EBBA697AFA8988BBB15C4717 /* Beginner_I_W5.pdf */; };
|
||||
C1F84182F12EB5CFF32768B6 /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5983A534E4836F30B5281ACB /* MainTabView.swift */; };
|
||||
C2B3D97F119EFCE97E3CB1CE /* ConjugaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EB4830F9289AACC82D753F8 /* ConjugaApp.swift */; };
|
||||
C384F12E910EE909AA97ECD9 /* LexemeSessionQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE79D3EC48233CAEC2E39F81 /* LexemeSessionQueue.swift */; };
|
||||
@@ -129,9 +136,11 @@
|
||||
E7BFEE9A90E1300EFF5B1F32 /* HandwritingRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3695075616689E72DBB26D4C /* HandwritingRecognizer.swift */; };
|
||||
E814A9CF1067313F74B509C6 /* StoreInspector.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E9833868EB73AF9EB3A611 /* StoreInspector.swift */; };
|
||||
E99473B7DF9BCAE150E9D1E1 /* WidgetDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D570252DA3DCDD9217C71863 /* WidgetDataService.swift */; };
|
||||
EB7CF33BA416BD7B5D995FF4 /* Beginner_I_W8.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83AA457D61B9D2C86C301236 /* Beginner_I_W8.pdf */; };
|
||||
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 */; };
|
||||
F22FD38D5CD6A89CC5940B0E /* CourseMaterialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42513164C858C8CBE1A70AF /* CourseMaterialView.swift */; };
|
||||
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 */; };
|
||||
@@ -240,15 +249,18 @@
|
||||
80D974250C396589656B8443 /* HandwritingCanvas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandwritingCanvas.swift; sourceTree = "<group>"; };
|
||||
833516C5D57F164C8660A479 /* CourseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseView.swift; sourceTree = "<group>"; };
|
||||
83A8C1A048627C8DEB83C12D /* AnswerReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnswerReviewView.swift; sourceTree = "<group>"; };
|
||||
83AA457D61B9D2C86C301236 /* Beginner_I_W8.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Beginner_I_W8.pdf; sourceTree = "<group>"; };
|
||||
841A3015E99E1E2FF19FF8EA /* VocabSessionQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VocabSessionQueue.swift; sourceTree = "<group>"; };
|
||||
842DB48F8570C39CDCFF2F57 /* PracticeSessionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PracticeSessionService.swift; sourceTree = "<group>"; };
|
||||
854EA2A8D6CF203958BA3C24 /* TextbookExerciseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextbookExerciseView.swift; sourceTree = "<group>"; };
|
||||
86A57770AAE3FAC5DCA329F9 /* VideoActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoActionsView.swift; sourceTree = "<group>"; };
|
||||
878D04B21589BAB7CF8EA0AF /* VocabMultipleChoicePracticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VocabMultipleChoicePracticeView.swift; sourceTree = "<group>"; };
|
||||
8B54119366CF052443A8C080 /* Beginner_I_W6.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Beginner_I_W6.pdf; sourceTree = "<group>"; };
|
||||
8C2D88FF9A3B0590B22C7837 /* conjuga_data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = conjuga_data.json; sourceTree = "<group>"; };
|
||||
8C935ECDF8A5D8D6FA541E20 /* GuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuideView.swift; sourceTree = "<group>"; };
|
||||
8E9BCDBB9BC24F5C8117767E /* WordOfDayWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordOfDayWidget.swift; sourceTree = "<group>"; };
|
||||
940826D9ED5C18D2C4E7B2C7 /* ReflexiveVerbStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReflexiveVerbStore.swift; sourceTree = "<group>"; };
|
||||
9518665E390E8BE4D7D957FE /* Beginner_I_W2.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Beginner_I_W2.pdf; sourceTree = "<group>"; };
|
||||
9708FF3CF33E4765DB225F93 /* ConjugaWidgetExtension.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = ConjugaWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9E1FB35614B709E6B1D1D017 /* Conjuga.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Conjuga.entitlements; sourceTree = "<group>"; };
|
||||
A014EEC3EE08E945FBBA5335 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@@ -261,6 +273,7 @@
|
||||
AC34396050805693AA4AC582 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
AEC6005F9D3D462B33803E7A /* LexemeStudyGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexemeStudyGroup.swift; sourceTree = "<group>"; };
|
||||
AFF65B05E7CEC386F121973E /* YouTubeVideoStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YouTubeVideoStore.swift; sourceTree = "<group>"; };
|
||||
B07767E9BC347964B98C0F3D /* Beginner_I_W4.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Beginner_I_W4.pdf; sourceTree = "<group>"; };
|
||||
B3EFFA19D0AB2528A868E8ED /* AnswerChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnswerChecker.swift; sourceTree = "<group>"; };
|
||||
BC273716CD14A99EFF8206CA /* course_data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = course_data.json; sourceTree = "<group>"; };
|
||||
BCCC95A95581458E068E0484 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
@@ -273,6 +286,7 @@
|
||||
CB6453FB9DCFAEC1C4E42D83 /* VocabFlashcardPracticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VocabFlashcardPracticeView.swift; sourceTree = "<group>"; };
|
||||
CBCF6FCFA6B00151C2371E77 /* ReviewStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewStore.swift; sourceTree = "<group>"; };
|
||||
CCC782D61A0763072E4964B6 /* VerbReviewStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerbReviewStore.swift; sourceTree = "<group>"; };
|
||||
CF62E9D108E2E564BA61A694 /* Beginner_I_W1.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Beginner_I_W1.pdf; sourceTree = "<group>"; };
|
||||
CF6D58AEE2F0DFE0F1829A73 /* SharedModels */ = {isa = PBXFileReference; lastKnownFileType = folder; name = SharedModels; path = SharedModels; sourceTree = SOURCE_ROOT; };
|
||||
D232CDA43CC9218D748BA121 /* ClozeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClozeView.swift; sourceTree = "<group>"; };
|
||||
D4B8AB6EC4F3EB86C732243D /* LexemeReviewStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexemeReviewStore.swift; sourceTree = "<group>"; };
|
||||
@@ -283,12 +297,16 @@
|
||||
DAFE27F29412021AEC57E728 /* TestResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestResult.swift; sourceTree = "<group>"; };
|
||||
E1DBE662F89F02A0282F5BEE /* VerbDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerbDetailView.swift; sourceTree = "<group>"; };
|
||||
E325FE0E484DE75009672D02 /* ConjugaWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConjugaWidgetBundle.swift; sourceTree = "<group>"; };
|
||||
E42513164C858C8CBE1A70AF /* CourseMaterialView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseMaterialView.swift; sourceTree = "<group>"; };
|
||||
E4365AC54DB1DA5D7017CB42 /* NounReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NounReviewView.swift; sourceTree = "<group>"; };
|
||||
E536AD1180FE10576EAC884A /* UserProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProgress.swift; sourceTree = "<group>"; };
|
||||
E6BA2ABC841F9C987AB15F67 /* GuideCrossLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuideCrossLinks.swift; sourceTree = "<group>"; };
|
||||
E8CEEA98D1805C5BA20A4559 /* Beginner_I_W7.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Beginner_I_W7.pdf; sourceTree = "<group>"; };
|
||||
E8D95887B18216FCA71643D6 /* VocabReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VocabReviewView.swift; sourceTree = "<group>"; };
|
||||
E8E9833868EB73AF9EB3A611 /* StoreInspector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreInspector.swift; sourceTree = "<group>"; };
|
||||
E972AA745F44586EF0B1B0C8 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
|
||||
EBBA697AFA8988BBB15C4717 /* Beginner_I_W5.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Beginner_I_W5.pdf; sourceTree = "<group>"; };
|
||||
EBDEA606F038A4A3DF1967E3 /* Beginner_I_W3.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Beginner_I_W3.pdf; sourceTree = "<group>"; };
|
||||
EBEEC9CC9A8C502AF5F42914 /* VerbExampleCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerbExampleCache.swift; sourceTree = "<group>"; };
|
||||
EE00D37419072ED6AC1AC63A /* ExtraStudyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtraStudyView.swift; sourceTree = "<group>"; };
|
||||
EE79D3EC48233CAEC2E39F81 /* LexemeSessionQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexemeSessionQueue.swift; sourceTree = "<group>"; };
|
||||
@@ -336,6 +354,7 @@
|
||||
7C60B9668AC8A5B3424E9F9C /* vocab_lexemes.json */,
|
||||
6658C35E454C137B53FC05A4 /* youtube_videos.json */,
|
||||
A6EC7C278E4287D91A0DB355 /* youtube_videos.md */,
|
||||
2610354CB0D62BD8A19BEC20 /* CourseMaterials */,
|
||||
353C5DE41FD410FA82E3AED7 /* Models */,
|
||||
23B49FBE9B44D8734D96625F /* Scripts */,
|
||||
1994867BC8E985795A172854 /* Services */,
|
||||
@@ -410,6 +429,21 @@
|
||||
path = Scripts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2610354CB0D62BD8A19BEC20 /* CourseMaterials */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CF62E9D108E2E564BA61A694 /* Beginner_I_W1.pdf */,
|
||||
9518665E390E8BE4D7D957FE /* Beginner_I_W2.pdf */,
|
||||
EBDEA606F038A4A3DF1967E3 /* Beginner_I_W3.pdf */,
|
||||
B07767E9BC347964B98C0F3D /* Beginner_I_W4.pdf */,
|
||||
EBBA697AFA8988BBB15C4717 /* Beginner_I_W5.pdf */,
|
||||
8B54119366CF052443A8C080 /* Beginner_I_W6.pdf */,
|
||||
E8CEEA98D1805C5BA20A4559 /* Beginner_I_W7.pdf */,
|
||||
83AA457D61B9D2C86C301236 /* Beginner_I_W8.pdf */,
|
||||
);
|
||||
path = CourseMaterials;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29F9EEAED5A6969FEDEAE227 /* Onboarding */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -621,6 +655,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
34C67DD1A1CB9B8B5A2BDCED /* CheckpointExamView.swift */,
|
||||
E42513164C858C8CBE1A70AF /* CourseMaterialView.swift */,
|
||||
143D06606AE10DCA30A140C2 /* CourseQuizView.swift */,
|
||||
833516C5D57F164C8660A479 /* CourseView.swift */,
|
||||
631DC0A942DD57C81DECE083 /* DeckStudyView.swift */,
|
||||
@@ -753,6 +788,14 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F59655A8B8FCE6264315DD33 /* Assets.xcassets in Resources */,
|
||||
3535A6B73D03486EB2E43823 /* Beginner_I_W1.pdf in Resources */,
|
||||
345AB6723C15590031B75A01 /* Beginner_I_W2.pdf in Resources */,
|
||||
5CBAD967B3545EA7560761C6 /* Beginner_I_W3.pdf in Resources */,
|
||||
5224FD701320B7DBCEFDD95B /* Beginner_I_W4.pdf in Resources */,
|
||||
C0DF369A6E30F01514A78CA1 /* Beginner_I_W5.pdf in Resources */,
|
||||
995C466AE3C95A95DB9457A1 /* Beginner_I_W6.pdf in Resources */,
|
||||
5C1C0011594A2C06BCD777A4 /* Beginner_I_W7.pdf in Resources */,
|
||||
EB7CF33BA416BD7B5D995FF4 /* Beginner_I_W8.pdf in Resources */,
|
||||
877B0306A15A0AD680B361F8 /* book_olly-vol2.json in Resources */,
|
||||
CF9E48ADF0501FB79F3DDB7B /* conjuga_data.json in Resources */,
|
||||
2B5B2D63DC9C290F66890A4A /* course_data.json in Resources */,
|
||||
@@ -790,6 +833,7 @@
|
||||
04C74D9E0ED84BF785A8331C /* ClozeView.swift in Sources */,
|
||||
C2B3D97F119EFCE97E3CB1CE /* ConjugaApp.swift in Sources */,
|
||||
ABBE5080E254D1D3E3465E40 /* ConversationService.swift in Sources */,
|
||||
F22FD38D5CD6A89CC5940B0E /* CourseMaterialView.swift in Sources */,
|
||||
C3851F960C1162239DC2F935 /* CourseQuizView.swift in Sources */,
|
||||
8C43F09F52EA9B537EA27E43 /* CourseReviewStore.swift in Sources */,
|
||||
F0D0778207F144D6AC3D39C3 /* CourseView.swift in Sources */,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,69 @@
|
||||
import SwiftUI
|
||||
import PDFKit
|
||||
|
||||
struct CourseMaterialView: View {
|
||||
let weekNumber: Int
|
||||
let courseName: String
|
||||
|
||||
private var resourceName: String? {
|
||||
// Only Beginner I has bundled PDFs (Beginner_I_W1.pdf … Beginner_I_W8.pdf).
|
||||
guard courseName.contains("Beginner I") else { return nil }
|
||||
return "Beginner_I_W\(weekNumber)"
|
||||
}
|
||||
|
||||
private var pdfURL: URL? {
|
||||
guard let name = resourceName else { return nil }
|
||||
return Bundle.main.url(forResource: name, withExtension: "pdf")
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if let url = pdfURL {
|
||||
PDFKitView(url: url)
|
||||
.ignoresSafeArea(edges: .bottom)
|
||||
} else {
|
||||
ContentUnavailableView(
|
||||
"Material unavailable",
|
||||
systemImage: "doc.questionmark",
|
||||
description: Text("No course material is bundled for week \(weekNumber).")
|
||||
)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Week \(weekNumber) Material")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
if let url = pdfURL {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
ShareLink(item: url) {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct PDFKitView: UIViewRepresentable {
|
||||
let url: URL
|
||||
|
||||
func makeUIView(context: Context) -> PDFView {
|
||||
let view = PDFView()
|
||||
view.document = PDFDocument(url: url)
|
||||
view.autoScales = true
|
||||
view.displayMode = .singlePageContinuous
|
||||
view.displayDirection = .vertical
|
||||
view.usePageViewController(false)
|
||||
return view
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: PDFView, context: Context) {
|
||||
if uiView.document?.documentURL != url {
|
||||
uiView.document = PDFDocument(url: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CourseMaterialDestination: Hashable {
|
||||
let courseName: String
|
||||
let weekNumber: Int
|
||||
}
|
||||
@@ -57,6 +57,11 @@ struct CourseView: View {
|
||||
return results.map(\.scorePercent).max()
|
||||
}
|
||||
|
||||
private func hasCourseMaterial(for week: Int) -> Bool {
|
||||
guard activeCourse.contains("Beginner I") else { return false }
|
||||
return Bundle.main.url(forResource: "Beginner_I_W\(week)", withExtension: "pdf") != nil
|
||||
}
|
||||
|
||||
private func shortName(_ full: String) -> String {
|
||||
full.replacingOccurrences(of: "LanGo Spanish | ", with: "")
|
||||
.replacingOccurrences(of: "LanGo Spanish ", with: "")
|
||||
@@ -116,6 +121,28 @@ struct CourseView: View {
|
||||
// Week sections
|
||||
ForEach(weekGroups, id: \.week) { week, weekDecks in
|
||||
Section {
|
||||
// Course material (PDF)
|
||||
if hasCourseMaterial(for: week) {
|
||||
NavigationLink(value: CourseMaterialDestination(courseName: activeCourse, weekNumber: week)) {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: "doc.richtext")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.purple)
|
||||
.frame(width: 32)
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Review Course Material")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Text("Week \(week) PDF")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test button
|
||||
NavigationLink(value: WeekTestDestination(courseName: activeCourse, weekNumber: week)) {
|
||||
HStack(spacing: 12) {
|
||||
@@ -231,6 +258,9 @@ struct CourseView: View {
|
||||
.navigationDestination(for: TextbookExerciseDestination.self) { dest in
|
||||
textbookExerciseView(for: dest)
|
||||
}
|
||||
.navigationDestination(for: CourseMaterialDestination.self) { dest in
|
||||
CourseMaterialView(weekNumber: dest.weekNumber, courseName: dest.courseName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ targets:
|
||||
buildPhase: resources
|
||||
- path: Conjuga/vocab_lexemes.json
|
||||
buildPhase: resources
|
||||
- path: Conjuga/CourseMaterials
|
||||
buildPhase: resources
|
||||
info:
|
||||
path: Conjuga/Info.plist
|
||||
properties:
|
||||
|
||||
Reference in New Issue
Block a user