Rework storage structs, link preview
This commit is contained in:
parent
b99c064d10
commit
a7197b9628
@ -16,21 +16,18 @@
|
|||||||
E218502B2CF790B30090B18B /* PostContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218502A2CF790AC0090B18B /* PostContentView.swift */; };
|
E218502B2CF790B30090B18B /* PostContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218502A2CF790AC0090B18B /* PostContentView.swift */; };
|
||||||
E218502D2CF791440090B18B /* PostImagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218502C2CF791440090B18B /* PostImagesView.swift */; };
|
E218502D2CF791440090B18B /* PostImagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218502C2CF791440090B18B /* PostImagesView.swift */; };
|
||||||
E21850332CFAFA2F0090B18B /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850322CFAFA200090B18B /* Settings.swift */; };
|
E21850332CFAFA2F0090B18B /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850322CFAFA200090B18B /* Settings.swift */; };
|
||||||
E21850352CFAFA5A0090B18B /* SettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850342CFAFA570090B18B /* SettingsFile.swift */; };
|
|
||||||
E21850372CFCA55F0090B18B /* LocalizedPostSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850362CFCA5580090B18B /* LocalizedPostSettings.swift */; };
|
E21850372CFCA55F0090B18B /* LocalizedPostSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850362CFCA5580090B18B /* LocalizedPostSettings.swift */; };
|
||||||
E21850392CFCA6C00090B18B /* WebsiteData+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850382CFCA6BA0090B18B /* WebsiteData+Mock.swift */; };
|
|
||||||
E218503D2CFCFD910090B18B /* LocalizedPostFeedSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218503C2CFCFD8C0090B18B /* LocalizedPostFeedSettingsView.swift */; };
|
E218503D2CFCFD910090B18B /* LocalizedPostFeedSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218503C2CFCFD8C0090B18B /* LocalizedPostFeedSettingsView.swift */; };
|
||||||
E22990152D0E2B7F009F8D77 /* ItemSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990142D0E2B74009F8D77 /* ItemSelectionView.swift */; };
|
E22990152D0E2B7F009F8D77 /* ItemSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990142D0E2B74009F8D77 /* ItemSelectionView.swift */; };
|
||||||
E22990172D0E330F009F8D77 /* TagOverviewPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990162D0E32F5009F8D77 /* TagOverviewPage.swift */; };
|
E22990172D0E330F009F8D77 /* TagOverviewPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990162D0E32F5009F8D77 /* TagOverviewPage.swift */; };
|
||||||
E22990192D0E3546009F8D77 /* ItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990182D0E3546009F8D77 /* ItemType.swift */; };
|
E22990192D0E3546009F8D77 /* ItemReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990182D0E3546009F8D77 /* ItemReference.swift */; };
|
||||||
E229901E2D0E4364009F8D77 /* LocalizedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229901D2D0E4362009F8D77 /* LocalizedItem.swift */; };
|
E229901E2D0E4364009F8D77 /* LocalizedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229901D2D0E4362009F8D77 /* LocalizedItem.swift */; };
|
||||||
E22990202D0ECBE5009F8D77 /* TagOverviewDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229901F2D0ECBD4009F8D77 /* TagOverviewDetailView.swift */; };
|
E22990202D0ECBE5009F8D77 /* TagOverviewDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229901F2D0ECBD4009F8D77 /* TagOverviewDetailView.swift */; };
|
||||||
E22990222D0ED12E009F8D77 /* TagOverviewFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990212D0ED129009F8D77 /* TagOverviewFile.swift */; };
|
|
||||||
E22990242D0EDBD0009F8D77 /* HeaderElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990232D0EDBD0009F8D77 /* HeaderElement.swift */; };
|
E22990242D0EDBD0009F8D77 /* HeaderElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990232D0EDBD0009F8D77 /* HeaderElement.swift */; };
|
||||||
E22990262D0F582B009F8D77 /* FilePropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990252D0F5822009F8D77 /* FilePropertyView.swift */; };
|
E22990262D0F582B009F8D77 /* FilePropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990252D0F5822009F8D77 /* FilePropertyView.swift */; };
|
||||||
E22990282D0F596C009F8D77 /* IntegerPropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990272D0F5967009F8D77 /* IntegerPropertyView.swift */; };
|
E22990282D0F596C009F8D77 /* IntegerPropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990272D0F5967009F8D77 /* IntegerPropertyView.swift */; };
|
||||||
E229902A2D0F5A14009F8D77 /* DetailTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990292D0F5A10009F8D77 /* DetailTitle.swift */; };
|
E229902A2D0F5A14009F8D77 /* DetailTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990292D0F5A10009F8D77 /* DetailTitle.swift */; };
|
||||||
E229902C2D0F6FC6009F8D77 /* ItemId.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229902B2D0F6FC0009F8D77 /* ItemId.swift */; };
|
E229902C2D0F6FC6009F8D77 /* LocalizedItemId.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229902B2D0F6FC0009F8D77 /* LocalizedItemId.swift */; };
|
||||||
E229902E2D0F7280009F8D77 /* IdPropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229902D2D0F7278009F8D77 /* IdPropertyView.swift */; };
|
E229902E2D0F7280009F8D77 /* IdPropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229902D2D0F7278009F8D77 /* IdPropertyView.swift */; };
|
||||||
E22990302D0F75DE009F8D77 /* BoolPropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229902F2D0F75CF009F8D77 /* BoolPropertyView.swift */; };
|
E22990302D0F75DE009F8D77 /* BoolPropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E229902F2D0F75CF009F8D77 /* BoolPropertyView.swift */; };
|
||||||
E22990322D0F767B009F8D77 /* DatePropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990312D0F7678009F8D77 /* DatePropertyView.swift */; };
|
E22990322D0F767B009F8D77 /* DatePropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22990312D0F7678009F8D77 /* DatePropertyView.swift */; };
|
||||||
@ -53,7 +50,6 @@
|
|||||||
E25DA5092CFD964E00AEF16D /* TagContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5082CFD964E00AEF16D /* TagContentView.swift */; };
|
E25DA5092CFD964E00AEF16D /* TagContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5082CFD964E00AEF16D /* TagContentView.swift */; };
|
||||||
E25DA50B2CFD988100AEF16D /* PageTagAssignmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA50A2CFD988100AEF16D /* PageTagAssignmentView.swift */; };
|
E25DA50B2CFD988100AEF16D /* PageTagAssignmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA50A2CFD988100AEF16D /* PageTagAssignmentView.swift */; };
|
||||||
E25DA50D2CFD9BA200AEF16D /* PostTagAssignmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA50C2CFD9B9C00AEF16D /* PostTagAssignmentView.swift */; };
|
E25DA50D2CFD9BA200AEF16D /* PostTagAssignmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA50C2CFD9B9C00AEF16D /* PostTagAssignmentView.swift */; };
|
||||||
E25DA5152CFF00C100AEF16D /* Content+Load.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5142CFF00B900AEF16D /* Content+Load.swift */; };
|
|
||||||
E25DA5172CFF00F500AEF16D /* Content+Save.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5162CFF00F200AEF16D /* Content+Save.swift */; };
|
E25DA5172CFF00F500AEF16D /* Content+Save.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5162CFF00F200AEF16D /* Content+Save.swift */; };
|
||||||
E25DA5192CFF035900AEF16D /* Array+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5182CFF035200AEF16D /* Array+Split.swift */; };
|
E25DA5192CFF035900AEF16D /* Array+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5182CFF035200AEF16D /* Array+Split.swift */; };
|
||||||
E25DA51B2CFF08BB00AEF16D /* PostFeedPageNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA51A2CFF08AF00AEF16D /* PostFeedPageNavigation.swift */; };
|
E25DA51B2CFF08BB00AEF16D /* PostFeedPageNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA51A2CFF08AF00AEF16D /* PostFeedPageNavigation.swift */; };
|
||||||
@ -64,8 +60,6 @@
|
|||||||
E25DA5272CFF745700AEF16D /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5262CFF745200AEF16D /* URL+Extensions.swift */; };
|
E25DA5272CFF745700AEF16D /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5262CFF745200AEF16D /* URL+Extensions.swift */; };
|
||||||
E25DA52C2CFFC3EC00AEF16D /* SDWebImageAVIFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = E25DA52B2CFFC3EC00AEF16D /* SDWebImageAVIFCoder */; };
|
E25DA52C2CFFC3EC00AEF16D /* SDWebImageAVIFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = E25DA52B2CFFC3EC00AEF16D /* SDWebImageAVIFCoder */; };
|
||||||
E25DA52F2CFFC91B00AEF16D /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = E25DA52E2CFFC91B00AEF16D /* SDWebImageWebPCoder */; };
|
E25DA52F2CFFC91B00AEF16D /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = E25DA52E2CFFC91B00AEF16D /* SDWebImageWebPCoder */; };
|
||||||
E25DA5362D0041EB00AEF16D /* PostSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5352D0041E200AEF16D /* PostSettingsFile.swift */; };
|
|
||||||
E25DA5382D00420E00AEF16D /* LocalizedPostSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5372D00420D00AEF16D /* LocalizedPostSettingsFile.swift */; };
|
|
||||||
E25DA5412D00446C00AEF16D /* PostSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5402D00446700AEF16D /* PostSettings.swift */; };
|
E25DA5412D00446C00AEF16D /* PostSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5402D00446700AEF16D /* PostSettings.swift */; };
|
||||||
E25DA5452D00952E00AEF16D /* SettingsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5442D00952D00AEF16D /* SettingsSection.swift */; };
|
E25DA5452D00952E00AEF16D /* SettingsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5442D00952D00AEF16D /* SettingsSection.swift */; };
|
||||||
E25DA56D2D00EBCF00AEF16D /* NavigationBarSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA56C2D00EBC900AEF16D /* NavigationBarSettingsView.swift */; };
|
E25DA56D2D00EBCF00AEF16D /* NavigationBarSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA56C2D00EBC900AEF16D /* NavigationBarSettingsView.swift */; };
|
||||||
@ -82,7 +76,6 @@
|
|||||||
E25DA58B2D020C9500AEF16D /* PageImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA58A2D020C9200AEF16D /* PageImage.swift */; };
|
E25DA58B2D020C9500AEF16D /* PageImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA58A2D020C9200AEF16D /* PageImage.swift */; };
|
||||||
E25DA58F2D02368D00AEF16D /* PageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA58E2D02368A00AEF16D /* PageSettings.swift */; };
|
E25DA58F2D02368D00AEF16D /* PageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA58E2D02368A00AEF16D /* PageSettings.swift */; };
|
||||||
E25DA5912D023A8400AEF16D /* IntegerField.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5902D023A7E00AEF16D /* IntegerField.swift */; };
|
E25DA5912D023A8400AEF16D /* IntegerField.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5902D023A7E00AEF16D /* IntegerField.swift */; };
|
||||||
E25DA5932D023B3C00AEF16D /* PageSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5922D023B3600AEF16D /* PageSettingsFile.swift */; };
|
|
||||||
E25DA5952D023BD100AEF16D /* PageSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5942D023BCC00AEF16D /* PageSettingsDetailView.swift */; };
|
E25DA5952D023BD100AEF16D /* PageSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5942D023BCC00AEF16D /* PageSettingsDetailView.swift */; };
|
||||||
E25DA5992D02401E00AEF16D /* PageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5982D02401A00AEF16D /* PageGenerator.swift */; };
|
E25DA5992D02401E00AEF16D /* PageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5982D02401A00AEF16D /* PageGenerator.swift */; };
|
||||||
E25DA59B2D024A2B00AEF16D /* DateItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA59A2D024A2900AEF16D /* DateItem.swift */; };
|
E25DA59B2D024A2B00AEF16D /* DateItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA59A2D024A2900AEF16D /* DateItem.swift */; };
|
||||||
@ -91,7 +84,6 @@
|
|||||||
E29D31242D0366860051B7F4 /* TagList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31232D0366820051B7F4 /* TagList.swift */; };
|
E29D31242D0366860051B7F4 /* TagList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31232D0366820051B7F4 /* TagList.swift */; };
|
||||||
E29D31262D0370A80051B7F4 /* VideoCommand+Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31252D0370A50051B7F4 /* VideoCommand+Option.swift */; };
|
E29D31262D0370A80051B7F4 /* VideoCommand+Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31252D0370A50051B7F4 /* VideoCommand+Option.swift */; };
|
||||||
E29D31282D0371930051B7F4 /* ContentPageVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31272D0371870051B7F4 /* ContentPageVideo.swift */; };
|
E29D31282D0371930051B7F4 /* ContentPageVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31272D0371870051B7F4 /* ContentPageVideo.swift */; };
|
||||||
E29D312A2D039B090051B7F4 /* FileDescriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31292D039B050051B7F4 /* FileDescriptions.swift */; };
|
|
||||||
E29D312C2D039DB80051B7F4 /* PageDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D312B2D039DB30051B7F4 /* PageDetailView.swift */; };
|
E29D312C2D039DB80051B7F4 /* PageDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D312B2D039DB30051B7F4 /* PageDetailView.swift */; };
|
||||||
E29D312E2D03A0D70051B7F4 /* LocalizedPageDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D312D2D03A0CF0051B7F4 /* LocalizedPageDetailView.swift */; };
|
E29D312E2D03A0D70051B7F4 /* LocalizedPageDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D312D2D03A0CF0051B7F4 /* LocalizedPageDetailView.swift */; };
|
||||||
E29D31302D03A2C50051B7F4 /* DescriptionField.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D312F2D03A2BD0051B7F4 /* DescriptionField.swift */; };
|
E29D31302D03A2C50051B7F4 /* DescriptionField.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D312F2D03A2BD0051B7F4 /* DescriptionField.swift */; };
|
||||||
@ -129,7 +121,6 @@
|
|||||||
E29D31902D0B34870051B7F4 /* GenerationAnomaly.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D318F2D0B34870051B7F4 /* GenerationAnomaly.swift */; };
|
E29D31902D0B34870051B7F4 /* GenerationAnomaly.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D318F2D0B34870051B7F4 /* GenerationAnomaly.swift */; };
|
||||||
E29D31942D0B7D280051B7F4 /* SimpleImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31932D0B7D250051B7F4 /* SimpleImage.swift */; };
|
E29D31942D0B7D280051B7F4 /* SimpleImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31932D0B7D250051B7F4 /* SimpleImage.swift */; };
|
||||||
E29D31962D0C186E0051B7F4 /* PathSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31952D0C18690051B7F4 /* PathSettings.swift */; };
|
E29D31962D0C186E0051B7F4 /* PathSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31952D0C18690051B7F4 /* PathSettings.swift */; };
|
||||||
E29D31982D0C19340051B7F4 /* PathSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31972D0C19300051B7F4 /* PathSettingsFile.swift */; };
|
|
||||||
E29D319B2D0C452B0051B7F4 /* PageIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D319A2D0C452B0051B7F4 /* PageIssue.swift */; };
|
E29D319B2D0C452B0051B7F4 /* PageIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D319A2D0C452B0051B7F4 /* PageIssue.swift */; };
|
||||||
E29D319D2D0C45B90051B7F4 /* PageIssueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D319C2D0C45B60051B7F4 /* PageIssueView.swift */; };
|
E29D319D2D0C45B90051B7F4 /* PageIssueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D319C2D0C45B60051B7F4 /* PageIssueView.swift */; };
|
||||||
E29D319F2D0C46310051B7F4 /* PageIssueChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D319E2D0C46290051B7F4 /* PageIssueChecker.swift */; };
|
E29D319F2D0C46310051B7F4 /* PageIssueChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D319E2D0C46290051B7F4 /* PageIssueChecker.swift */; };
|
||||||
@ -163,9 +154,6 @@
|
|||||||
E2A21C512CBBD53F0060935B /* FileResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A21C502CBBD53C0060935B /* FileResource.swift */; };
|
E2A21C512CBBD53F0060935B /* FileResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A21C502CBBD53C0060935B /* FileResource.swift */; };
|
||||||
E2A37D0C2CE4036B0000979F /* LocalizedPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25A0B882CE4021400F33674 /* LocalizedPage.swift */; };
|
E2A37D0C2CE4036B0000979F /* LocalizedPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25A0B882CE4021400F33674 /* LocalizedPage.swift */; };
|
||||||
E2A37D0E2CE527070000979F /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D0D2CE527040000979F /* Storage.swift */; };
|
E2A37D0E2CE527070000979F /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D0D2CE527040000979F /* Storage.swift */; };
|
||||||
E2A37D112CE537800000979F /* PageFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D102CE537670000979F /* PageFile.swift */; };
|
|
||||||
E2A37D152CE68BEC0000979F /* PostFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D142CE68BEA0000979F /* PostFile.swift */; };
|
|
||||||
E2A37D172CE73F1A0000979F /* TagFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D162CE73F170000979F /* TagFile.swift */; };
|
|
||||||
E2A37D192CEA36A90000979F /* LocalizedTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D182CEA36A40000979F /* LocalizedTag.swift */; };
|
E2A37D192CEA36A90000979F /* LocalizedTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D182CEA36A40000979F /* LocalizedTag.swift */; };
|
||||||
E2A37D1B2CEA45560000979F /* Tag+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D1A2CEA45530000979F /* Tag+Mock.swift */; };
|
E2A37D1B2CEA45560000979F /* Tag+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D1A2CEA45530000979F /* Tag+Mock.swift */; };
|
||||||
E2A37D1D2CEA922D0000979F /* LocalizedPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D1C2CEA922A0000979F /* LocalizedPost.swift */; };
|
E2A37D1D2CEA922D0000979F /* LocalizedPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A37D1C2CEA922A0000979F /* LocalizedPost.swift */; };
|
||||||
@ -187,6 +175,14 @@
|
|||||||
E2DD047E2C276F32003BFF1F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DD047D2C276F32003BFF1F /* Preview Assets.xcassets */; };
|
E2DD047E2C276F32003BFF1F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DD047D2C276F32003BFF1F /* Preview Assets.xcassets */; };
|
||||||
E2E06DFB2CA4A65E0019C2AF /* Content.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E06DFA2CA4A6570019C2AF /* Content.swift */; };
|
E2E06DFB2CA4A65E0019C2AF /* Content.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E06DFA2CA4A6570019C2AF /* Content.swift */; };
|
||||||
E2E06E002CA4A8F00019C2AF /* Page+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E06DFF2CA4A8EB0019C2AF /* Page+Mock.swift */; };
|
E2E06E002CA4A8F00019C2AF /* Page+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E06DFF2CA4A8EB0019C2AF /* Page+Mock.swift */; };
|
||||||
|
E2FD1D0D2D2DBBA600B48627 /* LinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D0C2D2DBBA100B48627 /* LinkPreview.swift */; };
|
||||||
|
E2FD1D192D2DC4F500B48627 /* LoadingContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D182D2DC4F500B48627 /* LoadingContext.swift */; };
|
||||||
|
E2FD1D1B2D2DC63800B48627 /* LinkPreviewDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D1A2D2DC62C00B48627 /* LinkPreviewDetailView.swift */; };
|
||||||
|
E2FD1D1D2D2DE31800B48627 /* ItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D1C2D2DE31600B48627 /* ItemType.swift */; };
|
||||||
|
E2FD1D1F2D2E9CC200B48627 /* ItemId.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D1E2D2E9CBE00B48627 /* ItemId.swift */; };
|
||||||
|
E2FD1D212D2EB22900B48627 /* ModelLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D202D2EB22700B48627 /* ModelLoader.swift */; };
|
||||||
|
E2FD1D232D2EB27000B48627 /* LoadingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D222D2EB26C00B48627 /* LoadingResult.swift */; };
|
||||||
|
E2FD1D252D2EBA8000B48627 /* TagOverview.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D242D2EBA7C00B48627 /* TagOverview.swift */; };
|
||||||
E2FE0EE62D15A0B5002963B7 /* GenerationResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */; };
|
E2FE0EE62D15A0B5002963B7 /* GenerationResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */; };
|
||||||
E2FE0EE82D16D4A3002963B7 /* ConvertThrowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */; };
|
E2FE0EE82D16D4A3002963B7 /* ConvertThrowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */; };
|
||||||
E2FE0EEC2D1C1253002963B7 /* MultiFileSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */; };
|
E2FE0EEC2D1C1253002963B7 /* MultiFileSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */; };
|
||||||
@ -196,8 +192,6 @@
|
|||||||
E2FE0EF82D1D8110002963B7 /* IconCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EF72D1D810C002963B7 /* IconCommand.swift */; };
|
E2FE0EF82D1D8110002963B7 /* IconCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EF72D1D810C002963B7 /* IconCommand.swift */; };
|
||||||
E2FE0EFA2D25AFBA002963B7 /* PageHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EF92D25AFB5002963B7 /* PageHeader.swift */; };
|
E2FE0EFA2D25AFBA002963B7 /* PageHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EF92D25AFB5002963B7 /* PageHeader.swift */; };
|
||||||
E2FE0EFC2D266D22002963B7 /* NavigationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EFB2D266D18002963B7 /* NavigationSettings.swift */; };
|
E2FE0EFC2D266D22002963B7 /* NavigationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EFB2D266D18002963B7 /* NavigationSettings.swift */; };
|
||||||
E2FE0EFE2D266DA5002963B7 /* NavigationSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EFD2D266DA1002963B7 /* NavigationSettingsFile.swift */; };
|
|
||||||
E2FE0F002D266E0A002963B7 /* LocalizedNavigationSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EFF2D266E0A002963B7 /* LocalizedNavigationSettingsFile.swift */; };
|
|
||||||
E2FE0F022D266FCB002963B7 /* LocalizedNavigationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F012D266FCB002963B7 /* LocalizedNavigationSettings.swift */; };
|
E2FE0F022D266FCB002963B7 /* LocalizedNavigationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F012D266FCB002963B7 /* LocalizedNavigationSettings.swift */; };
|
||||||
E2FE0F042D267206002963B7 /* LocalizedNavigationBarSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F032D2671FC002963B7 /* LocalizedNavigationBarSettingsView.swift */; };
|
E2FE0F042D267206002963B7 /* LocalizedNavigationBarSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F032D2671FC002963B7 /* LocalizedNavigationBarSettingsView.swift */; };
|
||||||
E2FE0F062D267350002963B7 /* TextFieldPropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F052D26734E002963B7 /* TextFieldPropertyView.swift */; };
|
E2FE0F062D267350002963B7 /* TextFieldPropertyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F052D26734E002963B7 /* TextFieldPropertyView.swift */; };
|
||||||
@ -209,7 +203,6 @@
|
|||||||
E2FE0F152D26918F002963B7 /* HtmlCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F142D269188002963B7 /* HtmlCommand.swift */; };
|
E2FE0F152D26918F002963B7 /* HtmlCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F142D269188002963B7 /* HtmlCommand.swift */; };
|
||||||
E2FE0F172D2698D5002963B7 /* LocalizedPageId.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F162D2698D5002963B7 /* LocalizedPageId.swift */; };
|
E2FE0F172D2698D5002963B7 /* LocalizedPageId.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F162D2698D5002963B7 /* LocalizedPageId.swift */; };
|
||||||
E2FE0F192D2723E3002963B7 /* ImageSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F182D2723E3002963B7 /* ImageSet.swift */; };
|
E2FE0F192D2723E3002963B7 /* ImageSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F182D2723E3002963B7 /* ImageSet.swift */; };
|
||||||
E2FE0F1B2D274FDF002963B7 /* LinkPreviewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1A2D274FDA002963B7 /* LinkPreviewItem.swift */; };
|
|
||||||
E2FE0F1E2D281AE1002963B7 /* TagOverviewGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */; };
|
E2FE0F1E2D281AE1002963B7 /* TagOverviewGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */; };
|
||||||
E2FE0F202D29A70E002963B7 /* Array+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */; };
|
E2FE0F202D29A70E002963B7 /* Array+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */; };
|
||||||
E2FE0F222D2A84A0002963B7 /* VideoCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F212D2A849B002963B7 /* VideoCommand.swift */; };
|
E2FE0F222D2A84A0002963B7 /* VideoCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F212D2A849B002963B7 /* VideoCommand.swift */; };
|
||||||
@ -223,7 +216,6 @@
|
|||||||
E2FE0F362D2B27F9002963B7 /* BlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F352D2B27F6002963B7 /* BlockProcessor.swift */; };
|
E2FE0F362D2B27F9002963B7 /* BlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F352D2B27F6002963B7 /* BlockProcessor.swift */; };
|
||||||
E2FE0F382D2B32F4002963B7 /* SingleFilePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F372D2B32ED002963B7 /* SingleFilePlayer.swift */; };
|
E2FE0F382D2B32F4002963B7 /* SingleFilePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F372D2B32ED002963B7 /* SingleFilePlayer.swift */; };
|
||||||
E2FE0F3A2D2B3E4F002963B7 /* AudioPlayerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F392D2B3E4E002963B7 /* AudioPlayerSettings.swift */; };
|
E2FE0F3A2D2B3E4F002963B7 /* AudioPlayerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F392D2B3E4E002963B7 /* AudioPlayerSettings.swift */; };
|
||||||
E2FE0F3C2D2B3F45002963B7 /* AudioPlayerSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F3B2D2B3F42002963B7 /* AudioPlayerSettingsFile.swift */; };
|
|
||||||
E2FE0F3E2D2B4225002963B7 /* AudioSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F3D2D2B4225002963B7 /* AudioSettingsDetailView.swift */; };
|
E2FE0F3E2D2B4225002963B7 /* AudioSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F3D2D2B4225002963B7 /* AudioSettingsDetailView.swift */; };
|
||||||
E2FE0F402D2B45D3002963B7 /* SwiftBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F3F2D2B45CD002963B7 /* SwiftBlock.swift */; };
|
E2FE0F402D2B45D3002963B7 /* SwiftBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F3F2D2B45CD002963B7 /* SwiftBlock.swift */; };
|
||||||
E2FE0F422D2B4821002963B7 /* OtherCodeBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F412D2B480B002963B7 /* OtherCodeBlock.swift */; };
|
E2FE0F422D2B4821002963B7 /* OtherCodeBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F412D2B480B002963B7 /* OtherCodeBlock.swift */; };
|
||||||
@ -238,13 +230,11 @@
|
|||||||
E2FE0F572D2BCFD4002963B7 /* BlockLineProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F562D2BCFD4002963B7 /* BlockLineProcessor.swift */; };
|
E2FE0F572D2BCFD4002963B7 /* BlockLineProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F562D2BCFD4002963B7 /* BlockLineProcessor.swift */; };
|
||||||
E2FE0F592D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F582D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift */; };
|
E2FE0F592D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F582D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift */; };
|
||||||
E2FE0F5B2D2BCFF2002963B7 /* KeyedBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F5A2D2BCFF2002963B7 /* KeyedBlockProcessor.swift */; };
|
E2FE0F5B2D2BCFF2002963B7 /* KeyedBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F5A2D2BCFF2002963B7 /* KeyedBlockProcessor.swift */; };
|
||||||
E2FE0F5E2D2BE190002963B7 /* FileResourceFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F5D2D2BE18B002963B7 /* FileResourceFile.swift */; };
|
|
||||||
E2FE0F602D2C0422002963B7 /* VideoBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F5F2D2C041E002963B7 /* VideoBlock.swift */; };
|
E2FE0F602D2C0422002963B7 /* VideoBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F5F2D2C041E002963B7 /* VideoBlock.swift */; };
|
||||||
E2FE0F622D2C0D8D002963B7 /* VersionedVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F612D2C0D8D002963B7 /* VersionedVideo.swift */; };
|
E2FE0F622D2C0D8D002963B7 /* VersionedVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F612D2C0D8D002963B7 /* VersionedVideo.swift */; };
|
||||||
E2FE0F642D2C2F4D002963B7 /* ButtonBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F632D2C2F46002963B7 /* ButtonBlock.swift */; };
|
E2FE0F642D2C2F4D002963B7 /* ButtonBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F632D2C2F46002963B7 /* ButtonBlock.swift */; };
|
||||||
E2FE0F662D2C3B3A002963B7 /* LabelsBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F652D2C3B33002963B7 /* LabelsBlock.swift */; };
|
E2FE0F662D2C3B3A002963B7 /* LabelsBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F652D2C3B33002963B7 /* LabelsBlock.swift */; };
|
||||||
E2FE0F682D2D2CF6002963B7 /* LocalizedPageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F672D2D2CF0002963B7 /* LocalizedPageSettings.swift */; };
|
E2FE0F682D2D2CF6002963B7 /* LocalizedPageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F672D2D2CF0002963B7 /* LocalizedPageSettings.swift */; };
|
||||||
E2FE0F6A2D2D2D55002963B7 /* LocalizedPageSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F692D2D2D4F002963B7 /* LocalizedPageSettingsFile.swift */; };
|
|
||||||
E2FE0F6C2D2D335E002963B7 /* LocalizedPageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6B2D2D3358002963B7 /* LocalizedPageSettingsView.swift */; };
|
E2FE0F6C2D2D335E002963B7 /* LocalizedPageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6B2D2D3358002963B7 /* LocalizedPageSettingsView.swift */; };
|
||||||
E2FE0F6E2D2D3689002963B7 /* LocalizedAudioPlayerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6D2D2D3685002963B7 /* LocalizedAudioPlayerSettings.swift */; };
|
E2FE0F6E2D2D3689002963B7 /* LocalizedAudioPlayerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6D2D2D3685002963B7 /* LocalizedAudioPlayerSettings.swift */; };
|
||||||
E2FE0F702D2D5235002963B7 /* DraftIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6F2D2D5231002963B7 /* DraftIndicator.swift */; };
|
E2FE0F702D2D5235002963B7 /* DraftIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6F2D2D5231002963B7 /* DraftIndicator.swift */; };
|
||||||
@ -260,21 +250,18 @@
|
|||||||
E218502A2CF790AC0090B18B /* PostContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostContentView.swift; sourceTree = "<group>"; };
|
E218502A2CF790AC0090B18B /* PostContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostContentView.swift; sourceTree = "<group>"; };
|
||||||
E218502C2CF791440090B18B /* PostImagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostImagesView.swift; sourceTree = "<group>"; };
|
E218502C2CF791440090B18B /* PostImagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostImagesView.swift; sourceTree = "<group>"; };
|
||||||
E21850322CFAFA200090B18B /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
|
E21850322CFAFA200090B18B /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
|
||||||
E21850342CFAFA570090B18B /* SettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFile.swift; sourceTree = "<group>"; };
|
|
||||||
E21850362CFCA5580090B18B /* LocalizedPostSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPostSettings.swift; sourceTree = "<group>"; };
|
E21850362CFCA5580090B18B /* LocalizedPostSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPostSettings.swift; sourceTree = "<group>"; };
|
||||||
E21850382CFCA6BA0090B18B /* WebsiteData+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebsiteData+Mock.swift"; sourceTree = "<group>"; };
|
|
||||||
E218503C2CFCFD8C0090B18B /* LocalizedPostFeedSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPostFeedSettingsView.swift; sourceTree = "<group>"; };
|
E218503C2CFCFD8C0090B18B /* LocalizedPostFeedSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPostFeedSettingsView.swift; sourceTree = "<group>"; };
|
||||||
E22990142D0E2B74009F8D77 /* ItemSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSelectionView.swift; sourceTree = "<group>"; };
|
E22990142D0E2B74009F8D77 /* ItemSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSelectionView.swift; sourceTree = "<group>"; };
|
||||||
E22990162D0E32F5009F8D77 /* TagOverviewPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewPage.swift; sourceTree = "<group>"; };
|
E22990162D0E32F5009F8D77 /* TagOverviewPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewPage.swift; sourceTree = "<group>"; };
|
||||||
E22990182D0E3546009F8D77 /* ItemType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemType.swift; sourceTree = "<group>"; };
|
E22990182D0E3546009F8D77 /* ItemReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemReference.swift; sourceTree = "<group>"; };
|
||||||
E229901D2D0E4362009F8D77 /* LocalizedItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedItem.swift; sourceTree = "<group>"; };
|
E229901D2D0E4362009F8D77 /* LocalizedItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedItem.swift; sourceTree = "<group>"; };
|
||||||
E229901F2D0ECBD4009F8D77 /* TagOverviewDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewDetailView.swift; sourceTree = "<group>"; };
|
E229901F2D0ECBD4009F8D77 /* TagOverviewDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewDetailView.swift; sourceTree = "<group>"; };
|
||||||
E22990212D0ED129009F8D77 /* TagOverviewFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewFile.swift; sourceTree = "<group>"; };
|
|
||||||
E22990232D0EDBD0009F8D77 /* HeaderElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderElement.swift; sourceTree = "<group>"; };
|
E22990232D0EDBD0009F8D77 /* HeaderElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderElement.swift; sourceTree = "<group>"; };
|
||||||
E22990252D0F5822009F8D77 /* FilePropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePropertyView.swift; sourceTree = "<group>"; };
|
E22990252D0F5822009F8D77 /* FilePropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePropertyView.swift; sourceTree = "<group>"; };
|
||||||
E22990272D0F5967009F8D77 /* IntegerPropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerPropertyView.swift; sourceTree = "<group>"; };
|
E22990272D0F5967009F8D77 /* IntegerPropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerPropertyView.swift; sourceTree = "<group>"; };
|
||||||
E22990292D0F5A10009F8D77 /* DetailTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailTitle.swift; sourceTree = "<group>"; };
|
E22990292D0F5A10009F8D77 /* DetailTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailTitle.swift; sourceTree = "<group>"; };
|
||||||
E229902B2D0F6FC0009F8D77 /* ItemId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemId.swift; sourceTree = "<group>"; };
|
E229902B2D0F6FC0009F8D77 /* LocalizedItemId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedItemId.swift; sourceTree = "<group>"; };
|
||||||
E229902D2D0F7278009F8D77 /* IdPropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdPropertyView.swift; sourceTree = "<group>"; };
|
E229902D2D0F7278009F8D77 /* IdPropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdPropertyView.swift; sourceTree = "<group>"; };
|
||||||
E229902F2D0F75CF009F8D77 /* BoolPropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoolPropertyView.swift; sourceTree = "<group>"; };
|
E229902F2D0F75CF009F8D77 /* BoolPropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoolPropertyView.swift; sourceTree = "<group>"; };
|
||||||
E22990312D0F7678009F8D77 /* DatePropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePropertyView.swift; sourceTree = "<group>"; };
|
E22990312D0F7678009F8D77 /* DatePropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePropertyView.swift; sourceTree = "<group>"; };
|
||||||
@ -297,7 +284,6 @@
|
|||||||
E25DA5082CFD964E00AEF16D /* TagContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagContentView.swift; sourceTree = "<group>"; };
|
E25DA5082CFD964E00AEF16D /* TagContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagContentView.swift; sourceTree = "<group>"; };
|
||||||
E25DA50A2CFD988100AEF16D /* PageTagAssignmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageTagAssignmentView.swift; sourceTree = "<group>"; };
|
E25DA50A2CFD988100AEF16D /* PageTagAssignmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageTagAssignmentView.swift; sourceTree = "<group>"; };
|
||||||
E25DA50C2CFD9B9C00AEF16D /* PostTagAssignmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTagAssignmentView.swift; sourceTree = "<group>"; };
|
E25DA50C2CFD9B9C00AEF16D /* PostTagAssignmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTagAssignmentView.swift; sourceTree = "<group>"; };
|
||||||
E25DA5142CFF00B900AEF16D /* Content+Load.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Load.swift"; sourceTree = "<group>"; };
|
|
||||||
E25DA5162CFF00F200AEF16D /* Content+Save.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Save.swift"; sourceTree = "<group>"; };
|
E25DA5162CFF00F200AEF16D /* Content+Save.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Save.swift"; sourceTree = "<group>"; };
|
||||||
E25DA5182CFF035200AEF16D /* Array+Split.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Split.swift"; sourceTree = "<group>"; };
|
E25DA5182CFF035200AEF16D /* Array+Split.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Split.swift"; sourceTree = "<group>"; };
|
||||||
E25DA51A2CFF08AF00AEF16D /* PostFeedPageNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostFeedPageNavigation.swift; sourceTree = "<group>"; };
|
E25DA51A2CFF08AF00AEF16D /* PostFeedPageNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostFeedPageNavigation.swift; sourceTree = "<group>"; };
|
||||||
@ -306,8 +292,6 @@
|
|||||||
E25DA5222CFF6C2600AEF16D /* ImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGenerator.swift; sourceTree = "<group>"; };
|
E25DA5222CFF6C2600AEF16D /* ImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGenerator.swift; sourceTree = "<group>"; };
|
||||||
E25DA5242CFF73A600AEF16D /* NSSize+Scaling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSSize+Scaling.swift"; sourceTree = "<group>"; };
|
E25DA5242CFF73A600AEF16D /* NSSize+Scaling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSSize+Scaling.swift"; sourceTree = "<group>"; };
|
||||||
E25DA5262CFF745200AEF16D /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; };
|
E25DA5262CFF745200AEF16D /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
E25DA5352D0041E200AEF16D /* PostSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostSettingsFile.swift; sourceTree = "<group>"; };
|
|
||||||
E25DA5372D00420D00AEF16D /* LocalizedPostSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPostSettingsFile.swift; sourceTree = "<group>"; };
|
|
||||||
E25DA5402D00446700AEF16D /* PostSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostSettings.swift; sourceTree = "<group>"; };
|
E25DA5402D00446700AEF16D /* PostSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostSettings.swift; sourceTree = "<group>"; };
|
||||||
E25DA5442D00952D00AEF16D /* SettingsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSection.swift; sourceTree = "<group>"; };
|
E25DA5442D00952D00AEF16D /* SettingsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSection.swift; sourceTree = "<group>"; };
|
||||||
E25DA56C2D00EBC900AEF16D /* NavigationBarSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarSettingsView.swift; sourceTree = "<group>"; };
|
E25DA56C2D00EBC900AEF16D /* NavigationBarSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarSettingsView.swift; sourceTree = "<group>"; };
|
||||||
@ -322,7 +306,6 @@
|
|||||||
E25DA58A2D020C9200AEF16D /* PageImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageImage.swift; sourceTree = "<group>"; };
|
E25DA58A2D020C9200AEF16D /* PageImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageImage.swift; sourceTree = "<group>"; };
|
||||||
E25DA58E2D02368A00AEF16D /* PageSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageSettings.swift; sourceTree = "<group>"; };
|
E25DA58E2D02368A00AEF16D /* PageSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageSettings.swift; sourceTree = "<group>"; };
|
||||||
E25DA5902D023A7E00AEF16D /* IntegerField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerField.swift; sourceTree = "<group>"; };
|
E25DA5902D023A7E00AEF16D /* IntegerField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerField.swift; sourceTree = "<group>"; };
|
||||||
E25DA5922D023B3600AEF16D /* PageSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageSettingsFile.swift; sourceTree = "<group>"; };
|
|
||||||
E25DA5942D023BCC00AEF16D /* PageSettingsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageSettingsDetailView.swift; sourceTree = "<group>"; };
|
E25DA5942D023BCC00AEF16D /* PageSettingsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageSettingsDetailView.swift; sourceTree = "<group>"; };
|
||||||
E25DA5982D02401A00AEF16D /* PageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageGenerator.swift; sourceTree = "<group>"; };
|
E25DA5982D02401A00AEF16D /* PageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageGenerator.swift; sourceTree = "<group>"; };
|
||||||
E25DA59A2D024A2900AEF16D /* DateItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateItem.swift; sourceTree = "<group>"; };
|
E25DA59A2D024A2900AEF16D /* DateItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateItem.swift; sourceTree = "<group>"; };
|
||||||
@ -331,7 +314,6 @@
|
|||||||
E29D31232D0366820051B7F4 /* TagList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagList.swift; sourceTree = "<group>"; };
|
E29D31232D0366820051B7F4 /* TagList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagList.swift; sourceTree = "<group>"; };
|
||||||
E29D31252D0370A50051B7F4 /* VideoCommand+Option.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VideoCommand+Option.swift"; sourceTree = "<group>"; };
|
E29D31252D0370A50051B7F4 /* VideoCommand+Option.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VideoCommand+Option.swift"; sourceTree = "<group>"; };
|
||||||
E29D31272D0371870051B7F4 /* ContentPageVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPageVideo.swift; sourceTree = "<group>"; };
|
E29D31272D0371870051B7F4 /* ContentPageVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPageVideo.swift; sourceTree = "<group>"; };
|
||||||
E29D31292D039B050051B7F4 /* FileDescriptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDescriptions.swift; sourceTree = "<group>"; };
|
|
||||||
E29D312B2D039DB30051B7F4 /* PageDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageDetailView.swift; sourceTree = "<group>"; };
|
E29D312B2D039DB30051B7F4 /* PageDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageDetailView.swift; sourceTree = "<group>"; };
|
||||||
E29D312D2D03A0CF0051B7F4 /* LocalizedPageDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageDetailView.swift; sourceTree = "<group>"; };
|
E29D312D2D03A0CF0051B7F4 /* LocalizedPageDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageDetailView.swift; sourceTree = "<group>"; };
|
||||||
E29D312F2D03A2BD0051B7F4 /* DescriptionField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionField.swift; sourceTree = "<group>"; };
|
E29D312F2D03A2BD0051B7F4 /* DescriptionField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionField.swift; sourceTree = "<group>"; };
|
||||||
@ -369,7 +351,6 @@
|
|||||||
E29D318F2D0B34870051B7F4 /* GenerationAnomaly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerationAnomaly.swift; sourceTree = "<group>"; };
|
E29D318F2D0B34870051B7F4 /* GenerationAnomaly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerationAnomaly.swift; sourceTree = "<group>"; };
|
||||||
E29D31932D0B7D250051B7F4 /* SimpleImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleImage.swift; sourceTree = "<group>"; };
|
E29D31932D0B7D250051B7F4 /* SimpleImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleImage.swift; sourceTree = "<group>"; };
|
||||||
E29D31952D0C18690051B7F4 /* PathSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathSettings.swift; sourceTree = "<group>"; };
|
E29D31952D0C18690051B7F4 /* PathSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathSettings.swift; sourceTree = "<group>"; };
|
||||||
E29D31972D0C19300051B7F4 /* PathSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathSettingsFile.swift; sourceTree = "<group>"; };
|
|
||||||
E29D319A2D0C452B0051B7F4 /* PageIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageIssue.swift; sourceTree = "<group>"; };
|
E29D319A2D0C452B0051B7F4 /* PageIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageIssue.swift; sourceTree = "<group>"; };
|
||||||
E29D319C2D0C45B60051B7F4 /* PageIssueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageIssueView.swift; sourceTree = "<group>"; };
|
E29D319C2D0C45B60051B7F4 /* PageIssueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageIssueView.swift; sourceTree = "<group>"; };
|
||||||
E29D319E2D0C46290051B7F4 /* PageIssueChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageIssueChecker.swift; sourceTree = "<group>"; };
|
E29D319E2D0C46290051B7F4 /* PageIssueChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageIssueChecker.swift; sourceTree = "<group>"; };
|
||||||
@ -401,9 +382,6 @@
|
|||||||
E2A21C472CBAF8830060935B /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
|
E2A21C472CBAF8830060935B /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
E2A21C502CBBD53C0060935B /* FileResource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileResource.swift; sourceTree = "<group>"; };
|
E2A21C502CBBD53C0060935B /* FileResource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileResource.swift; sourceTree = "<group>"; };
|
||||||
E2A37D0D2CE527040000979F /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
|
E2A37D0D2CE527040000979F /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
|
||||||
E2A37D102CE537670000979F /* PageFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageFile.swift; sourceTree = "<group>"; };
|
|
||||||
E2A37D142CE68BEA0000979F /* PostFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostFile.swift; sourceTree = "<group>"; };
|
|
||||||
E2A37D162CE73F170000979F /* TagFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagFile.swift; sourceTree = "<group>"; };
|
|
||||||
E2A37D182CEA36A40000979F /* LocalizedTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedTag.swift; sourceTree = "<group>"; };
|
E2A37D182CEA36A40000979F /* LocalizedTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedTag.swift; sourceTree = "<group>"; };
|
||||||
E2A37D1A2CEA45530000979F /* Tag+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Tag+Mock.swift"; sourceTree = "<group>"; };
|
E2A37D1A2CEA45530000979F /* Tag+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Tag+Mock.swift"; sourceTree = "<group>"; };
|
||||||
E2A37D1C2CEA922A0000979F /* LocalizedPost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPost.swift; sourceTree = "<group>"; };
|
E2A37D1C2CEA922A0000979F /* LocalizedPost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPost.swift; sourceTree = "<group>"; };
|
||||||
@ -426,6 +404,14 @@
|
|||||||
E2DD047D2C276F32003BFF1F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
E2DD047D2C276F32003BFF1F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
E2E06DFA2CA4A6570019C2AF /* Content.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Content.swift; sourceTree = "<group>"; };
|
E2E06DFA2CA4A6570019C2AF /* Content.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Content.swift; sourceTree = "<group>"; };
|
||||||
E2E06DFF2CA4A8EB0019C2AF /* Page+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Page+Mock.swift"; sourceTree = "<group>"; };
|
E2E06DFF2CA4A8EB0019C2AF /* Page+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Page+Mock.swift"; sourceTree = "<group>"; };
|
||||||
|
E2FD1D0C2D2DBBA100B48627 /* LinkPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreview.swift; sourceTree = "<group>"; };
|
||||||
|
E2FD1D182D2DC4F500B48627 /* LoadingContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingContext.swift; sourceTree = "<group>"; };
|
||||||
|
E2FD1D1A2D2DC62C00B48627 /* LinkPreviewDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewDetailView.swift; sourceTree = "<group>"; };
|
||||||
|
E2FD1D1C2D2DE31600B48627 /* ItemType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemType.swift; sourceTree = "<group>"; };
|
||||||
|
E2FD1D1E2D2E9CBE00B48627 /* ItemId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemId.swift; sourceTree = "<group>"; };
|
||||||
|
E2FD1D202D2EB22700B48627 /* ModelLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelLoader.swift; sourceTree = "<group>"; };
|
||||||
|
E2FD1D222D2EB26C00B48627 /* LoadingResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingResult.swift; sourceTree = "<group>"; };
|
||||||
|
E2FD1D242D2EBA7C00B48627 /* TagOverview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverview.swift; sourceTree = "<group>"; };
|
||||||
E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerationResults.swift; sourceTree = "<group>"; };
|
E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerationResults.swift; sourceTree = "<group>"; };
|
||||||
E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConvertThrowing.swift; sourceTree = "<group>"; };
|
E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConvertThrowing.swift; sourceTree = "<group>"; };
|
||||||
E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiFileSelectionView.swift; sourceTree = "<group>"; };
|
E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiFileSelectionView.swift; sourceTree = "<group>"; };
|
||||||
@ -435,8 +421,6 @@
|
|||||||
E2FE0EF72D1D810C002963B7 /* IconCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconCommand.swift; sourceTree = "<group>"; };
|
E2FE0EF72D1D810C002963B7 /* IconCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconCommand.swift; sourceTree = "<group>"; };
|
||||||
E2FE0EF92D25AFB5002963B7 /* PageHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHeader.swift; sourceTree = "<group>"; };
|
E2FE0EF92D25AFB5002963B7 /* PageHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHeader.swift; sourceTree = "<group>"; };
|
||||||
E2FE0EFB2D266D18002963B7 /* NavigationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSettings.swift; sourceTree = "<group>"; };
|
E2FE0EFB2D266D18002963B7 /* NavigationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSettings.swift; sourceTree = "<group>"; };
|
||||||
E2FE0EFD2D266DA1002963B7 /* NavigationSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSettingsFile.swift; sourceTree = "<group>"; };
|
|
||||||
E2FE0EFF2D266E0A002963B7 /* LocalizedNavigationSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedNavigationSettingsFile.swift; sourceTree = "<group>"; };
|
|
||||||
E2FE0F012D266FCB002963B7 /* LocalizedNavigationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedNavigationSettings.swift; sourceTree = "<group>"; };
|
E2FE0F012D266FCB002963B7 /* LocalizedNavigationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedNavigationSettings.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F032D2671FC002963B7 /* LocalizedNavigationBarSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedNavigationBarSettingsView.swift; sourceTree = "<group>"; };
|
E2FE0F032D2671FC002963B7 /* LocalizedNavigationBarSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedNavigationBarSettingsView.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F052D26734E002963B7 /* TextFieldPropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldPropertyView.swift; sourceTree = "<group>"; };
|
E2FE0F052D26734E002963B7 /* TextFieldPropertyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldPropertyView.swift; sourceTree = "<group>"; };
|
||||||
@ -448,7 +432,6 @@
|
|||||||
E2FE0F142D269188002963B7 /* HtmlCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HtmlCommand.swift; sourceTree = "<group>"; };
|
E2FE0F142D269188002963B7 /* HtmlCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HtmlCommand.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F162D2698D5002963B7 /* LocalizedPageId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageId.swift; sourceTree = "<group>"; };
|
E2FE0F162D2698D5002963B7 /* LocalizedPageId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageId.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F182D2723E3002963B7 /* ImageSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSet.swift; sourceTree = "<group>"; };
|
E2FE0F182D2723E3002963B7 /* ImageSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSet.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F1A2D274FDA002963B7 /* LinkPreviewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewItem.swift; sourceTree = "<group>"; };
|
|
||||||
E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewGenerator.swift; sourceTree = "<group>"; };
|
E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewGenerator.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Remove.swift"; sourceTree = "<group>"; };
|
E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Remove.swift"; sourceTree = "<group>"; };
|
||||||
E2FE0F212D2A849B002963B7 /* VideoCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoCommand.swift; sourceTree = "<group>"; };
|
E2FE0F212D2A849B002963B7 /* VideoCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoCommand.swift; sourceTree = "<group>"; };
|
||||||
@ -462,7 +445,6 @@
|
|||||||
E2FE0F352D2B27F6002963B7 /* BlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockProcessor.swift; sourceTree = "<group>"; };
|
E2FE0F352D2B27F6002963B7 /* BlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockProcessor.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F372D2B32ED002963B7 /* SingleFilePlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleFilePlayer.swift; sourceTree = "<group>"; };
|
E2FE0F372D2B32ED002963B7 /* SingleFilePlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleFilePlayer.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F392D2B3E4E002963B7 /* AudioPlayerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerSettings.swift; sourceTree = "<group>"; };
|
E2FE0F392D2B3E4E002963B7 /* AudioPlayerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerSettings.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F3B2D2B3F42002963B7 /* AudioPlayerSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerSettingsFile.swift; sourceTree = "<group>"; };
|
|
||||||
E2FE0F3D2D2B4225002963B7 /* AudioSettingsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSettingsDetailView.swift; sourceTree = "<group>"; };
|
E2FE0F3D2D2B4225002963B7 /* AudioSettingsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSettingsDetailView.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F3F2D2B45CD002963B7 /* SwiftBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftBlock.swift; sourceTree = "<group>"; };
|
E2FE0F3F2D2B45CD002963B7 /* SwiftBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftBlock.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F412D2B480B002963B7 /* OtherCodeBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherCodeBlock.swift; sourceTree = "<group>"; };
|
E2FE0F412D2B480B002963B7 /* OtherCodeBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherCodeBlock.swift; sourceTree = "<group>"; };
|
||||||
@ -477,13 +459,11 @@
|
|||||||
E2FE0F562D2BCFD4002963B7 /* BlockLineProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockLineProcessor.swift; sourceTree = "<group>"; };
|
E2FE0F562D2BCFD4002963B7 /* BlockLineProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockLineProcessor.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F582D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedKeyBlockProcessor.swift; sourceTree = "<group>"; };
|
E2FE0F582D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedKeyBlockProcessor.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F5A2D2BCFF2002963B7 /* KeyedBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedBlockProcessor.swift; sourceTree = "<group>"; };
|
E2FE0F5A2D2BCFF2002963B7 /* KeyedBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedBlockProcessor.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F5D2D2BE18B002963B7 /* FileResourceFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileResourceFile.swift; sourceTree = "<group>"; };
|
|
||||||
E2FE0F5F2D2C041E002963B7 /* VideoBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoBlock.swift; sourceTree = "<group>"; };
|
E2FE0F5F2D2C041E002963B7 /* VideoBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoBlock.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F612D2C0D8D002963B7 /* VersionedVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionedVideo.swift; sourceTree = "<group>"; };
|
E2FE0F612D2C0D8D002963B7 /* VersionedVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionedVideo.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F632D2C2F46002963B7 /* ButtonBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonBlock.swift; sourceTree = "<group>"; };
|
E2FE0F632D2C2F46002963B7 /* ButtonBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonBlock.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F652D2C3B33002963B7 /* LabelsBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelsBlock.swift; sourceTree = "<group>"; };
|
E2FE0F652D2C3B33002963B7 /* LabelsBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelsBlock.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F672D2D2CF0002963B7 /* LocalizedPageSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageSettings.swift; sourceTree = "<group>"; };
|
E2FE0F672D2D2CF0002963B7 /* LocalizedPageSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageSettings.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F692D2D2D4F002963B7 /* LocalizedPageSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageSettingsFile.swift; sourceTree = "<group>"; };
|
|
||||||
E2FE0F6B2D2D3358002963B7 /* LocalizedPageSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageSettingsView.swift; sourceTree = "<group>"; };
|
E2FE0F6B2D2D3358002963B7 /* LocalizedPageSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageSettingsView.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F6D2D2D3685002963B7 /* LocalizedAudioPlayerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedAudioPlayerSettings.swift; sourceTree = "<group>"; };
|
E2FE0F6D2D2D3685002963B7 /* LocalizedAudioPlayerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedAudioPlayerSettings.swift; sourceTree = "<group>"; };
|
||||||
E2FE0F6F2D2D5231002963B7 /* DraftIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraftIndicator.swift; sourceTree = "<group>"; };
|
E2FE0F6F2D2D5231002963B7 /* DraftIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraftIndicator.swift; sourceTree = "<group>"; };
|
||||||
@ -510,47 +490,18 @@
|
|||||||
E229901A2D0E3F09009F8D77 /* Item */ = {
|
E229901A2D0E3F09009F8D77 /* Item */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E2FE0F1A2D274FDA002963B7 /* LinkPreviewItem.swift */,
|
E2FD1D1E2D2E9CBE00B48627 /* ItemId.swift */,
|
||||||
|
E2FD1D1C2D2DE31600B48627 /* ItemType.swift */,
|
||||||
E2FE0F162D2698D5002963B7 /* LocalizedPageId.swift */,
|
E2FE0F162D2698D5002963B7 /* LocalizedPageId.swift */,
|
||||||
E229902B2D0F6FC0009F8D77 /* ItemId.swift */,
|
E229902B2D0F6FC0009F8D77 /* LocalizedItemId.swift */,
|
||||||
E229901D2D0E4362009F8D77 /* LocalizedItem.swift */,
|
E229901D2D0E4362009F8D77 /* LocalizedItem.swift */,
|
||||||
E29D31A22D0CC98B0051B7F4 /* Item.swift */,
|
E29D31A22D0CC98B0051B7F4 /* Item.swift */,
|
||||||
E22990182D0E3546009F8D77 /* ItemType.swift */,
|
E22990182D0E3546009F8D77 /* ItemReference.swift */,
|
||||||
E22990162D0E32F5009F8D77 /* TagOverviewPage.swift */,
|
E22990162D0E32F5009F8D77 /* TagOverviewPage.swift */,
|
||||||
);
|
);
|
||||||
path = Item;
|
path = Item;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
E25DA5112CFF001900AEF16D /* Model */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E29D31292D039B050051B7F4 /* FileDescriptions.swift */,
|
|
||||||
E25DA5322D0041C400AEF16D /* Settings */,
|
|
||||||
E2A37D102CE537670000979F /* PageFile.swift */,
|
|
||||||
E2A37D142CE68BEA0000979F /* PostFile.swift */,
|
|
||||||
E2A37D162CE73F170000979F /* TagFile.swift */,
|
|
||||||
E2FE0F5D2D2BE18B002963B7 /* FileResourceFile.swift */,
|
|
||||||
);
|
|
||||||
path = Model;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E25DA5322D0041C400AEF16D /* Settings */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E2FE0F3B2D2B3F42002963B7 /* AudioPlayerSettingsFile.swift */,
|
|
||||||
E2FE0EFF2D266E0A002963B7 /* LocalizedNavigationSettingsFile.swift */,
|
|
||||||
E2FE0EFD2D266DA1002963B7 /* NavigationSettingsFile.swift */,
|
|
||||||
E29D31972D0C19300051B7F4 /* PathSettingsFile.swift */,
|
|
||||||
E25DA5372D00420D00AEF16D /* LocalizedPostSettingsFile.swift */,
|
|
||||||
E25DA5352D0041E200AEF16D /* PostSettingsFile.swift */,
|
|
||||||
E25DA5922D023B3600AEF16D /* PageSettingsFile.swift */,
|
|
||||||
E2FE0F692D2D2D4F002963B7 /* LocalizedPageSettingsFile.swift */,
|
|
||||||
E21850342CFAFA570090B18B /* SettingsFile.swift */,
|
|
||||||
E22990212D0ED129009F8D77 /* TagOverviewFile.swift */,
|
|
||||||
);
|
|
||||||
path = Settings;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E25DA53B2D0042EA00AEF16D /* Settings */ = {
|
E25DA53B2D0042EA00AEF16D /* Settings */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -752,7 +703,6 @@
|
|||||||
E22990492D10BB90009F8D77 /* SecurityScopeBookmark.swift */,
|
E22990492D10BB90009F8D77 /* SecurityScopeBookmark.swift */,
|
||||||
E22990472D10B7B7009F8D77 /* StorageAccessError.swift */,
|
E22990472D10B7B7009F8D77 /* StorageAccessError.swift */,
|
||||||
E22990452D10B7A6009F8D77 /* SecurityScopeStatus.swift */,
|
E22990452D10B7A6009F8D77 /* SecurityScopeStatus.swift */,
|
||||||
E25DA5112CFF001900AEF16D /* Model */,
|
|
||||||
E2A37D0D2CE527040000979F /* Storage.swift */,
|
E2A37D0D2CE527040000979F /* Storage.swift */,
|
||||||
);
|
);
|
||||||
path = Storage;
|
path = Storage;
|
||||||
@ -775,13 +725,14 @@
|
|||||||
E2B85F392C428F020047CD0C /* Model */ = {
|
E2B85F392C428F020047CD0C /* Model */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E2FD1D262D2EBBA300B48627 /* Loading */,
|
||||||
|
E2FD1D0C2D2DBBA100B48627 /* LinkPreview.swift */,
|
||||||
E229901A2D0E3F09009F8D77 /* Item */,
|
E229901A2D0E3F09009F8D77 /* Item */,
|
||||||
E25DA53B2D0042EA00AEF16D /* Settings */,
|
E25DA53B2D0042EA00AEF16D /* Settings */,
|
||||||
E2E06DFA2CA4A6570019C2AF /* Content.swift */,
|
E2E06DFA2CA4A6570019C2AF /* Content.swift */,
|
||||||
E29D31A02D0C75C50051B7F4 /* Content+Validation.swift */,
|
E29D31A02D0C75C50051B7F4 /* Content+Validation.swift */,
|
||||||
E25DA5882D01CBCE00AEF16D /* Content+Generation.swift */,
|
E25DA5882D01CBCE00AEF16D /* Content+Generation.swift */,
|
||||||
E25DA5162CFF00F200AEF16D /* Content+Save.swift */,
|
E25DA5162CFF00F200AEF16D /* Content+Save.swift */,
|
||||||
E25DA5142CFF00B900AEF16D /* Content+Load.swift */,
|
|
||||||
E24252092C52C9260029FF16 /* ContentLanguage.swift */,
|
E24252092C52C9260029FF16 /* ContentLanguage.swift */,
|
||||||
E25DA59A2D024A2900AEF16D /* DateItem.swift */,
|
E25DA59A2D024A2900AEF16D /* DateItem.swift */,
|
||||||
E21850162CEE55FB0090B18B /* FileType.swift */,
|
E21850162CEE55FB0090B18B /* FileType.swift */,
|
||||||
@ -793,6 +744,7 @@
|
|||||||
E2A9CB7D2C7BCF2A005C89CC /* Page.swift */,
|
E2A9CB7D2C7BCF2A005C89CC /* Page.swift */,
|
||||||
E25A0B882CE4021400F33674 /* LocalizedPage.swift */,
|
E25A0B882CE4021400F33674 /* LocalizedPage.swift */,
|
||||||
E29D31C22D0DBEF00051B7F4 /* Song.swift */,
|
E29D31C22D0DBEF00051B7F4 /* Song.swift */,
|
||||||
|
E2FD1D242D2EBA7C00B48627 /* TagOverview.swift */,
|
||||||
);
|
);
|
||||||
path = Model;
|
path = Model;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -816,6 +768,7 @@
|
|||||||
E2B85F462C42C7CA0047CD0C /* Views */ = {
|
E2B85F462C42C7CA0047CD0C /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E2FD1D1A2D2DC62C00B48627 /* LinkPreviewDetailView.swift */,
|
||||||
E22990142D0E2B74009F8D77 /* ItemSelectionView.swift */,
|
E22990142D0E2B74009F8D77 /* ItemSelectionView.swift */,
|
||||||
E2A21C372CB9A4F10060935B /* Generic */,
|
E2A21C372CB9A4F10060935B /* Generic */,
|
||||||
E2B85F4B2C4B8B7F0047CD0C /* Posts */,
|
E2B85F4B2C4B8B7F0047CD0C /* Posts */,
|
||||||
@ -899,7 +852,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E25DA5762D018B9500AEF16D /* File+Mock.swift */,
|
E25DA5762D018B9500AEF16D /* File+Mock.swift */,
|
||||||
E21850382CFCA6BA0090B18B /* WebsiteData+Mock.swift */,
|
|
||||||
E218500A2CEE02FA0090B18B /* Content+Mock.swift */,
|
E218500A2CEE02FA0090B18B /* Content+Mock.swift */,
|
||||||
E2A37D1A2CEA45530000979F /* Tag+Mock.swift */,
|
E2A37D1A2CEA45530000979F /* Tag+Mock.swift */,
|
||||||
E2A21C1F2CB28ED20060935B /* MockImage.swift */,
|
E2A21C1F2CB28ED20060935B /* MockImage.swift */,
|
||||||
@ -910,6 +862,16 @@
|
|||||||
path = "Preview Content";
|
path = "Preview Content";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
E2FD1D262D2EBBA300B48627 /* Loading */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
E2FD1D202D2EB22700B48627 /* ModelLoader.swift */,
|
||||||
|
E2FD1D222D2EB26C00B48627 /* LoadingResult.swift */,
|
||||||
|
E2FD1D182D2DC4F500B48627 /* LoadingContext.swift */,
|
||||||
|
);
|
||||||
|
path = Loading;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
E2FE0F072D2689DC002963B7 /* Post Lists */ = {
|
E2FE0F072D2689DC002963B7 /* Post Lists */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -1103,9 +1065,10 @@
|
|||||||
files = (
|
files = (
|
||||||
E29D31242D0366860051B7F4 /* TagList.swift in Sources */,
|
E29D31242D0366860051B7F4 /* TagList.swift in Sources */,
|
||||||
E22990282D0F596C009F8D77 /* IntegerPropertyView.swift in Sources */,
|
E22990282D0F596C009F8D77 /* IntegerPropertyView.swift in Sources */,
|
||||||
|
E2FD1D1D2D2DE31800B48627 /* ItemType.swift in Sources */,
|
||||||
E2FE0F482D2BC7D1002963B7 /* MarkdownProcessor.swift in Sources */,
|
E2FE0F482D2BC7D1002963B7 /* MarkdownProcessor.swift in Sources */,
|
||||||
E2FE0EFE2D266DA5002963B7 /* NavigationSettingsFile.swift in Sources */,
|
|
||||||
E2FE0EE62D15A0B5002963B7 /* GenerationResults.swift in Sources */,
|
E2FE0EE62D15A0B5002963B7 /* GenerationResults.swift in Sources */,
|
||||||
|
E2FD1D252D2EBA8000B48627 /* TagOverview.swift in Sources */,
|
||||||
E2FE0F152D26918F002963B7 /* HtmlCommand.swift in Sources */,
|
E2FE0F152D26918F002963B7 /* HtmlCommand.swift in Sources */,
|
||||||
E2FE0F202D29A70E002963B7 /* Array+Remove.swift in Sources */,
|
E2FE0F202D29A70E002963B7 /* Array+Remove.swift in Sources */,
|
||||||
E25DA5772D018B9900AEF16D /* File+Mock.swift in Sources */,
|
E25DA5772D018B9900AEF16D /* File+Mock.swift in Sources */,
|
||||||
@ -1127,7 +1090,6 @@
|
|||||||
E2FE0EF42D1D6D2E002963B7 /* GeneralIcons.swift in Sources */,
|
E2FE0EF42D1D6D2E002963B7 /* GeneralIcons.swift in Sources */,
|
||||||
E25DA5172CFF00F500AEF16D /* Content+Save.swift in Sources */,
|
E25DA5172CFF00F500AEF16D /* Content+Save.swift in Sources */,
|
||||||
E229902E2D0F7280009F8D77 /* IdPropertyView.swift in Sources */,
|
E229902E2D0F7280009F8D77 /* IdPropertyView.swift in Sources */,
|
||||||
E2FE0F3C2D2B3F45002963B7 /* AudioPlayerSettingsFile.swift in Sources */,
|
|
||||||
E2FE0F462D2BC777002963B7 /* MarkdownImageProcessor.swift in Sources */,
|
E2FE0F462D2BC777002963B7 /* MarkdownImageProcessor.swift in Sources */,
|
||||||
E29D31AD2D0DA5360051B7F4 /* AudioPlayerIcons.swift in Sources */,
|
E29D31AD2D0DA5360051B7F4 /* AudioPlayerIcons.swift in Sources */,
|
||||||
E25DA5452D00952E00AEF16D /* SettingsSection.swift in Sources */,
|
E25DA5452D00952E00AEF16D /* SettingsSection.swift in Sources */,
|
||||||
@ -1138,11 +1100,9 @@
|
|||||||
E2FE0F042D267206002963B7 /* LocalizedNavigationBarSettingsView.swift in Sources */,
|
E2FE0F042D267206002963B7 /* LocalizedNavigationBarSettingsView.swift in Sources */,
|
||||||
E2FE0F382D2B32F4002963B7 /* SingleFilePlayer.swift in Sources */,
|
E2FE0F382D2B32F4002963B7 /* SingleFilePlayer.swift in Sources */,
|
||||||
E29D31B12D0DA5510051B7F4 /* ContentIcon.swift in Sources */,
|
E29D31B12D0DA5510051B7F4 /* ContentIcon.swift in Sources */,
|
||||||
E2A37D112CE537800000979F /* PageFile.swift in Sources */,
|
|
||||||
E242520A2C52C9260029FF16 /* ContentLanguage.swift in Sources */,
|
E242520A2C52C9260029FF16 /* ContentLanguage.swift in Sources */,
|
||||||
E2B85F452C429ED60047CD0C /* ImageGallery.swift in Sources */,
|
E2B85F452C429ED60047CD0C /* ImageGallery.swift in Sources */,
|
||||||
E2B85F3B2C428F0E0047CD0C /* Post.swift in Sources */,
|
E2B85F3B2C428F0E0047CD0C /* Post.swift in Sources */,
|
||||||
E2FE0F6A2D2D2D55002963B7 /* LocalizedPageSettingsFile.swift in Sources */,
|
|
||||||
E29D31852D0AE8EE0051B7F4 /* KnownHeaderElement.swift in Sources */,
|
E29D31852D0AE8EE0051B7F4 /* KnownHeaderElement.swift in Sources */,
|
||||||
E25DA51B2CFF08BB00AEF16D /* PostFeedPageNavigation.swift in Sources */,
|
E25DA51B2CFF08BB00AEF16D /* PostFeedPageNavigation.swift in Sources */,
|
||||||
E22990422D107A95009F8D77 /* ImageVersion.swift in Sources */,
|
E22990422D107A95009F8D77 /* ImageVersion.swift in Sources */,
|
||||||
@ -1160,6 +1120,7 @@
|
|||||||
E29D31942D0B7D280051B7F4 /* SimpleImage.swift in Sources */,
|
E29D31942D0B7D280051B7F4 /* SimpleImage.swift in Sources */,
|
||||||
E29D31632D06E95D0051B7F4 /* NavigationIcon.swift in Sources */,
|
E29D31632D06E95D0051B7F4 /* NavigationIcon.swift in Sources */,
|
||||||
E2FE0F362D2B27F9002963B7 /* BlockProcessor.swift in Sources */,
|
E2FE0F362D2B27F9002963B7 /* BlockProcessor.swift in Sources */,
|
||||||
|
E2FD1D1B2D2DC63800B48627 /* LinkPreviewDetailView.swift in Sources */,
|
||||||
E22990462D10B7A7009F8D77 /* SecurityScopeStatus.swift in Sources */,
|
E22990462D10B7A7009F8D77 /* SecurityScopeStatus.swift in Sources */,
|
||||||
E29D31512D06168E0051B7F4 /* PostListView.swift in Sources */,
|
E29D31512D06168E0051B7F4 /* PostListView.swift in Sources */,
|
||||||
E2FE0F4F2D2BCD80002963B7 /* TagLinkCommand.swift in Sources */,
|
E2FE0F4F2D2BCD80002963B7 /* TagLinkCommand.swift in Sources */,
|
||||||
@ -1167,9 +1128,8 @@
|
|||||||
E2A37D0C2CE4036B0000979F /* LocalizedPage.swift in Sources */,
|
E2A37D0C2CE4036B0000979F /* LocalizedPage.swift in Sources */,
|
||||||
E2A21C102CB18B3A0060935B /* FlowHStack.swift in Sources */,
|
E2A21C102CB18B3A0060935B /* FlowHStack.swift in Sources */,
|
||||||
E25DA5992D02401E00AEF16D /* PageGenerator.swift in Sources */,
|
E25DA5992D02401E00AEF16D /* PageGenerator.swift in Sources */,
|
||||||
E25DA5382D00420E00AEF16D /* LocalizedPostSettingsFile.swift in Sources */,
|
|
||||||
E2B85F3D2C4293F80047CD0C /* FeedPageGenerator.swift in Sources */,
|
E2B85F3D2C4293F80047CD0C /* FeedPageGenerator.swift in Sources */,
|
||||||
E229902C2D0F6FC6009F8D77 /* ItemId.swift in Sources */,
|
E229902C2D0F6FC6009F8D77 /* LocalizedItemId.swift in Sources */,
|
||||||
E25DA5952D023BD100AEF16D /* PageSettingsDetailView.swift in Sources */,
|
E25DA5952D023BD100AEF16D /* PageSettingsDetailView.swift in Sources */,
|
||||||
E21850092CEE01C30090B18B /* PagePickerView.swift in Sources */,
|
E21850092CEE01C30090B18B /* PagePickerView.swift in Sources */,
|
||||||
E29D31492D0489BB0051B7F4 /* AddFileView.swift in Sources */,
|
E29D31492D0489BB0051B7F4 /* AddFileView.swift in Sources */,
|
||||||
@ -1179,18 +1139,14 @@
|
|||||||
E29D31302D03A2C50051B7F4 /* DescriptionField.swift in Sources */,
|
E29D31302D03A2C50051B7F4 /* DescriptionField.swift in Sources */,
|
||||||
E29D31BE2D0DB85A0051B7F4 /* AudioPlayerCommand.swift in Sources */,
|
E29D31BE2D0DB85A0051B7F4 /* AudioPlayerCommand.swift in Sources */,
|
||||||
E29D31552D06D2CE0051B7F4 /* TagListView.swift in Sources */,
|
E29D31552D06D2CE0051B7F4 /* TagListView.swift in Sources */,
|
||||||
E29D31982D0C19340051B7F4 /* PathSettingsFile.swift in Sources */,
|
|
||||||
E2FE0F3A2D2B3E4F002963B7 /* AudioPlayerSettings.swift in Sources */,
|
E2FE0F3A2D2B3E4F002963B7 /* AudioPlayerSettings.swift in Sources */,
|
||||||
E2A37D152CE68BEC0000979F /* PostFile.swift in Sources */,
|
|
||||||
E2A21C032CB16C290060935B /* Environment+Language.swift in Sources */,
|
E2A21C032CB16C290060935B /* Environment+Language.swift in Sources */,
|
||||||
E2FE0F092D2689F0002963B7 /* TagPageGeneratorSource.swift in Sources */,
|
E2FE0F092D2689F0002963B7 /* TagPageGeneratorSource.swift in Sources */,
|
||||||
E22990302D0F75DE009F8D77 /* BoolPropertyView.swift in Sources */,
|
E22990302D0F75DE009F8D77 /* BoolPropertyView.swift in Sources */,
|
||||||
E21850392CFCA6C00090B18B /* WebsiteData+Mock.swift in Sources */,
|
|
||||||
E229901E2D0E4364009F8D77 /* LocalizedItem.swift in Sources */,
|
E229901E2D0E4364009F8D77 /* LocalizedItem.swift in Sources */,
|
||||||
E29D31262D0370A80051B7F4 /* VideoCommand+Option.swift in Sources */,
|
E29D31262D0370A80051B7F4 /* VideoCommand+Option.swift in Sources */,
|
||||||
E2FE0EF82D1D8110002963B7 /* IconCommand.swift in Sources */,
|
E2FE0EF82D1D8110002963B7 /* IconCommand.swift in Sources */,
|
||||||
E21850272CF3B42D0090B18B /* PostDetailView.swift in Sources */,
|
E21850272CF3B42D0090B18B /* PostDetailView.swift in Sources */,
|
||||||
E2A37D172CE73F1A0000979F /* TagFile.swift in Sources */,
|
|
||||||
E29D318E2D0B2E680051B7F4 /* PageSettingsContentView.swift in Sources */,
|
E29D318E2D0B2E680051B7F4 /* PageSettingsContentView.swift in Sources */,
|
||||||
E22990242D0EDBD0009F8D77 /* HeaderElement.swift in Sources */,
|
E22990242D0EDBD0009F8D77 /* HeaderElement.swift in Sources */,
|
||||||
E29D31BC2D0DB5120051B7F4 /* CommandProcessor.swift in Sources */,
|
E29D31BC2D0DB5120051B7F4 /* CommandProcessor.swift in Sources */,
|
||||||
@ -1221,18 +1177,16 @@
|
|||||||
E29D313F2D04822C0051B7F4 /* AddPostView.swift in Sources */,
|
E29D313F2D04822C0051B7F4 /* AddPostView.swift in Sources */,
|
||||||
E25DA5752D018B6100AEF16D /* FileDetailView.swift in Sources */,
|
E25DA5752D018B6100AEF16D /* FileDetailView.swift in Sources */,
|
||||||
E25DA50B2CFD988100AEF16D /* PageTagAssignmentView.swift in Sources */,
|
E25DA50B2CFD988100AEF16D /* PageTagAssignmentView.swift in Sources */,
|
||||||
E21850352CFAFA5A0090B18B /* SettingsFile.swift in Sources */,
|
|
||||||
E29D31A12D0C75CA0051B7F4 /* Content+Validation.swift in Sources */,
|
E29D31A12D0C75CA0051B7F4 /* Content+Validation.swift in Sources */,
|
||||||
E22990172D0E330F009F8D77 /* TagOverviewPage.swift in Sources */,
|
E22990172D0E330F009F8D77 /* TagOverviewPage.swift in Sources */,
|
||||||
E29D316D2D07A5050051B7F4 /* PageGenerationResults.swift in Sources */,
|
E29D316D2D07A5050051B7F4 /* PageGenerationResults.swift in Sources */,
|
||||||
E229903E2D0F8F02009F8D77 /* StringPropertyView.swift in Sources */,
|
E229903E2D0F8F02009F8D77 /* StringPropertyView.swift in Sources */,
|
||||||
E25DA5192CFF035900AEF16D /* Array+Split.swift in Sources */,
|
E25DA5192CFF035900AEF16D /* Array+Split.swift in Sources */,
|
||||||
E2FE0F1B2D274FDF002963B7 /* LinkPreviewItem.swift in Sources */,
|
|
||||||
E2FE0F062D267350002963B7 /* TextFieldPropertyView.swift in Sources */,
|
E2FE0F062D267350002963B7 /* TextFieldPropertyView.swift in Sources */,
|
||||||
|
E2FD1D232D2EB27000B48627 /* LoadingResult.swift in Sources */,
|
||||||
E29D31B82D0DAC250051B7F4 /* ButtonCommand.swift in Sources */,
|
E29D31B82D0DAC250051B7F4 /* ButtonCommand.swift in Sources */,
|
||||||
E29D31962D0C186E0051B7F4 /* PathSettings.swift in Sources */,
|
E29D31962D0C186E0051B7F4 /* PathSettings.swift in Sources */,
|
||||||
E2B85F412C4294790047CD0C /* PageHead.swift in Sources */,
|
E2B85F412C4294790047CD0C /* PageHead.swift in Sources */,
|
||||||
E2FE0F5E2D2BE190002963B7 /* FileResourceFile.swift in Sources */,
|
|
||||||
E2FE0F2A2D2AFBE6002963B7 /* ImageCompareIcons.swift in Sources */,
|
E2FE0F2A2D2AFBE6002963B7 /* ImageCompareIcons.swift in Sources */,
|
||||||
E29D316B2D07488B0051B7F4 /* PostListPageGenerator.swift in Sources */,
|
E29D316B2D07488B0051B7F4 /* PostListPageGenerator.swift in Sources */,
|
||||||
E218501D2CEE6CB60090B18B /* VerticalCenter.swift in Sources */,
|
E218501D2CEE6CB60090B18B /* VerticalCenter.swift in Sources */,
|
||||||
@ -1241,11 +1195,10 @@
|
|||||||
E2FE0F592D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift in Sources */,
|
E2FE0F592D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift in Sources */,
|
||||||
E2FE0EEC2D1C1253002963B7 /* MultiFileSelectionView.swift in Sources */,
|
E2FE0EEC2D1C1253002963B7 /* MultiFileSelectionView.swift in Sources */,
|
||||||
E29D31B32D0DA6E80051B7F4 /* ButtonIcons.swift in Sources */,
|
E29D31B32D0DA6E80051B7F4 /* ButtonIcons.swift in Sources */,
|
||||||
|
E2FD1D212D2EB22900B48627 /* ModelLoader.swift in Sources */,
|
||||||
E29D319D2D0C45B90051B7F4 /* PageIssueView.swift in Sources */,
|
E29D319D2D0C45B90051B7F4 /* PageIssueView.swift in Sources */,
|
||||||
E25DA5732D018AA100AEF16D /* FileContentView.swift in Sources */,
|
E25DA5732D018AA100AEF16D /* FileContentView.swift in Sources */,
|
||||||
E22990222D0ED12E009F8D77 /* TagOverviewFile.swift in Sources */,
|
|
||||||
E25DA5232CFF6C3700AEF16D /* ImageGenerator.swift in Sources */,
|
E25DA5232CFF6C3700AEF16D /* ImageGenerator.swift in Sources */,
|
||||||
E2FE0F002D266E0A002963B7 /* LocalizedNavigationSettingsFile.swift in Sources */,
|
|
||||||
E2A9CB7E2C7BCF2A005C89CC /* Page.swift in Sources */,
|
E2A9CB7E2C7BCF2A005C89CC /* Page.swift in Sources */,
|
||||||
E29D31202D0320E70051B7F4 /* ContentLabels.swift in Sources */,
|
E29D31202D0320E70051B7F4 /* ContentLabels.swift in Sources */,
|
||||||
E2FE0F572D2BCFD4002963B7 /* BlockLineProcessor.swift in Sources */,
|
E2FE0F572D2BCFD4002963B7 /* BlockLineProcessor.swift in Sources */,
|
||||||
@ -1263,6 +1216,7 @@
|
|||||||
E29D31A52D0CD03F0051B7F4 /* FileSelectionView.swift in Sources */,
|
E29D31A52D0CD03F0051B7F4 /* FileSelectionView.swift in Sources */,
|
||||||
E22990322D0F767B009F8D77 /* DatePropertyView.swift in Sources */,
|
E22990322D0F767B009F8D77 /* DatePropertyView.swift in Sources */,
|
||||||
E2A21C302CB490F90060935B /* HorizontalCenter.swift in Sources */,
|
E2A21C302CB490F90060935B /* HorizontalCenter.swift in Sources */,
|
||||||
|
E2FD1D1F2D2E9CC200B48627 /* ItemId.swift in Sources */,
|
||||||
E2FE0EFA2D25AFBA002963B7 /* PageHeader.swift in Sources */,
|
E2FE0EFA2D25AFBA002963B7 /* PageHeader.swift in Sources */,
|
||||||
E25DA5252CFF73AB00AEF16D /* NSSize+Scaling.swift in Sources */,
|
E25DA5252CFF73AB00AEF16D /* NSSize+Scaling.swift in Sources */,
|
||||||
E21850372CFCA55F0090B18B /* LocalizedPostSettings.swift in Sources */,
|
E21850372CFCA55F0090B18B /* LocalizedPostSettings.swift in Sources */,
|
||||||
@ -1283,7 +1237,7 @@
|
|||||||
E2FE0F112D268E7E002963B7 /* MarkdownCodeProcessor.swift in Sources */,
|
E2FE0F112D268E7E002963B7 /* MarkdownCodeProcessor.swift in Sources */,
|
||||||
E22990202D0ECBE5009F8D77 /* TagOverviewDetailView.swift in Sources */,
|
E22990202D0ECBE5009F8D77 /* TagOverviewDetailView.swift in Sources */,
|
||||||
E29D31C02D0DB9F20051B7F4 /* AudioPlayerContent.swift in Sources */,
|
E29D31C02D0DB9F20051B7F4 /* AudioPlayerContent.swift in Sources */,
|
||||||
E22990192D0E3546009F8D77 /* ItemType.swift in Sources */,
|
E22990192D0E3546009F8D77 /* ItemReference.swift in Sources */,
|
||||||
E25DA51F2CFF15C400AEF16D /* NavigationBar.swift in Sources */,
|
E25DA51F2CFF15C400AEF16D /* NavigationBar.swift in Sources */,
|
||||||
E2FE0F0F2D268D4F002963B7 /* BoxCommand.swift in Sources */,
|
E2FE0F0F2D268D4F002963B7 /* BoxCommand.swift in Sources */,
|
||||||
E22990482D10B7B7009F8D77 /* StorageAccessError.swift in Sources */,
|
E22990482D10B7B7009F8D77 /* StorageAccessError.swift in Sources */,
|
||||||
@ -1295,7 +1249,6 @@
|
|||||||
E29D314D2D04FCBF0051B7F4 /* FileToAddView.swift in Sources */,
|
E29D314D2D04FCBF0051B7F4 /* FileToAddView.swift in Sources */,
|
||||||
E2FE0F6C2D2D335E002963B7 /* LocalizedPageSettingsView.swift in Sources */,
|
E2FE0F6C2D2D335E002963B7 /* LocalizedPageSettingsView.swift in Sources */,
|
||||||
E218502D2CF791440090B18B /* PostImagesView.swift in Sources */,
|
E218502D2CF791440090B18B /* PostImagesView.swift in Sources */,
|
||||||
E29D312A2D039B090051B7F4 /* FileDescriptions.swift in Sources */,
|
|
||||||
E2B85F572C4BD0BB0047CD0C /* Binding+Extension.swift in Sources */,
|
E2B85F572C4BD0BB0047CD0C /* Binding+Extension.swift in Sources */,
|
||||||
E2B85F432C4294F60047CD0C /* FeedEntry.swift in Sources */,
|
E2B85F432C4294F60047CD0C /* FeedEntry.swift in Sources */,
|
||||||
E25DA56D2D00EBCF00AEF16D /* NavigationBarSettingsView.swift in Sources */,
|
E25DA56D2D00EBCF00AEF16D /* NavigationBarSettingsView.swift in Sources */,
|
||||||
@ -1311,13 +1264,12 @@
|
|||||||
E29D31AA2D0CEE3F0051B7F4 /* AudioPlayer.swift in Sources */,
|
E29D31AA2D0CEE3F0051B7F4 /* AudioPlayer.swift in Sources */,
|
||||||
E2FE0F4B2D2BCCAA002963B7 /* MarkdownHeadlineProcessor.swift in Sources */,
|
E2FE0F4B2D2BCCAA002963B7 /* MarkdownHeadlineProcessor.swift in Sources */,
|
||||||
E2FE0F532D2BCE17002963B7 /* SvgCommand.swift in Sources */,
|
E2FE0F532D2BCE17002963B7 /* SvgCommand.swift in Sources */,
|
||||||
E25DA5152CFF00C100AEF16D /* Content+Load.swift in Sources */,
|
|
||||||
E2FE0F3E2D2B4225002963B7 /* AudioSettingsDetailView.swift in Sources */,
|
E2FE0F3E2D2B4225002963B7 /* AudioSettingsDetailView.swift in Sources */,
|
||||||
E25DA58F2D02368D00AEF16D /* PageSettings.swift in Sources */,
|
E25DA58F2D02368D00AEF16D /* PageSettings.swift in Sources */,
|
||||||
E25DA50D2CFD9BA200AEF16D /* PostTagAssignmentView.swift in Sources */,
|
E25DA50D2CFD9BA200AEF16D /* PostTagAssignmentView.swift in Sources */,
|
||||||
|
E2FD1D0D2D2DBBA600B48627 /* LinkPreview.swift in Sources */,
|
||||||
E22990362D0F79D2009F8D77 /* OptionalStringPropertyView.swift in Sources */,
|
E22990362D0F79D2009F8D77 /* OptionalStringPropertyView.swift in Sources */,
|
||||||
E229903C2D0F8A7B009F8D77 /* OptionalTextFieldPropertyView.swift in Sources */,
|
E229903C2D0F8A7B009F8D77 /* OptionalTextFieldPropertyView.swift in Sources */,
|
||||||
E25DA5932D023B3C00AEF16D /* PageSettingsFile.swift in Sources */,
|
|
||||||
E29D31222D0363FD0051B7F4 /* ContentButtons.swift in Sources */,
|
E29D31222D0363FD0051B7F4 /* ContentButtons.swift in Sources */,
|
||||||
E2FE0F222D2A84A0002963B7 /* VideoCommand.swift in Sources */,
|
E2FE0F222D2A84A0002963B7 /* VideoCommand.swift in Sources */,
|
||||||
E2FE0F192D2723E3002963B7 /* ImageSet.swift in Sources */,
|
E2FE0F192D2723E3002963B7 /* ImageSet.swift in Sources */,
|
||||||
@ -1328,9 +1280,9 @@
|
|||||||
E2FE0F262D2AF9B0002963B7 /* ImageCompareCommand.swift in Sources */,
|
E2FE0F262D2AF9B0002963B7 /* ImageCompareCommand.swift in Sources */,
|
||||||
E2FE0F1E2D281AE1002963B7 /* TagOverviewGenerator.swift in Sources */,
|
E2FE0F1E2D281AE1002963B7 /* TagOverviewGenerator.swift in Sources */,
|
||||||
E29D31572D06D38B0051B7F4 /* AddTagView.swift in Sources */,
|
E29D31572D06D38B0051B7F4 /* AddTagView.swift in Sources */,
|
||||||
E25DA5362D0041EB00AEF16D /* PostSettingsFile.swift in Sources */,
|
|
||||||
E29D31792D083DE50051B7F4 /* PageContentResultsView.swift in Sources */,
|
E29D31792D083DE50051B7F4 /* PageContentResultsView.swift in Sources */,
|
||||||
E25DA5912D023A8400AEF16D /* IntegerField.swift in Sources */,
|
E25DA5912D023A8400AEF16D /* IntegerField.swift in Sources */,
|
||||||
|
E2FD1D192D2DC4F500B48627 /* LoadingContext.swift in Sources */,
|
||||||
E29D318B2D0B07EE0051B7F4 /* ContentBox.swift in Sources */,
|
E29D318B2D0B07EE0051B7F4 /* ContentBox.swift in Sources */,
|
||||||
E2FE0F4D2D2BCD30002963B7 /* PageLinkCommand.swift in Sources */,
|
E2FE0F4D2D2BCD30002963B7 /* PageLinkCommand.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
@ -120,6 +120,14 @@ extension String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
|
||||||
|
func removingPrefix(_ prefix: String) -> String? {
|
||||||
|
guard self.hasPrefix(prefix) else { return nil }
|
||||||
|
return String(self.dropFirst(prefix.count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
|
|
||||||
var fileNameWithoutExtension: String {
|
var fileNameWithoutExtension: String {
|
||||||
|
@ -38,9 +38,14 @@ struct PageLinkCommand: CommandProcessor {
|
|||||||
|
|
||||||
let localized = page.localized(in: language)
|
let localized = page.localized(in: language)
|
||||||
let url = page.absoluteUrl(in: language)
|
let url = page.absoluteUrl(in: language)
|
||||||
let title = localized.linkPreviewTitle ?? localized.title
|
let title = localized.linkPreview.title ?? localized.title
|
||||||
let description = localized.linkPreviewDescription ?? ""
|
let description = localized.linkPreview.description ?? ""
|
||||||
let image = makePageImage(item: localized)
|
let image = localized.linkPreview.image.map {
|
||||||
|
let size = content.settings.pages.pageLinkImageSize
|
||||||
|
let imageSet = $0.imageSet(width: size, height: size, language: language)
|
||||||
|
results.require(imageSet: imageSet)
|
||||||
|
return imageSet
|
||||||
|
}
|
||||||
|
|
||||||
return RelatedPageLink(
|
return RelatedPageLink(
|
||||||
title: title,
|
title: title,
|
||||||
@ -49,13 +54,4 @@ struct PageLinkCommand: CommandProcessor {
|
|||||||
image: image)
|
image: image)
|
||||||
.content
|
.content
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makePageImage(item: LinkPreviewItem) -> ImageSet? {
|
|
||||||
item.linkPreviewImage.map { image in
|
|
||||||
let size = content.settings.pages.pageLinkImageSize
|
|
||||||
let imageSet = image.imageSet(width: size, height: size, language: language)
|
|
||||||
results.require(imageSet: imageSet)
|
|
||||||
return imageSet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,13 @@ struct TagLinkCommand: CommandProcessor {
|
|||||||
let localized = tag.localized(in: language)
|
let localized = tag.localized(in: language)
|
||||||
let url = tag.absoluteUrl(in: language)
|
let url = tag.absoluteUrl(in: language)
|
||||||
let title = localized.name
|
let title = localized.name
|
||||||
let description = localized.linkPreviewDescription ?? ""
|
let description = localized.linkPreview.description ?? ""
|
||||||
let image = makePageImage(item: localized)
|
let image = localized.linkPreview.image.map {
|
||||||
|
let size = content.settings.pages.pageLinkImageSize
|
||||||
|
let imageSet = $0.imageSet(width: size, height: size, language: language)
|
||||||
|
results.require(imageSet: imageSet)
|
||||||
|
return imageSet
|
||||||
|
}
|
||||||
|
|
||||||
return RelatedPageLink(
|
return RelatedPageLink(
|
||||||
title: title,
|
title: title,
|
||||||
@ -43,13 +48,4 @@ struct TagLinkCommand: CommandProcessor {
|
|||||||
image: image)
|
image: image)
|
||||||
.content
|
.content
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makePageImage(item: LinkPreviewItem) -> ImageSet? {
|
|
||||||
item.linkPreviewImage.map { image in
|
|
||||||
let size = content.settings.pages.pageLinkImageSize
|
|
||||||
let imageSet = image.imageSet(width: size, height: size, language: language)
|
|
||||||
results.require(imageSet: imageSet)
|
|
||||||
return imageSet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,11 @@ struct ImageSet: HtmlProducer {
|
|||||||
|
|
||||||
let quality: CGFloat
|
let quality: CGFloat
|
||||||
|
|
||||||
let description: String
|
let description: String?
|
||||||
|
|
||||||
let extraAttributes: String
|
let extraAttributes: String
|
||||||
|
|
||||||
init(image: FileResource, maxWidth: Int, maxHeight: Int, description: String, quality: CGFloat = 0.7, extraAttributes: String? = nil) {
|
init(image: FileResource, maxWidth: Int, maxHeight: Int, description: String?, quality: CGFloat = 0.7, extraAttributes: String? = nil) {
|
||||||
self.image = image
|
self.image = image
|
||||||
self.maxWidth = maxWidth
|
self.maxWidth = maxWidth
|
||||||
self.maxHeight = maxHeight
|
self.maxHeight = maxHeight
|
||||||
@ -39,6 +39,13 @@ struct ImageSet: HtmlProducer {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var imageAltText: String {
|
||||||
|
guard let description else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return " alt='\(description.htmlEscaped())'"
|
||||||
|
}
|
||||||
|
|
||||||
func populate(_ result: inout String) {
|
func populate(_ result: inout String) {
|
||||||
let fileExtension = image.type.fileExtension.map { "." + $0 } ?? ""
|
let fileExtension = image.type.fileExtension.map { "." + $0 } ?? ""
|
||||||
|
|
||||||
@ -48,7 +55,7 @@ struct ImageSet: HtmlProducer {
|
|||||||
result += "<picture>"
|
result += "<picture>"
|
||||||
result += "<source type='image/avif' srcset='\(prefix1x).avif 1x, \(prefix2x).avif 2x'/>"
|
result += "<source type='image/avif' srcset='\(prefix1x).avif 1x, \(prefix2x).avif 2x'/>"
|
||||||
result += "<source type='image/webp' srcset='\(prefix1x).webp 1x, \(prefix1x).webp 2x'/>"
|
result += "<source type='image/webp' srcset='\(prefix1x).webp 1x, \(prefix1x).webp 2x'/>"
|
||||||
result += "<img srcset='\(prefix2x)\(fileExtension) 2x' src='\(prefix1x)\(fileExtension)' loading='lazy' alt='\(description.htmlEscaped())'\(extraAttributes)/>"
|
result += "<img srcset='\(prefix2x)\(fileExtension) 2x' src='\(prefix1x)\(fileExtension)' loading='lazy'\(imageAltText)\(extraAttributes)/>"
|
||||||
result += "</picture>"
|
result += "</picture>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,8 +58,8 @@ final class PageGenerator {
|
|||||||
|
|
||||||
let pageHeader = PageHeader(
|
let pageHeader = PageHeader(
|
||||||
language: language,
|
language: language,
|
||||||
title: localized.linkPreviewTitle ?? localized.title,
|
title: localized.linkPreview.title ?? localized.title,
|
||||||
description: localized.linkPreviewDescription,
|
description: localized.linkPreview.description,
|
||||||
iconUrl: iconUrl,
|
iconUrl: iconUrl,
|
||||||
languageButton: languageButton,
|
languageButton: languageButton,
|
||||||
links: content.navigationBar(in: language),
|
links: content.navigationBar(in: language),
|
||||||
|
@ -10,7 +10,7 @@ private struct TagData {
|
|||||||
init(tag: Tag, language: ContentLanguage) {
|
init(tag: Tag, language: ContentLanguage) {
|
||||||
let localized = tag.localized(in: language)
|
let localized = tag.localized(in: language)
|
||||||
self.url = tag.absoluteUrl(in: language)
|
self.url = tag.absoluteUrl(in: language)
|
||||||
self.title = localized.linkPreviewTitle ?? localized.name
|
self.title = localized.linkPreview.title ?? localized.name
|
||||||
self.localized = localized
|
self.localized = localized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,12 +81,12 @@ final class TagOverviewGenerator {
|
|||||||
self.results = results
|
self.results = results
|
||||||
}
|
}
|
||||||
|
|
||||||
func generatePages(tags: [Tag], overview: TagOverviewPage) {
|
func generatePages(tags: [Tag], overview: Tag) {
|
||||||
let localized = overview.localized(in: language)
|
let localized = overview.localized(in: language)
|
||||||
let header = TagHeaderContent(
|
let header = TagHeaderContent(
|
||||||
language: language,
|
language: language,
|
||||||
title: localized.linkPreviewTitle ?? localized.title,
|
title: localized.linkPreview.title ?? localized.title,
|
||||||
description: localized.linkPreviewDescription,
|
description: localized.linkPreview.description,
|
||||||
iconUrl: content.settings.navigation.localized(in: language).rootUrl,
|
iconUrl: content.settings.navigation.localized(in: language).rootUrl,
|
||||||
links: content.navigationBar(in: language),
|
links: content.navigationBar(in: language),
|
||||||
headers: content.postPageHeaders,
|
headers: content.postPageHeaders,
|
||||||
@ -123,8 +123,13 @@ final class TagOverviewGenerator {
|
|||||||
additionalFooter: "") { content in
|
additionalFooter: "") { content in
|
||||||
content += "<h1 class='separated-headline'>\(header.title)</h1>"
|
content += "<h1 class='separated-headline'>\(header.title)</h1>"
|
||||||
for tag in tags {
|
for tag in tags {
|
||||||
let description = tag.localized.linkPreviewDescription ?? ""
|
let description = tag.localized.linkPreview.description ?? ""
|
||||||
let image = self.makePageImage(item: tag.localized)
|
let image = tag.localized.linkPreview.image.map {
|
||||||
|
let size = self.content.settings.pages.pageLinkImageSize
|
||||||
|
let imageSet = $0.imageSet(width: size, height: size, language: self.language)
|
||||||
|
self.results.require(imageSet: imageSet)
|
||||||
|
return imageSet
|
||||||
|
}
|
||||||
|
|
||||||
content += RelatedPageLink(
|
content += RelatedPageLink(
|
||||||
title: tag.title,
|
title: tag.title,
|
||||||
@ -148,13 +153,4 @@ final class TagOverviewGenerator {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makePageImage(item: LinkPreviewItem) -> ImageSet? {
|
|
||||||
item.linkPreviewImage.map { image in
|
|
||||||
let size = content.settings.pages.pageLinkImageSize
|
|
||||||
let imageSet = image.imageSet(width: size, height: size, language: language)
|
|
||||||
results.require(imageSet: imageSet)
|
|
||||||
return imageSet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ struct TagPageGeneratorSource: PostListPageGeneratorSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pageDescription: String {
|
var pageDescription: String {
|
||||||
tag.localized(in: language).linkPreviewDescription ?? ""
|
tag.localized(in: language).linkPreview.description ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func pageUrlPrefix(for language: ContentLanguage) -> String {
|
func pageUrlPrefix(for language: ContentLanguage) -> String {
|
||||||
|
@ -56,7 +56,7 @@ final class GenerationResults: ObservableObject {
|
|||||||
var redirects: [String : String] = [:]
|
var redirects: [String : String] = [:]
|
||||||
|
|
||||||
/// The cache of previously used GenerationResults
|
/// The cache of previously used GenerationResults
|
||||||
private var cache: [ItemId : PageGenerationResults] = [:]
|
private var cache: [LocalizedItemId : PageGenerationResults] = [:]
|
||||||
|
|
||||||
private(set) var general: PageGenerationResults!
|
private(set) var general: PageGenerationResults!
|
||||||
|
|
||||||
@ -66,14 +66,14 @@ final class GenerationResults: ObservableObject {
|
|||||||
// MARK: Life cycle
|
// MARK: Life cycle
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
let id = ItemId(language: .english, itemType: .general)
|
let id = LocalizedItemId(language: .english, itemType: .general)
|
||||||
let general = PageGenerationResults(itemId: id, delegate: self)
|
let general = PageGenerationResults(itemId: id, delegate: self)
|
||||||
self.general = general
|
self.general = general
|
||||||
cache[id] = general
|
cache[id] = general
|
||||||
self.resultCount = 1
|
self.resultCount = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeResults(_ itemId: ItemId) -> PageGenerationResults {
|
func makeResults(_ itemId: LocalizedItemId) -> PageGenerationResults {
|
||||||
guard let result = cache[itemId] else {
|
guard let result = cache[itemId] else {
|
||||||
let result = PageGenerationResults(itemId: itemId, delegate: self)
|
let result = PageGenerationResults(itemId: itemId, delegate: self)
|
||||||
cache[itemId] = result
|
cache[itemId] = result
|
||||||
@ -83,18 +83,18 @@ final class GenerationResults: ObservableObject {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeResults(for type: ItemType, in language: ContentLanguage) -> PageGenerationResults {
|
func makeResults(for type: ItemReference, in language: ContentLanguage) -> PageGenerationResults {
|
||||||
let itemId = ItemId(language: language, itemType: type)
|
let itemId = LocalizedItemId(language: language, itemType: type)
|
||||||
return makeResults(itemId)
|
return makeResults(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeResults(for page: Page, in language: ContentLanguage) -> PageGenerationResults {
|
func makeResults(for page: Page, in language: ContentLanguage) -> PageGenerationResults {
|
||||||
let itemId = ItemId(language: language, itemType: .page(page))
|
let itemId = LocalizedItemId(language: language, itemType: .page(page))
|
||||||
return makeResults(itemId)
|
return makeResults(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeResults(for tag: Tag, in language: ContentLanguage) -> PageGenerationResults {
|
func makeResults(for tag: Tag, in language: ContentLanguage) -> PageGenerationResults {
|
||||||
let itemId = ItemId(language: language, itemType: .tagPage(tag))
|
let itemId = LocalizedItemId(language: language, itemType: .tagPage(tag))
|
||||||
return makeResults(itemId)
|
return makeResults(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,9 +209,9 @@ final class GenerationResults: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Dictionary where Value == Set<ItemId> {
|
private extension Dictionary where Value == Set<LocalizedItemId> {
|
||||||
|
|
||||||
mutating func remove<S>(keys: S, of item: ItemId) where S: Sequence, S.Element == Key {
|
mutating func remove<S>(keys: S, of item: LocalizedItemId) where S: Sequence, S.Element == Key {
|
||||||
for key in keys {
|
for key in keys {
|
||||||
guard var value = self[key] else { continue }
|
guard var value = self[key] else { continue }
|
||||||
value.remove(item)
|
value.remove(item)
|
||||||
|
@ -17,7 +17,7 @@ extension ImageToGenerate: Hashable {
|
|||||||
|
|
||||||
final class PageGenerationResults: ObservableObject {
|
final class PageGenerationResults: ObservableObject {
|
||||||
|
|
||||||
let itemId: ItemId
|
let itemId: LocalizedItemId
|
||||||
|
|
||||||
private unowned let delegate: GenerationResults
|
private unowned let delegate: GenerationResults
|
||||||
|
|
||||||
@ -73,13 +73,13 @@ final class PageGenerationResults: ObservableObject {
|
|||||||
private(set) var warnings: Set<String>
|
private(set) var warnings: Set<String>
|
||||||
|
|
||||||
/// The files that could not be saved to the output folder
|
/// The files that could not be saved to the output folder
|
||||||
private(set) var unsavedOutputFiles: [String: Set<ItemType>] = [:]
|
private(set) var unsavedOutputFiles: [String: Set<ItemReference>] = [:]
|
||||||
|
|
||||||
private(set) var pageIsEmpty: Bool
|
private(set) var pageIsEmpty: Bool
|
||||||
|
|
||||||
private(set) var redirect: (originalUrl: String, newUrl: String)?
|
private(set) var redirect: (originalUrl: String, newUrl: String)?
|
||||||
|
|
||||||
init(itemId: ItemId, delegate: GenerationResults) {
|
init(itemId: LocalizedItemId, delegate: GenerationResults) {
|
||||||
self.itemId = itemId
|
self.itemId = itemId
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
inaccessibleFiles = []
|
inaccessibleFiles = []
|
||||||
@ -245,7 +245,7 @@ final class PageGenerationResults: ObservableObject {
|
|||||||
delegate.warning(warning)
|
delegate.warning(warning)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsavedOutput(_ path: String, source: ItemType) {
|
func unsavedOutput(_ path: String, source: ItemReference) {
|
||||||
unsavedOutputFiles[path, default: []].insert(source)
|
unsavedOutputFiles[path, default: []].insert(source)
|
||||||
delegate.unsaved(path)
|
delegate.unsaved(path)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ struct InitialSetupView: View {
|
|||||||
if let message {
|
if let message {
|
||||||
Text(message)
|
Text(message)
|
||||||
.padding(.bottom)
|
.padding(.bottom)
|
||||||
|
.lineLimit(10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
@ -52,16 +53,27 @@ struct InitialSetupView: View {
|
|||||||
set(message: "Failed to set content path")
|
set(message: "Failed to set content path")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
|
||||||
do {
|
DispatchQueue.global().async {
|
||||||
try content.loadFromDisk()
|
let loader = ModelLoader(content: content, storage: content.storage)
|
||||||
} catch {
|
let result = loader.load()
|
||||||
set(message: "Failed to load database: \(error)")
|
guard result.errors.isEmpty else {
|
||||||
|
let message = "Failed to load database\n" + result.errors.sorted().joined(separator: "\n")
|
||||||
|
set(message: message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
content.files = result.files
|
||||||
|
content.posts = result.posts
|
||||||
|
content.pages = result.pages
|
||||||
|
content.tags = result.tags
|
||||||
|
content.settings = result.settings
|
||||||
|
content.tagOverview = result.tagOverview
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func set(message: String) {
|
private func set(message: String) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
@ -69,6 +69,12 @@ struct MainView: App {
|
|||||||
@State
|
@State
|
||||||
private var showInitialSetupSheet = false
|
private var showInitialSetupSheet = false
|
||||||
|
|
||||||
|
@State
|
||||||
|
private var showLoadErrorSheet = false
|
||||||
|
|
||||||
|
@State
|
||||||
|
private var loadErrors: [String] = []
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
var sidebar: some View {
|
var sidebar: some View {
|
||||||
switch selectedTab {
|
switch selectedTab {
|
||||||
@ -202,14 +208,30 @@ struct MainView: App {
|
|||||||
.environment(\.language, language)
|
.environment(\.language, language)
|
||||||
.environmentObject(content)
|
.environmentObject(content)
|
||||||
}
|
}
|
||||||
|
.sheet(isPresented: $showLoadErrorSheet) {
|
||||||
|
VStack {
|
||||||
|
Text("Failed to load database")
|
||||||
|
.font(.headline)
|
||||||
|
List(loadErrors, id: \.self) { error in
|
||||||
|
HStack {
|
||||||
|
Text(error)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(minHeight: 200)
|
||||||
|
Button("Dismiss", action: { showLoadErrorSheet = false })
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func save() {
|
private func save() {
|
||||||
do {
|
guard content.saveToDisk() else {
|
||||||
try content.saveToDisk()
|
print("Failed to save content")
|
||||||
} catch {
|
#warning("Show error message")
|
||||||
print("Failed to save content: \(error.localizedDescription)")
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,10 +240,12 @@ struct MainView: App {
|
|||||||
showInitialSheet()
|
showInitialSheet()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
do {
|
content.loadFromDisk { errors in
|
||||||
try content.loadFromDisk()
|
guard !errors.isEmpty else {
|
||||||
} catch {
|
return
|
||||||
print("Failed to load content: \(error.localizedDescription)")
|
}
|
||||||
|
self.loadErrors = errors
|
||||||
|
self.showLoadErrorSheet = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
extension Content {
|
|
||||||
|
|
||||||
private func convert(_ tag: LocalizedTagFile, images: [String : FileResource]) -> LocalizedTag {
|
|
||||||
LocalizedTag(
|
|
||||||
content: self,
|
|
||||||
urlComponent: tag.urlComponent,
|
|
||||||
name: tag.name,
|
|
||||||
linkPreviewTitle: tag.linkPreviewTitle,
|
|
||||||
linkPreviewDescription: tag.linkPreviewDescription,
|
|
||||||
linkPreviewImage: tag.linkPreviewImage.map { images[$0] },
|
|
||||||
originalUrl: tag.originalURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func convert(_ page: LocalizedPageFile, images: [String : FileResource]) -> LocalizedPage {
|
|
||||||
LocalizedPage(
|
|
||||||
content: self,
|
|
||||||
urlString: page.url,
|
|
||||||
title: page.title,
|
|
||||||
lastModified: page.lastModifiedDate,
|
|
||||||
originalUrl: page.originalURL,
|
|
||||||
linkPreviewImage: page.linkPreviewImage.map { images[$0] },
|
|
||||||
linkPreviewTitle: page.linkPreviewTitle,
|
|
||||||
linkPreviewDescription: page.linkPreviewDescription,
|
|
||||||
hideTitle: page.hideTitle ?? false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadFromDisk() throws {
|
|
||||||
guard storage.contentScope != nil else {
|
|
||||||
print("Storage not initialized, not loading content")
|
|
||||||
throw StorageAccessError.noBookmarkData
|
|
||||||
}
|
|
||||||
|
|
||||||
let settings = storage.loadSettings() ?? .default
|
|
||||||
|
|
||||||
guard let tagData = storage.loadAllTags() else {
|
|
||||||
print("Failed to load file tags")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if tagData.isEmpty { print("No tags loaded") }
|
|
||||||
|
|
||||||
guard let pagesData = storage.loadAllPages() else {
|
|
||||||
print("Failed to load file pages")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if pagesData.isEmpty { print("No pages loaded") }
|
|
||||||
|
|
||||||
guard let postsData = storage.loadAllPosts() else {
|
|
||||||
print("Failed to load file posts")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if postsData.isEmpty { print("No posts loaded") }
|
|
||||||
|
|
||||||
guard let fileList = storage.loadAllFiles() else {
|
|
||||||
print("Failed to load file list")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if fileList.isEmpty { print("No files loaded") }
|
|
||||||
|
|
||||||
print("Loaded data from disk, processing...")
|
|
||||||
// All data loaded from storage, start constructing the data model
|
|
||||||
|
|
||||||
let files: [String : FileResource] = fileList.reduce(into: [:]) { (files, data) in
|
|
||||||
let fileId = data.key
|
|
||||||
let fileData = data.value.data
|
|
||||||
let isExternal = data.value.isExternal
|
|
||||||
files[fileId] = FileResource(content: self, id: fileId, file: fileData, isExternalFile: isExternal)
|
|
||||||
}
|
|
||||||
|
|
||||||
let images = files.filter { $0.value.type.isImage }
|
|
||||||
|
|
||||||
let tags = tagData.reduce(into: [:]) { (tags, data) in
|
|
||||||
tags[data.key] = Tag(
|
|
||||||
content: self,
|
|
||||||
id: data.value.id,
|
|
||||||
isVisible: data.value.isVisible,
|
|
||||||
german: convert(data.value.german, images: images),
|
|
||||||
english: convert(data.value.english, images: images))
|
|
||||||
}
|
|
||||||
|
|
||||||
let pages: [String : Page] = loadPages(pagesData, tags: tags, files: files)
|
|
||||||
|
|
||||||
let posts: [String : Post] = postsData.reduce(into: [:]) { dict, data in
|
|
||||||
let (postId, post) = data
|
|
||||||
let linkedPage = post.linkedPageId.map { pages[$0] }
|
|
||||||
let german = LocalizedPost(content: self, file: post.german, images: images)
|
|
||||||
let english = LocalizedPost(content: self, file: post.english, images: images)
|
|
||||||
|
|
||||||
dict[postId] = Post(
|
|
||||||
content: self,
|
|
||||||
id: postId,
|
|
||||||
isDraft: post.isDraft,
|
|
||||||
createdDate: post.createdDate,
|
|
||||||
startDate: post.startDate,
|
|
||||||
endDate: post.endDate,
|
|
||||||
tags: post.tags.map { tags[$0]! },
|
|
||||||
german: german,
|
|
||||||
english: english,
|
|
||||||
linkedPage: linkedPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
let tagOverview = settings.tagOverview.map { file in
|
|
||||||
TagOverviewPage(
|
|
||||||
content: self,
|
|
||||||
german: .init(content: self, file: file.german, image: file.german.linkPreviewImage.map { files[$0] }),
|
|
||||||
english: .init(content: self, file: file.english, image: file.english.linkPreviewImage.map { files[$0] }))
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tags = tags.values.sorted()
|
|
||||||
self.pages = pages.values.sorted(ascending: false) { $0.startDate }
|
|
||||||
self.files = files.values.sorted { $0.id }
|
|
||||||
self.posts = posts.values.sorted(ascending: false) { $0.startDate }
|
|
||||||
self.tagOverview = tagOverview
|
|
||||||
self.settings = .init(file: settings, files: files) { raw in
|
|
||||||
#warning("Notify about missing links")
|
|
||||||
guard let type = ItemType(rawValue: raw, posts: posts, pages: pages, tags: tags) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch type {
|
|
||||||
case .general:
|
|
||||||
return nil
|
|
||||||
case .post(let post):
|
|
||||||
return post
|
|
||||||
case .feed:
|
|
||||||
return nil // TODO: Provide feed object
|
|
||||||
case .page(let page):
|
|
||||||
return page
|
|
||||||
case .tagPage(let tag):
|
|
||||||
return tag
|
|
||||||
case .tagOverview:
|
|
||||||
return tagOverview
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print("Content loaded")
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadPages(_ pagesData: [String : PageFile], tags: [String : Tag], files: [String : FileResource]) -> [String : Page] {
|
|
||||||
pagesData.reduce(into: [:]) { pages, data in
|
|
||||||
let (pageId, page) = data
|
|
||||||
pages[pageId] = Page(
|
|
||||||
content: self,
|
|
||||||
id: pageId,
|
|
||||||
externalLink: page.externalLink,
|
|
||||||
isDraft: page.isDraft,
|
|
||||||
createdDate: page.createdDate,
|
|
||||||
hideDate: page.hideDate ?? false,
|
|
||||||
startDate: page.startDate,
|
|
||||||
endDate: page.endDate,
|
|
||||||
german: convert(page.german, images: files),
|
|
||||||
english: convert(page.english, images: files),
|
|
||||||
tags: page.tags.compactMap { tags[$0] },
|
|
||||||
requiredFiles: page.requiredFiles?.compactMap { files[$0] } ?? [])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -2,22 +2,25 @@ import Foundation
|
|||||||
|
|
||||||
extension Content {
|
extension Content {
|
||||||
|
|
||||||
func saveToDisk() throws {
|
func saveToDisk() -> Bool {
|
||||||
|
guard didLoadContent else { return false }
|
||||||
guard storage.contentScope != nil else {
|
guard storage.contentScope != nil else {
|
||||||
print("Storage not initialized, not saving content")
|
print("Storage not initialized, not saving content")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var failedSaves = 0
|
var failedSaves = 0
|
||||||
failedSaves += pages.count { !storage.save(pageMetadata: $0.pageFile, for: $0.id) }
|
failedSaves += pages.count { !storage.save(pageMetadata: $0.data, for: $0.id) }
|
||||||
failedSaves += posts.count { !storage.save(post: $0.postFile, for: $0.id) }
|
failedSaves += posts.count { !storage.save(post: $0.data, for: $0.id) }
|
||||||
failedSaves += tags.count { !storage.save(tagMetadata: $0.file, for: $0.id) }
|
failedSaves += tags.count { !storage.save(tagMetadata: $0.data, for: $0.id) }
|
||||||
failedSaves.increment(!storage.save(settings: settings.file(tagOverview: tagOverview)))
|
failedSaves.increment(!storage.save(settings: settings.data(tagOverview: tagOverview)))
|
||||||
failedSaves += files.count { !storage.save(fileInfo: $0.fileInfo, for: $0.id) }
|
failedSaves += files.count { !storage.save(fileInfo: $0.data, for: $0.id) }
|
||||||
|
|
||||||
if failedSaves > 0 {
|
if failedSaves > 0 {
|
||||||
print("Save partially failed with \(failedSaves) errors")
|
print("Save partially failed with \(failedSaves) errors")
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeUnlinkedFiles() -> Bool {
|
func removeUnlinkedFiles() -> Bool {
|
||||||
@ -37,49 +40,3 @@ extension Content {
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Page {
|
|
||||||
|
|
||||||
var pageFile: PageFile {
|
|
||||||
.init(isDraft: isDraft,
|
|
||||||
externalLink: externalLink,
|
|
||||||
tags: tags.map { $0.id },
|
|
||||||
hideDate: hideDate ? true : nil,
|
|
||||||
createdDate: createdDate,
|
|
||||||
startDate: startDate,
|
|
||||||
endDate: hasEndDate ? endDate : nil,
|
|
||||||
german: german.pageFile,
|
|
||||||
english: english.pageFile,
|
|
||||||
requiredFiles: requiredFiles.nonEmpty?.map { $0.id }.sorted())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension LocalizedPage {
|
|
||||||
|
|
||||||
var pageFile: LocalizedPageFile {
|
|
||||||
.init(url: urlString,
|
|
||||||
title: title,
|
|
||||||
linkPreviewImage: linkPreviewImage?.id,
|
|
||||||
linkPreviewTitle: linkPreviewTitle,
|
|
||||||
linkPreviewDescription: linkPreviewDescription,
|
|
||||||
lastModifiedDate: lastModified,
|
|
||||||
originalURL: originalUrl,
|
|
||||||
hideTitle: hideTitle ? true : nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private extension Post {
|
|
||||||
|
|
||||||
var postFile: PostFile {
|
|
||||||
.init(
|
|
||||||
isDraft: isDraft,
|
|
||||||
createdDate: createdDate,
|
|
||||||
startDate: startDate,
|
|
||||||
endDate: hasEndDate ? endDate : nil,
|
|
||||||
tags: tags.map { $0.id },
|
|
||||||
german: german.postFile,
|
|
||||||
english: english.postFile,
|
|
||||||
linkedPageId: linkedPage?.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,6 +4,9 @@ import Combine
|
|||||||
|
|
||||||
final class Content: ObservableObject {
|
final class Content: ObservableObject {
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var didLoadContent = false
|
||||||
|
|
||||||
@ObservedObject
|
@ObservedObject
|
||||||
var storage: Storage
|
var storage: Storage
|
||||||
|
|
||||||
@ -23,7 +26,7 @@ final class Content: ObservableObject {
|
|||||||
var files: [FileResource]
|
var files: [FileResource]
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var tagOverview: TagOverviewPage?
|
var tagOverview: Tag?
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var results: GenerationResults
|
var results: GenerationResults
|
||||||
@ -47,7 +50,7 @@ final class Content: ObservableObject {
|
|||||||
pages: [Page],
|
pages: [Page],
|
||||||
tags: [Tag],
|
tags: [Tag],
|
||||||
files: [FileResource],
|
files: [FileResource],
|
||||||
tagOverview: TagOverviewPage?) {
|
tagOverview: Tag?) {
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.posts = posts
|
self.posts = posts
|
||||||
self.pages = pages
|
self.pages = pages
|
||||||
@ -112,16 +115,11 @@ final class Content: ObservableObject {
|
|||||||
pages.insert(page, at: 0)
|
pages.insert(page, at: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(contentPath: URL) {
|
func update(contentPath: URL, callback: @escaping ([String]) -> ()) {
|
||||||
guard storage.save(contentPath: contentPath) else {
|
guard storage.save(contentPath: contentPath) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
clear()
|
loadFromDisk(callback: callback)
|
||||||
do {
|
|
||||||
try loadFromDisk()
|
|
||||||
} catch {
|
|
||||||
print("Failed to reload content: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func remove(_ file: FileResource) {
|
func remove(_ file: FileResource) {
|
||||||
@ -146,4 +144,29 @@ final class Content: ObservableObject {
|
|||||||
func file(withOutputPath: String) -> FileResource? {
|
func file(withOutputPath: String) -> FileResource? {
|
||||||
files.first { $0.absoluteUrl == withOutputPath }
|
files.first { $0.absoluteUrl == withOutputPath }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadFromDisk(callback: @escaping (_ errors: [String]) -> ()) {
|
||||||
|
DispatchQueue.global().async {
|
||||||
|
let loader = ModelLoader(content: self, storage: self.storage)
|
||||||
|
let result = loader.load()
|
||||||
|
guard result.errors.isEmpty else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.didLoadContent = false
|
||||||
|
callback(result.errors.sorted())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.files = result.files
|
||||||
|
self.posts = result.posts
|
||||||
|
self.pages = result.pages
|
||||||
|
self.tags = result.tags
|
||||||
|
self.settings = result.settings
|
||||||
|
self.tagOverview = result.tagOverview
|
||||||
|
self.didLoadContent = true
|
||||||
|
callback([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,43 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
final class FileResource: Item {
|
final class FileResource: Item, LocalizedItem {
|
||||||
|
|
||||||
let type: FileType
|
let type: FileType
|
||||||
|
|
||||||
|
/// Indicate if the file content is stored by the app
|
||||||
@Published
|
@Published
|
||||||
var isExternallyStored: Bool
|
var isExternallyStored: Bool
|
||||||
|
|
||||||
|
/// The file/image description in German
|
||||||
@Published
|
@Published
|
||||||
var german: String
|
var german: String?
|
||||||
|
|
||||||
|
/// The file/image description in English
|
||||||
@Published
|
@Published
|
||||||
var english: String
|
var english: String?
|
||||||
|
|
||||||
|
/// A version string of this resource, mostly for assets
|
||||||
@Published
|
@Published
|
||||||
var version: String?
|
var version: String?
|
||||||
|
|
||||||
|
/// A URL where the resource was copied/downloaded from
|
||||||
@Published
|
@Published
|
||||||
var sourceUrl: String?
|
var sourceUrl: String?
|
||||||
|
|
||||||
|
/// The list of generated image versions for this image
|
||||||
@Published
|
@Published
|
||||||
var generatedImageVersions: Set<String>
|
var generatedImageVersions: Set<String>
|
||||||
|
|
||||||
|
/// A custom file path in the output folder where this file is located
|
||||||
@Published
|
@Published
|
||||||
var customOutputPath: String?
|
var customOutputPath: String?
|
||||||
|
|
||||||
|
/// The date when the file was added
|
||||||
@Published
|
@Published
|
||||||
var addedDate: Date
|
var addedDate: Date
|
||||||
|
|
||||||
|
/// The date when the file was last modified
|
||||||
@Published
|
@Published
|
||||||
var modifiedDate: Date
|
var modifiedDate: Date
|
||||||
|
|
||||||
@ -53,8 +62,8 @@ final class FileResource: Item {
|
|||||||
modifiedDate: Date = .now) {
|
modifiedDate: Date = .now) {
|
||||||
self.type = FileType(fileExtension: id.fileExtension)
|
self.type = FileType(fileExtension: id.fileExtension)
|
||||||
self.isExternallyStored = isExternallyStored
|
self.isExternallyStored = isExternallyStored
|
||||||
self.german = german ?? ""
|
self.german = german
|
||||||
self.english = english ?? ""
|
self.english = english
|
||||||
self.version = version
|
self.version = version
|
||||||
self.sourceUrl = sourceUrl
|
self.sourceUrl = sourceUrl
|
||||||
self.generatedImageVersions = generatedImageVersions
|
self.generatedImageVersions = generatedImageVersions
|
||||||
@ -64,20 +73,6 @@ final class FileResource: Item {
|
|||||||
super.init(content: content, id: id)
|
super.init(content: content, id: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(content: Content, id: String, file: FileResourceFile, isExternalFile: Bool) {
|
|
||||||
self.type = FileType(fileExtension: id.fileExtension)
|
|
||||||
self.isExternallyStored = isExternalFile
|
|
||||||
self.german = file.germanDescription ?? ""
|
|
||||||
self.english = file.englishDescription ?? ""
|
|
||||||
self.version = file.version
|
|
||||||
self.sourceUrl = file.sourceUrl
|
|
||||||
self.generatedImageVersions = Set(file.generatedImages ?? [])
|
|
||||||
self.customOutputPath = file.customOutputPath
|
|
||||||
self.addedDate = file.addedDate
|
|
||||||
self.modifiedDate = file.modifiedDate
|
|
||||||
super.init(content: content, id: id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Only for bundle images
|
Only for bundle images
|
||||||
*/
|
*/
|
||||||
@ -101,7 +96,7 @@ final class FileResource: Item {
|
|||||||
content.storage.fileContent(for: id) ?? ""
|
content.storage.fileContent(for: id) ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func dataContent() -> Data? {
|
func dataContent() -> Foundation.Data? {
|
||||||
content.storage.fileData(for: id)
|
content.storage.fileData(for: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +126,6 @@ final class FileResource: Item {
|
|||||||
// Image must have changed, so force regeneration
|
// Image must have changed, so force regeneration
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.imageDimensions = size
|
self.imageDimensions = size
|
||||||
self.didChange()
|
|
||||||
self.removeGeneratedImages()
|
self.removeGeneratedImages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -299,12 +293,34 @@ final class FileResource: Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension FileResource: CustomStringConvertible {
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension FileResource {
|
extension FileResource {
|
||||||
|
|
||||||
var fileInfo: FileResourceFile {
|
convenience init(content: Content, id: String, data: FileResource.Data, isExternalFile: Bool) {
|
||||||
|
self.init(
|
||||||
|
content: content,
|
||||||
|
id: id,
|
||||||
|
isExternallyStored: isExternalFile,
|
||||||
|
english: data.englishDescription,
|
||||||
|
german: data.germanDescription,
|
||||||
|
version: data.version,
|
||||||
|
sourceUrl: data.sourceUrl,
|
||||||
|
generatedImageVersions: Set(data.generatedImages ?? []),
|
||||||
|
customOutputPath: data.customOutputPath,
|
||||||
|
addedDate: data.addedDate,
|
||||||
|
modifiedDate: data.modifiedDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
.init(
|
.init(
|
||||||
englishDescription: english.nonEmpty,
|
englishDescription: english,
|
||||||
germanDescription: german.nonEmpty,
|
germanDescription: german,
|
||||||
generatedImages: generatedImageVersions.sorted().nonEmpty,
|
generatedImages: generatedImageVersions.sorted().nonEmpty,
|
||||||
customOutputPath: customOutputPath,
|
customOutputPath: customOutputPath,
|
||||||
version: version,
|
version: version,
|
||||||
@ -312,15 +328,16 @@ extension FileResource {
|
|||||||
addedDate: addedDate,
|
addedDate: addedDate,
|
||||||
modifiedDate: modifiedDate)
|
modifiedDate: modifiedDate)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension FileResource: LocalizedItem {
|
/// This struct holds metadata about a file resource that is stored in the content folder.
|
||||||
|
struct Data: Codable {
|
||||||
}
|
let englishDescription: String?
|
||||||
|
let germanDescription: String?
|
||||||
extension FileResource: CustomStringConvertible {
|
let generatedImages: [String]?
|
||||||
|
let customOutputPath: String?
|
||||||
var description: String {
|
let version: String?
|
||||||
id
|
let sourceUrl: String?
|
||||||
|
let addedDate: Date
|
||||||
|
let modifiedDate: Date
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,12 +39,20 @@ class Item: ObservableObject, Identifiable {
|
|||||||
var itemType: ItemType {
|
var itemType: ItemType {
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var itemReference: ItemReference {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemId: ItemId {
|
||||||
|
.init(type: itemType, id: id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Item: Equatable {
|
extension Item: Equatable {
|
||||||
|
|
||||||
static func == (lhs: Item, rhs: Item) -> Bool {
|
static func == (lhs: Item, rhs: Item) -> Bool {
|
||||||
lhs.id == rhs.id
|
lhs.id == rhs.id && lhs.itemType == rhs.itemType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,12 +60,13 @@ extension Item: Hashable {
|
|||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
func hash(into hasher: inout Hasher) {
|
||||||
hasher.combine(id)
|
hasher.combine(id)
|
||||||
|
hasher.combine(itemType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Item: Comparable {
|
extension Item: Comparable {
|
||||||
|
|
||||||
static func < (lhs: Item, rhs: Item) -> Bool {
|
static func < (lhs: Item, rhs: Item) -> Bool {
|
||||||
lhs.id < rhs.id
|
lhs.id < rhs.id && lhs.itemType < rhs.itemType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,11 @@
|
|||||||
|
|
||||||
struct ItemId {
|
struct ItemId {
|
||||||
|
|
||||||
let language: ContentLanguage
|
let type: ItemType
|
||||||
|
|
||||||
let itemType: ItemType
|
let id: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ItemId: Equatable {
|
extension ItemId: Codable {
|
||||||
|
|
||||||
static func == (lhs: ItemId, rhs: ItemId) -> Bool {
|
|
||||||
lhs.language == rhs.language &&
|
|
||||||
lhs.itemType == rhs.itemType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ItemId: Hashable {
|
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
|
||||||
hasher.combine(language)
|
|
||||||
hasher.combine(itemType.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ItemId: Comparable {
|
|
||||||
|
|
||||||
static func < (lhs: ItemId, rhs: ItemId) -> Bool {
|
|
||||||
guard lhs.itemType == rhs.itemType else {
|
|
||||||
return lhs.itemType < rhs.itemType
|
|
||||||
}
|
|
||||||
return lhs.language < rhs.language
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
68
CHDataManagement/Model/Item/ItemReference.swift
Normal file
68
CHDataManagement/Model/Item/ItemReference.swift
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
|
||||||
|
enum ItemReference {
|
||||||
|
|
||||||
|
case general
|
||||||
|
|
||||||
|
case post(Post)
|
||||||
|
|
||||||
|
case feed
|
||||||
|
|
||||||
|
case page(Page)
|
||||||
|
|
||||||
|
case tagPage(Tag)
|
||||||
|
|
||||||
|
case tagOverview
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ItemReference: Equatable {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ItemReference: Hashable {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ItemReference: Identifiable {
|
||||||
|
|
||||||
|
var id: String {
|
||||||
|
switch self {
|
||||||
|
case .general:
|
||||||
|
return "0-general"
|
||||||
|
case .feed:
|
||||||
|
return "1-feed"
|
||||||
|
case .post(let post):
|
||||||
|
return "2-post-\(post.id)"
|
||||||
|
case .page(let page):
|
||||||
|
return "3-page-\(page.id)"
|
||||||
|
case .tagPage(let tag):
|
||||||
|
return "5-tag-\(tag.id)"
|
||||||
|
case .tagOverview:
|
||||||
|
return "4-tag-overview"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init?(context: LoadingContext, rawValue: String) {
|
||||||
|
if rawValue == "0-general" {
|
||||||
|
self = .general
|
||||||
|
} else if rawValue == "1-feed" {
|
||||||
|
self = .feed
|
||||||
|
} else if rawValue == "4-tag-overview" {
|
||||||
|
self = .tagOverview
|
||||||
|
} else if let id = rawValue.removingPrefix("3-page-"), let page = context.page(id) {
|
||||||
|
self = .page(page)
|
||||||
|
} else if let id = rawValue.removingPrefix("2-post-"), let post = context.post(id) {
|
||||||
|
self = .post(post)
|
||||||
|
} else if let id = rawValue.removingPrefix("5-tag-"), let tag = context.tag(id) {
|
||||||
|
self = .tagPage(tag)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ItemReference: Comparable {
|
||||||
|
|
||||||
|
static func < (lhs: ItemReference, rhs: ItemReference) -> Bool {
|
||||||
|
lhs.id < rhs.id
|
||||||
|
}
|
||||||
|
}
|
@ -1,76 +1,21 @@
|
|||||||
|
|
||||||
enum ItemType {
|
enum ItemType: String, Equatable, Hashable {
|
||||||
|
|
||||||
case general
|
case post = "post"
|
||||||
|
|
||||||
case post(Post)
|
case page = "page"
|
||||||
|
|
||||||
case feed
|
case tag = "tag"
|
||||||
|
|
||||||
case page(Page)
|
case tagOverview = "tag-overview"
|
||||||
|
|
||||||
case tagPage(Tag)
|
|
||||||
|
|
||||||
case tagOverview
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ItemType: Equatable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ItemType: Hashable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ItemType: Identifiable {
|
|
||||||
|
|
||||||
var id: String {
|
|
||||||
switch self {
|
|
||||||
case .general:
|
|
||||||
return "0-general"
|
|
||||||
case .feed:
|
|
||||||
return "1-feed"
|
|
||||||
case .post(let post):
|
|
||||||
return "2-post-\(post.id)"
|
|
||||||
case .page(let page):
|
|
||||||
return "3-page-\(page.id)"
|
|
||||||
case .tagPage(let tag):
|
|
||||||
return "5-tag-\(tag.id)"
|
|
||||||
case .tagOverview:
|
|
||||||
return "4-tag-overview"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init?(rawValue: String, posts: [String : Post], pages: [String : Page], tags: [String : Tag]) {
|
|
||||||
if rawValue == "0-general" {
|
|
||||||
self = .general
|
|
||||||
} else if rawValue == "1-feed" {
|
|
||||||
self = .feed
|
|
||||||
} else if rawValue == "4-tag-overview" {
|
|
||||||
self = .tagOverview
|
|
||||||
} else if let id = rawValue.removingPrefix("3-page-"), let page = pages[id] {
|
|
||||||
self = .page(page)
|
|
||||||
} else if let id = rawValue.removingPrefix("2-post-"), let post = posts[id] {
|
|
||||||
self = .post(post)
|
|
||||||
} else if let id = rawValue.removingPrefix("5-tag-"), let tag = tags[id] {
|
|
||||||
self = .tagPage(tag)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ItemType: Comparable {
|
extension ItemType: Comparable {
|
||||||
|
public static func < (lhs: ItemType, rhs: ItemType) -> Bool {
|
||||||
static func < (lhs: ItemType, rhs: ItemType) -> Bool {
|
lhs.rawValue < rhs.rawValue
|
||||||
lhs.id < rhs.id
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension String {
|
extension ItemType: Codable {
|
||||||
|
|
||||||
func removingPrefix(_ prefix: String) -> String? {
|
|
||||||
guard self.hasPrefix(prefix) else { return nil }
|
|
||||||
return String(self.dropFirst(prefix.count))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
protocol LinkPreviewItem: AnyObject {
|
|
||||||
|
|
||||||
var linkPreviewImage: FileResource? { get set }
|
|
||||||
|
|
||||||
var linkPreviewTitle: String? { get }
|
|
||||||
|
|
||||||
var linkPreviewDescription: String? { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LinkPreviewItem {
|
|
||||||
|
|
||||||
func remove(linkPreviewImage file: FileResource) {
|
|
||||||
if linkPreviewImage == file {
|
|
||||||
linkPreviewImage = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
33
CHDataManagement/Model/Item/LocalizedItemId.swift
Normal file
33
CHDataManagement/Model/Item/LocalizedItemId.swift
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
struct LocalizedItemId {
|
||||||
|
|
||||||
|
let language: ContentLanguage
|
||||||
|
|
||||||
|
let itemType: ItemReference
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LocalizedItemId: Equatable {
|
||||||
|
|
||||||
|
static func == (lhs: LocalizedItemId, rhs: LocalizedItemId) -> Bool {
|
||||||
|
lhs.language == rhs.language &&
|
||||||
|
lhs.itemType == rhs.itemType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LocalizedItemId: Hashable {
|
||||||
|
|
||||||
|
func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(language)
|
||||||
|
hasher.combine(itemType.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LocalizedItemId: Comparable {
|
||||||
|
|
||||||
|
static func < (lhs: LocalizedItemId, rhs: LocalizedItemId) -> Bool {
|
||||||
|
guard lhs.itemType == rhs.itemType else {
|
||||||
|
return lhs.itemType < rhs.itemType
|
||||||
|
}
|
||||||
|
return lhs.language < rhs.language
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
/*
|
||||||
final class TagOverviewPage: Item {
|
final class TagOverviewPage: Item {
|
||||||
|
|
||||||
static let id = "all-tags"
|
static let id = "all-tags"
|
||||||
@ -105,3 +105,4 @@ final class LocalizedTagOverviewPage: ObservableObject {
|
|||||||
!content.containsTag(withUrlComponent: urlComponent)
|
!content.containsTag(withUrlComponent: urlComponent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
58
CHDataManagement/Model/LinkPreview.swift
Normal file
58
CHDataManagement/Model/LinkPreview.swift
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
/**
|
||||||
|
The information to use when constructing the link preview of a page.
|
||||||
|
|
||||||
|
The information will be placed in the `<head>` of the page as `<meta>` tags.
|
||||||
|
*/
|
||||||
|
final class LinkPreview: ObservableObject {
|
||||||
|
|
||||||
|
/// The description to show when linking to a page (contained in the `<head>` of the page)
|
||||||
|
@Published
|
||||||
|
var title: String?
|
||||||
|
|
||||||
|
/// The image id of the thumbnail to attach to the link preview (contained in the `<head>` of the page)
|
||||||
|
@Published
|
||||||
|
var description: String?
|
||||||
|
|
||||||
|
/// The title to show for a link preview (contained in the `<head>` of the page)
|
||||||
|
@Published
|
||||||
|
var image: FileResource?
|
||||||
|
|
||||||
|
init(title: String? = nil, description: String? = nil, image: FileResource? = nil) {
|
||||||
|
self.title = title
|
||||||
|
self.description = description
|
||||||
|
self.image = image
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Remove a file if it is used in the link preview.
|
||||||
|
*/
|
||||||
|
func remove(_ file: FileResource) {
|
||||||
|
if image == file {
|
||||||
|
image = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Storage
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
.init(title: title, description: description, image: image?.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(context: LoadingContext, data: Data) {
|
||||||
|
self.title = data.title
|
||||||
|
self.description = data.description
|
||||||
|
self.image = data.image.map(context.image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LinkPreview {
|
||||||
|
|
||||||
|
/// The object to serialize a link preview for storage
|
||||||
|
struct Data: Codable {
|
||||||
|
let title: String?
|
||||||
|
let description: String?
|
||||||
|
let image: String?
|
||||||
|
}
|
||||||
|
}
|
110
CHDataManagement/Model/Loading/LoadingContext.swift
Normal file
110
CHDataManagement/Model/Loading/LoadingContext.swift
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
|
||||||
|
final class LoadingContext {
|
||||||
|
|
||||||
|
let content: Content
|
||||||
|
|
||||||
|
var files: [String: FileResource] = [:]
|
||||||
|
|
||||||
|
var pages: [String : Page] = [:]
|
||||||
|
|
||||||
|
var tags: [String : Tag] = [:]
|
||||||
|
|
||||||
|
var posts: [String : Post] = [:]
|
||||||
|
|
||||||
|
var errors: Set<String> = []
|
||||||
|
|
||||||
|
var tagOverview: TagOverview?
|
||||||
|
|
||||||
|
var settings: Settings?
|
||||||
|
|
||||||
|
init(content: Content) {
|
||||||
|
self.content = content
|
||||||
|
}
|
||||||
|
|
||||||
|
func results() -> LoadingResult {
|
||||||
|
.init(
|
||||||
|
settings: settings ?? .default,
|
||||||
|
posts: posts.values.sorted(ascending: false) { $0.startDate },
|
||||||
|
pages: pages.values.sorted(ascending: false) { $0.startDate },
|
||||||
|
tags: tags.values.sorted(),
|
||||||
|
files: files.values.sorted { $0.id },
|
||||||
|
tagOverview: tagOverview,
|
||||||
|
errors: errors.sorted())
|
||||||
|
}
|
||||||
|
|
||||||
|
func error(_ message: String) {
|
||||||
|
errors.insert(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func post(_ postId: String) -> Post? {
|
||||||
|
if let post = posts[postId] {
|
||||||
|
return post
|
||||||
|
}
|
||||||
|
error("Missing post \(postId)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tag(_ tagId: String) -> Tag? {
|
||||||
|
if let tag = tags[tagId] {
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
error("Missing tag \(tagId)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func page(_ pageId: String) -> Page? {
|
||||||
|
if let page = pages[pageId] {
|
||||||
|
return page
|
||||||
|
}
|
||||||
|
error("Missing page \(pageId)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func file(_ fileId: String) -> FileResource? {
|
||||||
|
if let file = files[fileId] {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
error("Missing file \(fileId)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func image(_ imageId: String) -> FileResource? {
|
||||||
|
guard let image = file(imageId) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if image.type.isImage {
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
error("Image \(imageId) is not an image")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(itemId: ItemId) -> Item? {
|
||||||
|
switch itemId.type {
|
||||||
|
case .post:
|
||||||
|
guard let id = itemId.id else {
|
||||||
|
error("Missing post id in itemId")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return post(id)
|
||||||
|
case .page:
|
||||||
|
guard let id = itemId.id else {
|
||||||
|
error("Missing page id in itemId")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return page(id)
|
||||||
|
case .tag:
|
||||||
|
guard let id = itemId.id else {
|
||||||
|
error("Missing tag id in itemId")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tag(id)
|
||||||
|
case .tagOverview:
|
||||||
|
guard let tagOverview else {
|
||||||
|
error("Missing tag overview")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tagOverview
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
CHDataManagement/Model/Loading/LoadingResult.swift
Normal file
17
CHDataManagement/Model/Loading/LoadingResult.swift
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
struct LoadingResult {
|
||||||
|
|
||||||
|
let settings: Settings
|
||||||
|
|
||||||
|
let posts: [Post]
|
||||||
|
|
||||||
|
let pages: [Page]
|
||||||
|
|
||||||
|
let tags: [Tag]
|
||||||
|
|
||||||
|
let files: [FileResource]
|
||||||
|
|
||||||
|
let tagOverview: Tag?
|
||||||
|
|
||||||
|
let errors: [String]
|
||||||
|
}
|
96
CHDataManagement/Model/Loading/ModelLoader.swift
Normal file
96
CHDataManagement/Model/Loading/ModelLoader.swift
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
|
||||||
|
final class ModelLoader {
|
||||||
|
|
||||||
|
let content: Content
|
||||||
|
|
||||||
|
let storage: Storage
|
||||||
|
|
||||||
|
let context: LoadingContext
|
||||||
|
|
||||||
|
init(content: Content, storage: Storage) {
|
||||||
|
self.content = content
|
||||||
|
self.storage = storage
|
||||||
|
self.context = .init(content: content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func load() -> LoadingResult {
|
||||||
|
loadInternal()
|
||||||
|
return context.results()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadInternal() {
|
||||||
|
guard storage.contentScope != nil else {
|
||||||
|
context.error("Storage not initialized, not loading content")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFiles()
|
||||||
|
loadTags()
|
||||||
|
loadPages()
|
||||||
|
loadPosts()
|
||||||
|
loadSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadFiles() {
|
||||||
|
guard let files = storage.loadAllFiles() else {
|
||||||
|
context.error("Failed to load file list")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if files.isEmpty { print("No files loaded") }
|
||||||
|
|
||||||
|
files.forEach { (fileId, data) in
|
||||||
|
let fileData = data.data
|
||||||
|
let isExternal = data.isExternal
|
||||||
|
context.files[fileId] = FileResource(content: content, id: fileId, data: fileData, isExternalFile: isExternal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadTags() {
|
||||||
|
guard let tags = storage.loadAllTags() else {
|
||||||
|
context.error("Failed to load file tags")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tags.isEmpty { print("No tags loaded") }
|
||||||
|
|
||||||
|
tags.forEach { (tagId, data) in
|
||||||
|
context.tags[tagId] = Tag(context: context, id: tagId, data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadPages() {
|
||||||
|
guard let pages = storage.loadAllPages() else {
|
||||||
|
context.error("Failed to load file pages")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if pages.isEmpty { print("No pages loaded") }
|
||||||
|
|
||||||
|
pages.forEach { pageId, data in
|
||||||
|
context.pages[pageId] = Page(context: context, id: pageId, data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadPosts() {
|
||||||
|
guard let posts = storage.loadAllPosts() else {
|
||||||
|
context.error("Failed to load file posts")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if posts.isEmpty { print("No posts loaded") }
|
||||||
|
|
||||||
|
posts.forEach { postId, data in
|
||||||
|
context.posts[postId] = Post(context: context, id: postId, data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadSettings() {
|
||||||
|
guard let settings = storage.loadSettings() else {
|
||||||
|
context.error("Failed to load settings")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
context.tagOverview = settings.tagOverview.map { data in
|
||||||
|
TagOverview(context: context, id: "all-tags", data: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.settings = Settings(context: context, data: settings)
|
||||||
|
}
|
||||||
|
}
|
@ -35,13 +35,7 @@ final class LocalizedPage: ObservableObject {
|
|||||||
let originalUrl: String?
|
let originalUrl: String?
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var linkPreviewImage: FileResource?
|
var linkPreview: LinkPreview
|
||||||
|
|
||||||
@Published
|
|
||||||
var linkPreviewTitle: String?
|
|
||||||
|
|
||||||
@Published
|
|
||||||
var linkPreviewDescription: String?
|
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var hideTitle: Bool
|
var hideTitle: Bool
|
||||||
@ -51,18 +45,14 @@ final class LocalizedPage: ObservableObject {
|
|||||||
title: String,
|
title: String,
|
||||||
lastModified: Date? = nil,
|
lastModified: Date? = nil,
|
||||||
originalUrl: String? = nil,
|
originalUrl: String? = nil,
|
||||||
linkPreviewImage: FileResource? = nil,
|
linkPreview: LinkPreview = .init(),
|
||||||
linkPreviewTitle: String? = nil,
|
|
||||||
linkPreviewDescription: String? = nil,
|
|
||||||
hideTitle: Bool = false) {
|
hideTitle: Bool = false) {
|
||||||
self.content = content
|
self.content = content
|
||||||
self.urlString = urlString
|
self.urlString = urlString
|
||||||
self.title = title
|
self.title = title
|
||||||
self.lastModified = lastModified
|
self.lastModified = lastModified
|
||||||
self.originalUrl = originalUrl
|
self.originalUrl = originalUrl
|
||||||
self.linkPreviewImage = linkPreviewImage
|
self.linkPreview = linkPreview
|
||||||
self.linkPreviewTitle = linkPreviewTitle
|
|
||||||
self.linkPreviewDescription = linkPreviewDescription
|
|
||||||
self.hideTitle = hideTitle
|
self.hideTitle = hideTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +62,37 @@ final class LocalizedPage: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LocalizedPage: LinkPreviewItem {
|
|
||||||
|
|
||||||
|
extension LocalizedPage {
|
||||||
|
|
||||||
|
convenience init(context: LoadingContext, data: LocalizedPage.Data) {
|
||||||
|
self.init(
|
||||||
|
content: context.content,
|
||||||
|
urlString: data.url,
|
||||||
|
title: data.title,
|
||||||
|
lastModified: data.lastModifiedDate,
|
||||||
|
originalUrl: data.originalURL,
|
||||||
|
linkPreview: .init(context: context, data: data.linkPreview),
|
||||||
|
hideTitle: data.hideTitle ?? false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The structure to store the metadata of a localized page
|
||||||
|
struct Data: Codable {
|
||||||
|
let url: String
|
||||||
|
let title: String
|
||||||
|
let linkPreview: LinkPreview.Data
|
||||||
|
let lastModifiedDate: Date?
|
||||||
|
let originalURL: String?
|
||||||
|
let hideTitle: Bool?
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
.init(
|
||||||
|
url: urlString,
|
||||||
|
title: title,
|
||||||
|
linkPreview: linkPreview.data,
|
||||||
|
lastModifiedDate: lastModified,
|
||||||
|
originalURL: originalUrl,
|
||||||
|
hideTitle: hideTitle ? true : nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,7 @@ final class LocalizedPost: ObservableObject {
|
|||||||
var pageLinkText: String?
|
var pageLinkText: String?
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var linkPreviewImage: FileResource?
|
var linkPreview: LinkPreview
|
||||||
|
|
||||||
@Published
|
|
||||||
var linkPreviewTitle: String?
|
|
||||||
|
|
||||||
@Published
|
|
||||||
var linkPreviewDescription: String?
|
|
||||||
|
|
||||||
init(content: Content,
|
init(content: Content,
|
||||||
title: String? = nil,
|
title: String? = nil,
|
||||||
@ -36,41 +30,14 @@ final class LocalizedPost: ObservableObject {
|
|||||||
lastModified: Date? = nil,
|
lastModified: Date? = nil,
|
||||||
images: [FileResource] = [],
|
images: [FileResource] = [],
|
||||||
pageLinkText: String? = nil,
|
pageLinkText: String? = nil,
|
||||||
linkPreviewImage: FileResource? = nil,
|
linkPreview: LinkPreview = .init()) {
|
||||||
linkPreviewTitle: String? = nil,
|
|
||||||
linkPreviewDescription: String? = nil) {
|
|
||||||
self.content = content
|
self.content = content
|
||||||
self.title = title
|
self.title = title
|
||||||
self.text = text
|
self.text = text
|
||||||
self.lastModified = lastModified
|
self.lastModified = lastModified
|
||||||
self.images = images
|
self.images = images
|
||||||
self.pageLinkText = pageLinkText
|
self.pageLinkText = pageLinkText
|
||||||
self.linkPreviewImage = linkPreviewImage
|
self.linkPreview = linkPreview
|
||||||
self.linkPreviewTitle = linkPreviewTitle
|
|
||||||
self.linkPreviewDescription = linkPreviewDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
init(content: Content, file: LocalizedPostFile, images: [String : FileResource]) {
|
|
||||||
self.content = content
|
|
||||||
self.title = file.title
|
|
||||||
self.text = file.content
|
|
||||||
self.lastModified = file.lastModifiedDate
|
|
||||||
self.images = file.images.compactMap { images[$0] }
|
|
||||||
self.pageLinkText = file.pageLinkText
|
|
||||||
self.linkPreviewImage = file.linkPreviewImage.map { images[$0] }
|
|
||||||
self.linkPreviewTitle = file.linkPreviewTitle
|
|
||||||
self.linkPreviewDescription = file.linkPreviewDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
var postFile: LocalizedPostFile {
|
|
||||||
.init(images: images.map { $0.id },
|
|
||||||
title: title,
|
|
||||||
content: text,
|
|
||||||
lastModifiedDate: lastModified,
|
|
||||||
pageLinkText: pageLinkText,
|
|
||||||
linkPreviewImage: linkPreviewImage?.id,
|
|
||||||
linkPreviewTitle: linkPreviewTitle,
|
|
||||||
linkPreviewDescription: linkPreviewDescription)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func contains(_ string: String) -> Bool {
|
func contains(_ string: String) -> Bool {
|
||||||
@ -84,10 +51,41 @@ final class LocalizedPost: ObservableObject {
|
|||||||
if images.contains(file) {
|
if images.contains(file) {
|
||||||
images.remove(file)
|
images.remove(file)
|
||||||
}
|
}
|
||||||
remove(linkPreviewImage: file)
|
linkPreview.remove(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LocalizedPost: LinkPreviewItem {
|
// MARK: Storage
|
||||||
|
|
||||||
|
extension LocalizedPost {
|
||||||
|
|
||||||
|
convenience init(context: LoadingContext, data: Data) {
|
||||||
|
self.init(
|
||||||
|
content: context.content,
|
||||||
|
title: data.title,
|
||||||
|
text: data.text,
|
||||||
|
lastModified: data.lastModifiedDate,
|
||||||
|
images: data.images.compactMap(context.image),
|
||||||
|
pageLinkText: data.pageLinkText,
|
||||||
|
linkPreview: .init(context: context, data: data.linkPreview))
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
.init(images: images.map { $0.id },
|
||||||
|
title: title,
|
||||||
|
text: text,
|
||||||
|
lastModifiedDate: lastModified,
|
||||||
|
pageLinkText: pageLinkText,
|
||||||
|
linkPreview: linkPreview.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The structure to store the metadata of a localized post
|
||||||
|
struct Data: Codable {
|
||||||
|
let images: [String]
|
||||||
|
let title: String?
|
||||||
|
let text: String
|
||||||
|
let lastModifiedDate: Date?
|
||||||
|
let pageLinkText: String?
|
||||||
|
let linkPreview: LinkPreview.Data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,14 +12,7 @@ final class LocalizedTag: ObservableObject {
|
|||||||
var name: String
|
var name: String
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var linkPreviewTitle: String?
|
var linkPreview: LinkPreview
|
||||||
|
|
||||||
@Published
|
|
||||||
var linkPreviewDescription: String?
|
|
||||||
|
|
||||||
/// The image id of the thumbnail
|
|
||||||
@Published
|
|
||||||
var linkPreviewImage: FileResource?
|
|
||||||
|
|
||||||
/// The original url in the previous site layout
|
/// The original url in the previous site layout
|
||||||
let originalUrl: String?
|
let originalUrl: String?
|
||||||
@ -27,42 +20,51 @@ final class LocalizedTag: ObservableObject {
|
|||||||
init(content: Content,
|
init(content: Content,
|
||||||
urlComponent: String,
|
urlComponent: String,
|
||||||
name: String,
|
name: String,
|
||||||
linkPreviewTitle: String? = nil,
|
linkPreview: LinkPreview = .init(),
|
||||||
linkPreviewDescription: String? = nil,
|
|
||||||
linkPreviewImage: FileResource? = nil,
|
|
||||||
originalUrl: String? = nil) {
|
originalUrl: String? = nil) {
|
||||||
self.content = content
|
self.content = content
|
||||||
self.urlComponent = urlComponent
|
self.urlComponent = urlComponent
|
||||||
self.name = name
|
self.name = name
|
||||||
self.linkPreviewTitle = linkPreviewTitle
|
self.linkPreview = linkPreview
|
||||||
self.linkPreviewDescription = linkPreviewDescription
|
|
||||||
self.linkPreviewImage = linkPreviewImage
|
|
||||||
self.originalUrl = originalUrl
|
self.originalUrl = originalUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValid(urlComponent: String) -> Bool {
|
func isValid(urlComponent: String) -> Bool {
|
||||||
|
!urlComponent.isEmpty &&
|
||||||
content.isValidIdForTagOrPageOrPost(urlComponent) &&
|
content.isValidIdForTagOrPageOrPost(urlComponent) &&
|
||||||
!content.containsTag(withUrlComponent: urlComponent)
|
!content.containsTag(withUrlComponent: urlComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The title to display when considering multiple items of this tag
|
/// The title to display when considering multiple items of this tag
|
||||||
var title: String {
|
var title: String {
|
||||||
linkPreviewTitle ?? name
|
linkPreview.title ?? name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LocalizedTag: LinkPreviewItem {
|
// MARK: Storage
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedTag {
|
extension LocalizedTag {
|
||||||
|
|
||||||
var tagFile: LocalizedTagFile {
|
convenience init(context: LoadingContext, data: Data) {
|
||||||
|
self.init(
|
||||||
|
content: context.content,
|
||||||
|
urlComponent: data.urlComponent,
|
||||||
|
name: data.name,
|
||||||
|
linkPreview: .init(context: context, data: data.linkPreview),
|
||||||
|
originalUrl: data.originalUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let urlComponent: String
|
||||||
|
let name: String
|
||||||
|
let linkPreview: LinkPreview.Data
|
||||||
|
let originalUrl: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
.init(urlComponent: urlComponent,
|
.init(urlComponent: urlComponent,
|
||||||
name: name,
|
name: name,
|
||||||
linkPreviewTitle: linkPreviewTitle,
|
linkPreview: linkPreview.data,
|
||||||
linkPreviewDescription: linkPreviewDescription,
|
originalUrl: originalUrl)
|
||||||
linkPreviewImage: linkPreviewImage?.id,
|
|
||||||
originalURL: originalUrl)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class Page: Item {
|
final class Page: Item, DateItem, LocalizedItem {
|
||||||
|
|
||||||
|
override var itemType: ItemType { .page }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The external link this page points to.
|
The external link this page points to.
|
||||||
@ -38,9 +40,7 @@ final class Page: Item {
|
|||||||
@Published
|
@Published
|
||||||
var tags: [Tag]
|
var tags: [Tag]
|
||||||
|
|
||||||
/**
|
/// Additional files to copy, because the page content references them
|
||||||
Additional files to copy, because the page content references them
|
|
||||||
*/
|
|
||||||
@Published
|
@Published
|
||||||
var requiredFiles: [FileResource]
|
var requiredFiles: [FileResource]
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ final class Page: Item {
|
|||||||
content.settings.paths.pagesOutputFolderPath + "/" + localized(in: language).urlString
|
content.settings.paths.pagesOutputFolderPath + "/" + localized(in: language).urlString
|
||||||
}
|
}
|
||||||
|
|
||||||
override var itemType: ItemType {
|
override var itemReference: ItemReference {
|
||||||
.page(self)
|
.page(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,15 +161,57 @@ final class Page: Item {
|
|||||||
if requiredFiles.contains(file) {
|
if requiredFiles.contains(file) {
|
||||||
requiredFiles.remove(file)
|
requiredFiles.remove(file)
|
||||||
}
|
}
|
||||||
english.remove(linkPreviewImage: file)
|
english.linkPreview.remove(file)
|
||||||
german.remove(linkPreviewImage: file)
|
german.linkPreview.remove(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Page: DateItem {
|
// MARK: Storage
|
||||||
|
|
||||||
|
extension Page {
|
||||||
|
|
||||||
|
convenience init(context: LoadingContext, id: String, data: Data) {
|
||||||
|
self.init(
|
||||||
|
content: context.content,
|
||||||
|
id: id,
|
||||||
|
externalLink: data.externalLink,
|
||||||
|
isDraft: data.isDraft,
|
||||||
|
createdDate: data.createdDate,
|
||||||
|
hideDate: data.hideDate ?? false,
|
||||||
|
startDate: data.startDate,
|
||||||
|
endDate: data.endDate,
|
||||||
|
german: .init(context: context, data: data.german),
|
||||||
|
english: .init(context: context, data: data.english),
|
||||||
|
tags: data.tags.compactMap(context.tag),
|
||||||
|
requiredFiles: data.requiredFiles?.compactMap(context.file) ?? [])
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Page: LocalizedItem {
|
/// The structure to store the metadata of a page on disk
|
||||||
|
struct Data: Codable {
|
||||||
|
let isDraft: Bool
|
||||||
|
let externalLink: String?
|
||||||
|
let tags: [String]
|
||||||
|
let hideDate: Bool?
|
||||||
|
let createdDate: Date
|
||||||
|
let startDate: Date
|
||||||
|
let endDate: Date?
|
||||||
|
let german: LocalizedPage.Data
|
||||||
|
let english: LocalizedPage.Data
|
||||||
|
let requiredFiles: [String]?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
.init(
|
||||||
|
isDraft: isDraft,
|
||||||
|
externalLink: externalLink,
|
||||||
|
tags: tags.map { $0.id },
|
||||||
|
hideDate: hideDate ? true : nil,
|
||||||
|
createdDate: createdDate,
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: hasEndDate ? endDate : nil,
|
||||||
|
german: german.data,
|
||||||
|
english: english.data,
|
||||||
|
requiredFiles: requiredFiles.nonEmpty?.map { $0.id }.sorted())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class Post: Item {
|
final class Post: Item, DateItem, LocalizedItem {
|
||||||
|
|
||||||
|
override var itemType: ItemType { .post }
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var isDraft: Bool
|
var isDraft: Bool
|
||||||
@ -142,10 +144,42 @@ final class Post: Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Post: DateItem {
|
extension Post {
|
||||||
|
|
||||||
|
convenience init(context: LoadingContext, id: String, data: Data) {
|
||||||
|
self.init(
|
||||||
|
content: context.content,
|
||||||
|
id: id,
|
||||||
|
isDraft: data.isDraft,
|
||||||
|
createdDate: data.createdDate,
|
||||||
|
startDate: data.startDate,
|
||||||
|
endDate: data.endDate,
|
||||||
|
tags: data.tags.compactMap(context.tag),
|
||||||
|
german: .init(context: context, data: data.german),
|
||||||
|
english: .init(context: context, data: data.english),
|
||||||
|
linkedPage: data.linkedPageId.map(context.page))
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Post: LocalizedItem {
|
struct Data: Codable {
|
||||||
|
let isDraft: Bool
|
||||||
|
let createdDate: Date
|
||||||
|
let startDate: Date
|
||||||
|
let endDate: Date?
|
||||||
|
let tags: [String]
|
||||||
|
let german: LocalizedPost.Data
|
||||||
|
let english: LocalizedPost.Data
|
||||||
|
let linkedPageId: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
.init(
|
||||||
|
isDraft: isDraft,
|
||||||
|
createdDate: createdDate,
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: hasEndDate ? endDate : nil,
|
||||||
|
tags: tags.map { $0.id },
|
||||||
|
german: german.data,
|
||||||
|
english: english.data,
|
||||||
|
linkedPageId: linkedPage?.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class AudioPlayerSettings: ObservableObject {
|
final class AudioPlayerSettings: ObservableObject, LocalizedItem {
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var playlistCoverImageSize: Int
|
var playlistCoverImageSize: Int
|
||||||
@ -34,24 +34,6 @@ final class AudioPlayerSettings: ObservableObject {
|
|||||||
self.english = english
|
self.english = english
|
||||||
}
|
}
|
||||||
|
|
||||||
init(file: AudioPlayerSettingsFile, files: [String : FileResource]) {
|
|
||||||
self.playlistCoverImageSize = file.playlistCoverImageSize
|
|
||||||
self.smallCoverImageSize = file.smallCoverImageSize
|
|
||||||
self.audioPlayerJsFile = file.audioPlayerJsFile.map { files[$0] }
|
|
||||||
self.audioPlayerCssFile = file.audioPlayerCssFile.map { files[$0] }
|
|
||||||
self.german = .init(file: file.german)
|
|
||||||
self.english = .init(file: file.english)
|
|
||||||
}
|
|
||||||
|
|
||||||
var file: AudioPlayerSettingsFile {
|
|
||||||
.init(playlistCoverImageSize: playlistCoverImageSize,
|
|
||||||
smallCoverImageSize: smallCoverImageSize,
|
|
||||||
audioPlayerJsFile: audioPlayerJsFile?.id,
|
|
||||||
audioPlayerCssFile: audioPlayerCssFile?.id,
|
|
||||||
german: german.file,
|
|
||||||
english: english.file)
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove(_ file: FileResource) {
|
func remove(_ file: FileResource) {
|
||||||
if audioPlayerJsFile == file {
|
if audioPlayerJsFile == file {
|
||||||
audioPlayerJsFile = nil
|
audioPlayerJsFile = nil
|
||||||
@ -62,17 +44,37 @@ final class AudioPlayerSettings: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Storage
|
||||||
|
|
||||||
extension AudioPlayerSettings {
|
extension AudioPlayerSettings {
|
||||||
|
|
||||||
static let `default`: AudioPlayerSettings = .init(
|
convenience init(context: LoadingContext, data: Data) {
|
||||||
playlistCoverImageSize: 280,
|
self.init(
|
||||||
smallCoverImageSize: 78,
|
playlistCoverImageSize: data.playlistCoverImageSize,
|
||||||
audioPlayerJsFile: nil,
|
smallCoverImageSize: data.smallCoverImageSize,
|
||||||
audioPlayerCssFile: nil,
|
audioPlayerJsFile: data.audioPlayerJsFile.map(context.file),
|
||||||
german: .init(playlistText: "Wiedergabeliste"),
|
audioPlayerCssFile: data.audioPlayerCssFile.map(context.file),
|
||||||
english: .init(playlistText: "Playlist"))
|
german: .init(data: data.german),
|
||||||
|
english: .init(data: data.english))
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
.init(playlistCoverImageSize: playlistCoverImageSize,
|
||||||
|
smallCoverImageSize: smallCoverImageSize,
|
||||||
|
audioPlayerJsFile: audioPlayerJsFile?.id,
|
||||||
|
audioPlayerCssFile: audioPlayerCssFile?.id,
|
||||||
|
german: german.data,
|
||||||
|
english: english.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let playlistCoverImageSize: Int
|
||||||
|
let smallCoverImageSize: Int
|
||||||
|
let audioPlayerJsFile: String?
|
||||||
|
let audioPlayerCssFile: String?
|
||||||
|
let german: LocalizedAudioPlayerSettings.Data
|
||||||
|
let english: LocalizedAudioPlayerSettings.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AudioPlayerSettings: LocalizedItem {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,21 @@ final class LocalizedAudioPlayerSettings: ObservableObject {
|
|||||||
init(playlistText: String) {
|
init(playlistText: String) {
|
||||||
self.playlistText = playlistText
|
self.playlistText = playlistText
|
||||||
}
|
}
|
||||||
|
|
||||||
init(file: LocalizedAudioPlayerSettingsFile) {
|
|
||||||
self.playlistText = file.playlistText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var file: LocalizedAudioPlayerSettingsFile {
|
// MARK: Storage
|
||||||
|
|
||||||
|
extension LocalizedAudioPlayerSettings {
|
||||||
|
|
||||||
|
convenience init(data: Data) {
|
||||||
|
self.init(playlistText: data.playlistText)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
.init(playlistText: playlistText)
|
.init(playlistText: playlistText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let playlistText: String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,21 @@ final class LocalizedNavigationSettings: ObservableObject {
|
|||||||
init(rootUrl: String) {
|
init(rootUrl: String) {
|
||||||
self.rootUrl = rootUrl
|
self.rootUrl = rootUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
init(file: LocalizedNavigationSettingsFile) {
|
|
||||||
self.rootUrl = file.rootUrl
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var file: LocalizedNavigationSettingsFile {
|
// MARK: Storage
|
||||||
|
|
||||||
|
extension LocalizedNavigationSettings {
|
||||||
|
|
||||||
|
convenience init(data: Data) {
|
||||||
|
self.init(rootUrl: data.rootUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let rootUrl: String
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
.init(rootUrl: rootUrl)
|
.init(rootUrl: rootUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,25 @@ final class LocalizedPageSettings: ObservableObject {
|
|||||||
self.emptyPageTitle = emptyPageTitle
|
self.emptyPageTitle = emptyPageTitle
|
||||||
self.emptyPageText = emptyPageText
|
self.emptyPageText = emptyPageText
|
||||||
}
|
}
|
||||||
|
|
||||||
init(file: LocalizedPageSettingsFile) {
|
|
||||||
self.emptyPageTitle = file.emptyPageTitle
|
|
||||||
self.emptyPageText = file.emptyPageText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var file: LocalizedPageSettingsFile {
|
// MARK: Storage
|
||||||
|
|
||||||
|
extension LocalizedPageSettings {
|
||||||
|
|
||||||
|
convenience init(data: Data) {
|
||||||
|
self.init(
|
||||||
|
emptyPageTitle: data.emptyPageTitle,
|
||||||
|
emptyPageText: data.emptyPageText)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
.init(emptyPageTitle: emptyPageTitle,
|
.init(emptyPageTitle: emptyPageTitle,
|
||||||
emptyPageText: emptyPageText)
|
emptyPageText: emptyPageText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let emptyPageTitle: String
|
||||||
|
let emptyPageText: String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,23 @@ import Foundation
|
|||||||
|
|
||||||
final class LocalizedPostSettings: ObservableObject {
|
final class LocalizedPostSettings: ObservableObject {
|
||||||
|
|
||||||
|
/// The page title for the post feed
|
||||||
@Published
|
@Published
|
||||||
var title: String
|
var title: String
|
||||||
|
|
||||||
|
/// The page description for the post feed
|
||||||
@Published
|
@Published
|
||||||
var description: String
|
var description: String
|
||||||
|
|
||||||
|
/// The path to the feed in the final website, appended with the page number
|
||||||
@Published
|
@Published
|
||||||
var feedUrlPrefix: String
|
var feedUrlPrefix: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
The text to display when linking to a page
|
||||||
|
|
||||||
|
Each post may define a custom text.
|
||||||
|
*/
|
||||||
@Published
|
@Published
|
||||||
var defaultPageLinkText: String
|
var defaultPageLinkText: String
|
||||||
|
|
||||||
@ -20,21 +28,32 @@ final class LocalizedPostSettings: ObservableObject {
|
|||||||
self.feedUrlPrefix = feedUrlPrefix
|
self.feedUrlPrefix = feedUrlPrefix
|
||||||
self.defaultPageLinkText = defaultPageLinkText
|
self.defaultPageLinkText = defaultPageLinkText
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Storage
|
// MARK: Storage
|
||||||
|
|
||||||
init(file: LocalizedPostSettingsFile) {
|
extension LocalizedPostSettings {
|
||||||
self.title = file.feedTitle
|
|
||||||
self.description = file.feedDescription
|
convenience init(data: Data) {
|
||||||
self.feedUrlPrefix = file.feedUrlPrefix
|
self.init(
|
||||||
self.defaultPageLinkText = file.defaultPageLinkText ?? "View"
|
title: data.feedTitle,
|
||||||
|
description: data.feedDescription,
|
||||||
|
feedUrlPrefix: data.feedUrlPrefix,
|
||||||
|
defaultPageLinkText: data.defaultPageLinkText)
|
||||||
}
|
}
|
||||||
|
|
||||||
var file: LocalizedPostSettingsFile {
|
var data: Data {
|
||||||
.init(
|
.init(
|
||||||
feedTitle: title,
|
feedTitle: title,
|
||||||
feedDescription: description,
|
feedDescription: description,
|
||||||
feedUrlPrefix: feedUrlPrefix,
|
feedUrlPrefix: feedUrlPrefix,
|
||||||
defaultPageLinkText: defaultPageLinkText)
|
defaultPageLinkText: defaultPageLinkText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let feedTitle: String
|
||||||
|
let feedDescription: String
|
||||||
|
let feedUrlPrefix: String
|
||||||
|
let defaultPageLinkText: String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class NavigationSettings: ObservableObject {
|
final class NavigationSettings: ObservableObject, LocalizedItem {
|
||||||
|
|
||||||
/// The items to show in the navigation bar
|
/// The items to show in the navigation bar
|
||||||
@Published
|
@Published
|
||||||
@ -19,25 +19,33 @@ final class NavigationSettings: ObservableObject {
|
|||||||
self.german = german
|
self.german = german
|
||||||
self.english = english
|
self.english = english
|
||||||
}
|
}
|
||||||
|
|
||||||
init(file: NavigationSettingsFile, map: (String) -> Item?) {
|
|
||||||
self.navigationItems = file.navigationItems.compactMap(map)
|
|
||||||
self.german = LocalizedNavigationSettings(file: file.german)
|
|
||||||
self.english = LocalizedNavigationSettings(file: file.english)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var file: NavigationSettingsFile {
|
// MARK: Storage
|
||||||
|
|
||||||
|
extension NavigationSettings {
|
||||||
|
|
||||||
|
convenience init(context: LoadingContext, data: NavigationSettings.Data) {
|
||||||
|
self.init(
|
||||||
|
navigationItems: data.navigationItems.compactMap(context.item),
|
||||||
|
german: LocalizedNavigationSettings(data: data.german),
|
||||||
|
english: LocalizedNavigationSettings(data: data.english))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let navigationItems: [ItemId]
|
||||||
|
let german: LocalizedNavigationSettings.Data
|
||||||
|
let english: LocalizedNavigationSettings.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
.init(
|
.init(
|
||||||
navigationItems: navigationItems.map { $0.itemType.id },
|
navigationItems: navigationItems.map { $0.itemId },
|
||||||
german: german.file,
|
german: german.data,
|
||||||
english: english.file)
|
english: english.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NavigationSettings: LocalizedItem {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NavigationSettings {
|
extension NavigationSettings {
|
||||||
|
|
||||||
static var `default`: NavigationSettings {
|
static var `default`: NavigationSettings {
|
||||||
|
@ -32,30 +32,26 @@ final class PageSettings: ObservableObject {
|
|||||||
@Published
|
@Published
|
||||||
var english: LocalizedPageSettings
|
var english: LocalizedPageSettings
|
||||||
|
|
||||||
init(file: PageSettingsFile, files: [String : FileResource]) {
|
init(contentWidth: Int,
|
||||||
self.contentWidth = file.contentWidth
|
largeImageWidth: Int,
|
||||||
self.largeImageWidth = file.largeImageWidth
|
pageLinkImageSize: Int,
|
||||||
self.pageLinkImageSize = file.pageLinkImageSize
|
defaultCssFile: FileResource? = nil,
|
||||||
self.defaultCssFile = file.defaultCssFile.map { files[$0] }
|
codeHighlightingJsFile: FileResource? = nil,
|
||||||
self.codeHighlightingJsFile = file.codeHighlightingJsFile.map { files[$0] }
|
modelViewerJsFile: FileResource? = nil,
|
||||||
self.modelViewerJsFile = file.modelViewerJsFile.map { files[$0] }
|
imageCompareJsFile: FileResource? = nil,
|
||||||
self.imageCompareCssFile = file.imageCompareCssFile.map { files[$0] }
|
imageCompareCssFile: FileResource? = nil,
|
||||||
self.imageCompareJsFile = file.imageCompareJsFile.map { files[$0] }
|
german: LocalizedPageSettings,
|
||||||
self.german = .init(file: file.german)
|
english: LocalizedPageSettings) {
|
||||||
self.english = .init(file: file.english)
|
self.contentWidth = contentWidth
|
||||||
}
|
self.largeImageWidth = largeImageWidth
|
||||||
|
self.pageLinkImageSize = pageLinkImageSize
|
||||||
var file: PageSettingsFile {
|
self.defaultCssFile = defaultCssFile
|
||||||
.init(contentWidth: contentWidth,
|
self.codeHighlightingJsFile = codeHighlightingJsFile
|
||||||
largeImageWidth: largeImageWidth,
|
self.modelViewerJsFile = modelViewerJsFile
|
||||||
pageLinkImageSize: pageLinkImageSize,
|
self.imageCompareJsFile = imageCompareJsFile
|
||||||
defaultCssFile: defaultCssFile?.id,
|
self.imageCompareCssFile = imageCompareCssFile
|
||||||
codeHighlightingJsFile: codeHighlightingJsFile?.id,
|
self.german = german
|
||||||
modelViewerJsFile: modelViewerJsFile?.id,
|
self.english = english
|
||||||
imageCompareJsFile: imageCompareJsFile?.id,
|
|
||||||
imageCompareCssFile: imageCompareCssFile?.id,
|
|
||||||
german: german.file,
|
|
||||||
english: english.file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func remove(_ file: FileResource) {
|
func remove(_ file: FileResource) {
|
||||||
@ -77,6 +73,52 @@ final class PageSettings: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Storage
|
||||||
|
|
||||||
|
extension PageSettings {
|
||||||
|
|
||||||
|
convenience init(context: LoadingContext, data: Data) {
|
||||||
|
self.init(
|
||||||
|
contentWidth: data.contentWidth,
|
||||||
|
largeImageWidth: data.largeImageWidth,
|
||||||
|
pageLinkImageSize: data.pageLinkImageSize,
|
||||||
|
defaultCssFile: data.defaultCssFile.map(context.file),
|
||||||
|
codeHighlightingJsFile: data.codeHighlightingJsFile.map(context.file),
|
||||||
|
modelViewerJsFile: data.modelViewerJsFile.map(context.file),
|
||||||
|
imageCompareJsFile: data.imageCompareJsFile.map(context.file),
|
||||||
|
imageCompareCssFile: data.imageCompareCssFile.map(context.file),
|
||||||
|
german: .init(data: data.german),
|
||||||
|
english: .init(data: data.english))
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
.init(contentWidth: contentWidth,
|
||||||
|
largeImageWidth: largeImageWidth,
|
||||||
|
pageLinkImageSize: pageLinkImageSize,
|
||||||
|
defaultCssFile: defaultCssFile?.id,
|
||||||
|
codeHighlightingJsFile: codeHighlightingJsFile?.id,
|
||||||
|
modelViewerJsFile: modelViewerJsFile?.id,
|
||||||
|
imageCompareJsFile: imageCompareJsFile?.id,
|
||||||
|
imageCompareCssFile: imageCompareCssFile?.id,
|
||||||
|
german: german.data,
|
||||||
|
english: english.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let contentWidth: Int
|
||||||
|
let largeImageWidth: Int
|
||||||
|
let pageLinkImageSize: Int
|
||||||
|
let defaultCssFile: String?
|
||||||
|
let codeHighlightingJsFile: String?
|
||||||
|
let modelViewerJsFile: String?
|
||||||
|
let imageCompareJsFile: String?
|
||||||
|
let imageCompareCssFile: String?
|
||||||
|
let german: LocalizedPageSettings.Data
|
||||||
|
let english: LocalizedPageSettings.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extension PageSettings: LocalizedItem {
|
extension PageSettings: LocalizedItem {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,23 +23,54 @@ final class PathSettings: ObservableObject {
|
|||||||
@Published
|
@Published
|
||||||
var tagsOutputFolderPath: String
|
var tagsOutputFolderPath: String
|
||||||
|
|
||||||
init(file: PathSettingsFile) {
|
init(assetsOutputFolderPath: String,
|
||||||
self.assetsOutputFolderPath = file.assetsOutputFolderPath
|
pagesOutputFolderPath: String,
|
||||||
self.pagesOutputFolderPath = file.pagesOutputFolderPath
|
imagesOutputFolderPath: String,
|
||||||
self.imagesOutputFolderPath = file.imagesOutputFolderPath
|
filesOutputFolderPath: String,
|
||||||
self.filesOutputFolderPath = file.filesOutputFolderPath
|
videosOutputFolderPath: String,
|
||||||
self.videosOutputFolderPath = file.videosOutputFolderPath
|
audioOutputFolderPath: String,
|
||||||
self.tagsOutputFolderPath = file.tagsOutputFolderPath
|
tagsOutputFolderPath: String) {
|
||||||
self.audioOutputFolderPath = file.audioOutputFolderPath
|
self.assetsOutputFolderPath = assetsOutputFolderPath
|
||||||
|
self.pagesOutputFolderPath = pagesOutputFolderPath
|
||||||
|
self.imagesOutputFolderPath = imagesOutputFolderPath
|
||||||
|
self.filesOutputFolderPath = filesOutputFolderPath
|
||||||
|
self.videosOutputFolderPath = videosOutputFolderPath
|
||||||
|
self.audioOutputFolderPath = audioOutputFolderPath
|
||||||
|
self.tagsOutputFolderPath = tagsOutputFolderPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var file: PathSettingsFile {
|
extension PathSettings {
|
||||||
.init(assetsOutputFolderPath: assetsOutputFolderPath,
|
|
||||||
|
convenience init(data: Data) {
|
||||||
|
self.init(
|
||||||
|
assetsOutputFolderPath: data.assetsOutputFolderPath,
|
||||||
|
pagesOutputFolderPath: data.pagesOutputFolderPath,
|
||||||
|
imagesOutputFolderPath: data.imagesOutputFolderPath,
|
||||||
|
filesOutputFolderPath: data.filesOutputFolderPath,
|
||||||
|
videosOutputFolderPath: data.videosOutputFolderPath,
|
||||||
|
audioOutputFolderPath: data.audioOutputFolderPath,
|
||||||
|
tagsOutputFolderPath: data.tagsOutputFolderPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
.init(
|
||||||
|
assetsOutputFolderPath: assetsOutputFolderPath,
|
||||||
pagesOutputFolderPath: pagesOutputFolderPath,
|
pagesOutputFolderPath: pagesOutputFolderPath,
|
||||||
imagesOutputFolderPath: imagesOutputFolderPath,
|
imagesOutputFolderPath: imagesOutputFolderPath,
|
||||||
filesOutputFolderPath: filesOutputFolderPath,
|
filesOutputFolderPath: filesOutputFolderPath,
|
||||||
videosOutputFolderPath: videosOutputFolderPath,
|
videosOutputFolderPath: videosOutputFolderPath,
|
||||||
tagsOutputFolderPath: tagsOutputFolderPath,
|
audioOutputFolderPath: audioOutputFolderPath,
|
||||||
audioOutputFolderPath: audioOutputFolderPath)
|
tagsOutputFolderPath: tagsOutputFolderPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let assetsOutputFolderPath: String
|
||||||
|
let pagesOutputFolderPath: String
|
||||||
|
let imagesOutputFolderPath: String
|
||||||
|
let filesOutputFolderPath: String
|
||||||
|
let videosOutputFolderPath: String
|
||||||
|
let audioOutputFolderPath: String
|
||||||
|
let tagsOutputFolderPath: String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class PostSettings: ObservableObject {
|
final class PostSettings: ObservableObject, LocalizedItem {
|
||||||
|
|
||||||
/// The number of posts to show in a single page of the news feed
|
/// The number of posts to show in a single page of the news feed
|
||||||
@Published
|
@Published
|
||||||
@ -41,28 +41,6 @@ final class PostSettings: ObservableObject {
|
|||||||
self.english = english
|
self.english = english
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Storage
|
|
||||||
|
|
||||||
init(file: PostSettingsFile, files: [String : FileResource]) {
|
|
||||||
self.postsPerPage = file.postsPerPage
|
|
||||||
self.contentWidth = file.contentWidth
|
|
||||||
self.swiperCssFile = file.swiperCssFile.map { files[$0] }
|
|
||||||
self.swiperJsFile = file.swiperJsFile.map { files[$0] }
|
|
||||||
self.defaultCssFile = file.defaultCssFile.map { files[$0] }
|
|
||||||
self.german = .init(file: file.german)
|
|
||||||
self.english = .init(file: file.english)
|
|
||||||
}
|
|
||||||
|
|
||||||
var file: PostSettingsFile {
|
|
||||||
.init(postsPerPage: postsPerPage,
|
|
||||||
contentWidth: contentWidth,
|
|
||||||
swiperCssFile: swiperCssFile?.id,
|
|
||||||
swiperJsFile: swiperJsFile?.id,
|
|
||||||
defaultCssFile: defaultCssFile?.id,
|
|
||||||
german: german.file,
|
|
||||||
english: english.file)
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove(_ file: FileResource) {
|
func remove(_ file: FileResource) {
|
||||||
if swiperJsFile == file {
|
if swiperJsFile == file {
|
||||||
swiperJsFile = nil
|
swiperJsFile = nil
|
||||||
@ -76,13 +54,38 @@ final class PostSettings: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Storage
|
||||||
|
|
||||||
extension PostSettings {
|
extension PostSettings {
|
||||||
|
|
||||||
static var `default`: PostSettings {
|
convenience init(context: LoadingContext, data: Data) {
|
||||||
.init(file: .default, files: [:])
|
self.init(
|
||||||
}
|
postsPerPage: data.postsPerPage,
|
||||||
|
contentWidth: data.contentWidth,
|
||||||
|
swiperCssFile: data.swiperCssFile.map(context.file),
|
||||||
|
swiperJsFile: data.swiperJsFile.map(context.file),
|
||||||
|
defaultCssFile: data.defaultCssFile.map(context.file),
|
||||||
|
german: .init(data: data.german),
|
||||||
|
english: .init(data: data.english))
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PostSettings: LocalizedItem {
|
var data: PostSettings.Data {
|
||||||
|
.init(postsPerPage: postsPerPage,
|
||||||
|
contentWidth: contentWidth,
|
||||||
|
swiperCssFile: swiperCssFile?.id,
|
||||||
|
swiperJsFile: swiperJsFile?.id,
|
||||||
|
defaultCssFile: defaultCssFile?.id,
|
||||||
|
german: german.data,
|
||||||
|
english: english.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let postsPerPage: Int
|
||||||
|
let contentWidth: Int
|
||||||
|
let swiperCssFile: String?
|
||||||
|
let swiperJsFile: String?
|
||||||
|
let defaultCssFile: String?
|
||||||
|
let german: LocalizedPostSettings.Data
|
||||||
|
let english: LocalizedPostSettings.Data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,25 +30,6 @@ final class Settings: ObservableObject {
|
|||||||
self.audioPlayer = audioPlayer
|
self.audioPlayer = audioPlayer
|
||||||
}
|
}
|
||||||
|
|
||||||
init(file: SettingsFile, files: [String : FileResource], map: (String) -> Item?) {
|
|
||||||
self.navigation = NavigationSettings(file: file.navigation, map: map)
|
|
||||||
|
|
||||||
self.posts = PostSettings(file: file.posts, files: files)
|
|
||||||
self.pages = PageSettings(file: file.pages, files: files)
|
|
||||||
self.paths = PathSettings(file: file.paths)
|
|
||||||
self.audioPlayer = .init(file: file.audioPlayer, files: files)
|
|
||||||
}
|
|
||||||
|
|
||||||
func file(tagOverview: TagOverviewPage?) -> SettingsFile {
|
|
||||||
.init(
|
|
||||||
paths: paths.file,
|
|
||||||
navigation: navigation.file,
|
|
||||||
posts: posts.file,
|
|
||||||
pages: pages.file,
|
|
||||||
audioPlayer: audioPlayer.file,
|
|
||||||
tagOverview: tagOverview?.file)
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove(_ file: FileResource) {
|
func remove(_ file: FileResource) {
|
||||||
pages.remove(file)
|
pages.remove(file)
|
||||||
posts.remove(file)
|
posts.remove(file)
|
||||||
@ -56,6 +37,39 @@ final class Settings: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Storage
|
||||||
|
|
||||||
|
extension Settings {
|
||||||
|
|
||||||
|
convenience init(context: LoadingContext, data: Settings.Data) {
|
||||||
|
self.init(
|
||||||
|
paths: .init(data: data.paths),
|
||||||
|
navigation: .init(context: context, data: data.navigation),
|
||||||
|
posts: .init(context: context, data: data.posts),
|
||||||
|
pages: .init(context: context, data: data.pages),
|
||||||
|
audioPlayer: .init(context: context, data: data.audioPlayer))
|
||||||
|
}
|
||||||
|
|
||||||
|
func data(tagOverview: Tag?) -> Data {
|
||||||
|
.init(
|
||||||
|
paths: paths.data,
|
||||||
|
navigation: navigation.data,
|
||||||
|
posts: posts.data,
|
||||||
|
pages: pages.data,
|
||||||
|
audioPlayer: audioPlayer.data,
|
||||||
|
tagOverview: tagOverview?.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
let paths: PathSettings.Data
|
||||||
|
let navigation: NavigationSettings.Data
|
||||||
|
let posts: PostSettings.Data
|
||||||
|
let pages: PageSettings.Data
|
||||||
|
let audioPlayer: AudioPlayerSettings.Data
|
||||||
|
let tagOverview: Tag.Data?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Settings {
|
extension Settings {
|
||||||
|
|
||||||
static let `default`: Settings = .init(
|
static let `default`: Settings = .init(
|
||||||
@ -65,3 +79,70 @@ extension Settings {
|
|||||||
pages: .default,
|
pages: .default,
|
||||||
audioPlayer: .default)
|
audioPlayer: .default)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension AudioPlayerSettings {
|
||||||
|
|
||||||
|
static let `default`: AudioPlayerSettings = .init(
|
||||||
|
playlistCoverImageSize: 280,
|
||||||
|
smallCoverImageSize: 78,
|
||||||
|
audioPlayerJsFile: nil,
|
||||||
|
audioPlayerCssFile: nil,
|
||||||
|
german: .init(playlistText: "Wiedergabeliste"),
|
||||||
|
english: .init(playlistText: "Playlist"))
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PostSettings {
|
||||||
|
|
||||||
|
static var `default`: PostSettings {
|
||||||
|
.init(postsPerPage: 25,
|
||||||
|
contentWidth: 600,
|
||||||
|
swiperCssFile: nil,
|
||||||
|
swiperJsFile: nil,
|
||||||
|
defaultCssFile: nil,
|
||||||
|
german: .init(
|
||||||
|
title: "Beiträge",
|
||||||
|
description: "Alle Beiträge",
|
||||||
|
feedUrlPrefix: "blog",
|
||||||
|
defaultPageLinkText: "Anzeigen"),
|
||||||
|
english: .init(
|
||||||
|
title: "Blog posts",
|
||||||
|
description: "All blog posts",
|
||||||
|
feedUrlPrefix: "blog",
|
||||||
|
defaultPageLinkText: "View"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PathSettings {
|
||||||
|
|
||||||
|
static var `default`: PathSettings {
|
||||||
|
.init(
|
||||||
|
assetsOutputFolderPath: "asset",
|
||||||
|
pagesOutputFolderPath: "page",
|
||||||
|
imagesOutputFolderPath: "image",
|
||||||
|
filesOutputFolderPath: "file",
|
||||||
|
videosOutputFolderPath: "video",
|
||||||
|
audioOutputFolderPath: "audio",
|
||||||
|
tagsOutputFolderPath: "tag")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PageSettings {
|
||||||
|
|
||||||
|
static var `default`: PageSettings {
|
||||||
|
.init(contentWidth: 600,
|
||||||
|
largeImageWidth: 1200,
|
||||||
|
pageLinkImageSize: 180,
|
||||||
|
defaultCssFile: nil,
|
||||||
|
codeHighlightingJsFile: nil,
|
||||||
|
modelViewerJsFile: nil,
|
||||||
|
imageCompareJsFile: nil,
|
||||||
|
imageCompareCssFile: nil,
|
||||||
|
german: .init(
|
||||||
|
emptyPageTitle: "Leere Seite",
|
||||||
|
emptyPageText: "Diese Seite ist leer"),
|
||||||
|
english: .init(
|
||||||
|
emptyPageTitle: "Empty page",
|
||||||
|
emptyPageText: "This page is empty"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class Tag: Item {
|
class Tag: Item, LocalizedItem {
|
||||||
|
|
||||||
|
override var itemType: ItemType { .tag }
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var isVisible: Bool
|
var isVisible: Bool
|
||||||
@ -59,7 +61,7 @@ final class Tag: Item {
|
|||||||
localized(in: language).title
|
localized(in: language).title
|
||||||
}
|
}
|
||||||
|
|
||||||
override var itemType: ItemType {
|
override var itemReference: ItemReference {
|
||||||
.tagPage(self)
|
.tagPage(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,21 +70,35 @@ final class Tag: Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func remove(_ file: FileResource) {
|
func remove(_ file: FileResource) {
|
||||||
english.remove(linkPreviewImage: file)
|
english.linkPreview.remove(file)
|
||||||
german.remove(linkPreviewImage: file)
|
german.linkPreview.remove(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Tag: LocalizedItem {
|
// MARK: Storage
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Tag {
|
extension Tag {
|
||||||
|
|
||||||
var file: TagFile {
|
convenience init(context: LoadingContext, id: String, data: Data) {
|
||||||
.init(id: id,
|
self.init(
|
||||||
isVisible: isVisible,
|
content: context.content,
|
||||||
german: german.tagFile,
|
id: id,
|
||||||
english: english.tagFile)
|
isVisible: data.isVisible ?? true,
|
||||||
|
german: .init(context: context, data: data.german),
|
||||||
|
english: .init(context: context, data: data.english))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data: Codable {
|
||||||
|
// Defaults to true if unset
|
||||||
|
let isVisible: Bool?
|
||||||
|
let german: LocalizedTag.Data
|
||||||
|
let english: LocalizedTag.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
.init(
|
||||||
|
isVisible: isVisible ? nil : false,
|
||||||
|
german: german.data,
|
||||||
|
english: english.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
CHDataManagement/Model/TagOverview.swift
Normal file
7
CHDataManagement/Model/TagOverview.swift
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
final class TagOverview: Tag {
|
||||||
|
|
||||||
|
override var itemId: ItemId {
|
||||||
|
.init(type: .tagOverview, id: id)
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ struct PartialSvgImage: HtmlProducer {
|
|||||||
|
|
||||||
let imagePath: String
|
let imagePath: String
|
||||||
|
|
||||||
let altText: String
|
let altText: String?
|
||||||
|
|
||||||
let x: Int
|
let x: Int
|
||||||
|
|
||||||
@ -20,9 +20,16 @@ struct PartialSvgImage: HtmlProducer {
|
|||||||
return Double(width) / Double(height)
|
return Double(width) / Double(height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var imageAltText: String {
|
||||||
|
guard let altText else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return " alt='\(altText.htmlEscaped())'"
|
||||||
|
}
|
||||||
|
|
||||||
func populate(_ result: inout String) {
|
func populate(_ result: inout String) {
|
||||||
result += "<span class='content-image svg-image'>"
|
result += "<span class='content-image svg-image'>"
|
||||||
result += "<img src='\(imagePath)#svgView(viewBox(\(x), \(y), \(width), \(height)))' loading='lazy' style='aspect-ratio:\(aspectRatio)' alt='\(altText)'/>"
|
result += "<img src='\(imagePath)#svgView(viewBox(\(x), \(y), \(width), \(height)))' loading='lazy' style='aspect-ratio:\(aspectRatio)'\(imageAltText)/>"
|
||||||
result += "</span>"
|
result += "</span>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,18 @@ struct SimpleImage: HtmlProducer {
|
|||||||
|
|
||||||
let imagePath: String
|
let imagePath: String
|
||||||
|
|
||||||
let altText: String
|
let altText: String?
|
||||||
|
|
||||||
|
private var imageAltText: String {
|
||||||
|
guard let altText else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return " alt='\(altText.htmlEscaped())'"
|
||||||
|
}
|
||||||
|
|
||||||
func populate(_ result: inout String) {
|
func populate(_ result: inout String) {
|
||||||
result += "<div class='content-image svg-image'>"
|
result += "<div class='content-image svg-image'>"
|
||||||
result += "<img src='\(imagePath)' loading='lazy' alt='\(altText)'/>"
|
result += "<img src='\(imagePath)' loading='lazy'\(imageAltText)/>"
|
||||||
result += "</div>"
|
result += "</div>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,16 @@ struct ModelViewer {
|
|||||||
|
|
||||||
let file: String
|
let file: String
|
||||||
|
|
||||||
let description: String
|
let description: String?
|
||||||
|
|
||||||
|
private var imageAltText: String {
|
||||||
|
guard let description else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return " alt='\(description.htmlEscaped())'"
|
||||||
|
}
|
||||||
|
|
||||||
var content: String {
|
var content: String {
|
||||||
"<model-viewer alt='\(description)' src='\(file)' ar shadow-intensity='1' camera-controls touch-action='pan-y'></model-viewer>"
|
"<model-viewer\(imageAltText) src='\(file)' ar shadow-intensity='1' camera-controls touch-action='pan-y'></model-viewer>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,15 +43,15 @@ extension LocalizedTag {
|
|||||||
content: .mock,
|
content: .mock,
|
||||||
urlComponent: "electronics",
|
urlComponent: "electronics",
|
||||||
name: "Electronics",
|
name: "Electronics",
|
||||||
linkPreviewDescription: "Some description of the tag",
|
linkPreview: .init(description: "Some description of the tag",
|
||||||
linkPreviewImage: FileResource(resourceImage: "image1", type: .jpg),
|
image: FileResource(resourceImage: "image1", type: .jpg)),
|
||||||
originalUrl: "projects/electronics")
|
originalUrl: "projects/electronics")
|
||||||
|
|
||||||
static let german = LocalizedTag(
|
static let german = LocalizedTag(
|
||||||
content: .mock,
|
content: .mock,
|
||||||
urlComponent: "elektronik",
|
urlComponent: "elektronik",
|
||||||
name: "Elektronik",
|
name: "Elektronik",
|
||||||
linkPreviewDescription: "Eine Beschreibung des Tags",
|
linkPreview: .init(description: "Eine Beschreibung des Tags",
|
||||||
linkPreviewImage: FileResource(resourceImage: "image2", type: .jpg),
|
image: FileResource(resourceImage: "image2", type: .jpg)),
|
||||||
originalUrl: "projects/electronics")
|
originalUrl: "projects/electronics")
|
||||||
}
|
}
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
extension PathSettings {
|
|
||||||
|
|
||||||
static var `default`: PathSettings {
|
|
||||||
.init(file: .default)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PageSettings {
|
|
||||||
|
|
||||||
static var `default`: PageSettings {
|
|
||||||
.init(file: .default, files: [:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedPostSettings {
|
|
||||||
|
|
||||||
static var german: LocalizedPostSettings {
|
|
||||||
.init(
|
|
||||||
title: "Titel",
|
|
||||||
description: "Beschreibung",
|
|
||||||
feedUrlPrefix: "blog",
|
|
||||||
defaultPageLinkText: "Anzeigen")
|
|
||||||
}
|
|
||||||
|
|
||||||
static var english: LocalizedPostSettings {
|
|
||||||
.init(
|
|
||||||
title: "A Title",
|
|
||||||
description: "Description",
|
|
||||||
feedUrlPrefix: "feed",
|
|
||||||
defaultPageLinkText: "View")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
struct FileDescriptions {
|
|
||||||
|
|
||||||
let fileId: String
|
|
||||||
|
|
||||||
let german: String?
|
|
||||||
|
|
||||||
let english: String?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FileDescriptions: Codable {
|
|
||||||
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
/**
|
|
||||||
This struct holds metadata about a file resource that is stored in the content folder.
|
|
||||||
*/
|
|
||||||
struct FileResourceFile {
|
|
||||||
|
|
||||||
/// The file/image description in German
|
|
||||||
let englishDescription: String?
|
|
||||||
|
|
||||||
/// The file/image description in English
|
|
||||||
let germanDescription: String?
|
|
||||||
|
|
||||||
/// The list of generated image versions for this image
|
|
||||||
let generatedImages: [String]?
|
|
||||||
|
|
||||||
/// A custom file path in the output folder where this file is located
|
|
||||||
let customOutputPath: String?
|
|
||||||
|
|
||||||
/// A version string of this resource, mostly for assets
|
|
||||||
let version: String?
|
|
||||||
|
|
||||||
/// A URL where the resource was copied/downloaded from
|
|
||||||
let sourceUrl: String?
|
|
||||||
|
|
||||||
/// The date when the file was added
|
|
||||||
let addedDate: Date
|
|
||||||
|
|
||||||
/// The date when the file was last modified
|
|
||||||
let modifiedDate: Date
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extension FileResourceFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
struct PageFile {
|
|
||||||
|
|
||||||
let isDraft: Bool
|
|
||||||
|
|
||||||
let externalLink: String?
|
|
||||||
|
|
||||||
let tags: [String]
|
|
||||||
|
|
||||||
let hideDate: Bool?
|
|
||||||
|
|
||||||
let createdDate: Date
|
|
||||||
|
|
||||||
let startDate: Date
|
|
||||||
|
|
||||||
let endDate: Date?
|
|
||||||
|
|
||||||
let german: LocalizedPageFile
|
|
||||||
|
|
||||||
let english: LocalizedPageFile
|
|
||||||
|
|
||||||
/**
|
|
||||||
Specifies additional files which should be copied to the destination when generating the content.
|
|
||||||
- Note: This property defaults to an empty set.
|
|
||||||
*/
|
|
||||||
let requiredFiles: [String]?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PageFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
The structure to store the metadata of a localized page
|
|
||||||
*/
|
|
||||||
struct LocalizedPageFile {
|
|
||||||
|
|
||||||
let url: String
|
|
||||||
|
|
||||||
let title: String
|
|
||||||
|
|
||||||
let linkPreviewImage: String?
|
|
||||||
|
|
||||||
let linkPreviewTitle: String?
|
|
||||||
|
|
||||||
let linkPreviewDescription: String?
|
|
||||||
|
|
||||||
let lastModifiedDate: Date?
|
|
||||||
|
|
||||||
let originalURL: String?
|
|
||||||
|
|
||||||
let hideTitle: Bool?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedPageFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
struct PostFile {
|
|
||||||
|
|
||||||
let isDraft: Bool
|
|
||||||
|
|
||||||
let createdDate: Date
|
|
||||||
|
|
||||||
let startDate: Date
|
|
||||||
|
|
||||||
let endDate: Date?
|
|
||||||
|
|
||||||
let tags: [String]
|
|
||||||
|
|
||||||
let german: LocalizedPostFile
|
|
||||||
|
|
||||||
let english: LocalizedPostFile
|
|
||||||
|
|
||||||
let linkedPageId: String?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PostFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
The structure to store the metadata of a localized post
|
|
||||||
*/
|
|
||||||
struct LocalizedPostFile {
|
|
||||||
|
|
||||||
let images: [String]
|
|
||||||
|
|
||||||
let title: String?
|
|
||||||
|
|
||||||
let content: String
|
|
||||||
|
|
||||||
let lastModifiedDate: Date?
|
|
||||||
|
|
||||||
let pageLinkText: String?
|
|
||||||
|
|
||||||
let linkPreviewImage: String?
|
|
||||||
|
|
||||||
let linkPreviewTitle: String?
|
|
||||||
|
|
||||||
let linkPreviewDescription: String?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedPostFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
|
|
||||||
struct AudioPlayerSettingsFile: Codable {
|
|
||||||
|
|
||||||
let playlistCoverImageSize: Int
|
|
||||||
|
|
||||||
let smallCoverImageSize: Int
|
|
||||||
|
|
||||||
let audioPlayerJsFile: String?
|
|
||||||
|
|
||||||
let audioPlayerCssFile: String?
|
|
||||||
|
|
||||||
let german: LocalizedAudioPlayerSettingsFile
|
|
||||||
|
|
||||||
let english: LocalizedAudioPlayerSettingsFile
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LocalizedAudioPlayerSettingsFile: Codable {
|
|
||||||
|
|
||||||
let playlistText: String
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AudioPlayerSettingsFile: LocalizedItem {
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
struct LocalizedNavigationSettingsFile {
|
|
||||||
|
|
||||||
let rootUrl: String
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedNavigationSettingsFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedNavigationSettingsFile {
|
|
||||||
|
|
||||||
static var `default`: LocalizedNavigationSettingsFile {
|
|
||||||
.init(rootUrl: "/")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
struct LocalizedPageSettingsFile {
|
|
||||||
|
|
||||||
let emptyPageTitle: String
|
|
||||||
|
|
||||||
let emptyPageText: String
|
|
||||||
|
|
||||||
init(emptyPageTitle: String, emptyPageText: String) {
|
|
||||||
self.emptyPageTitle = emptyPageTitle
|
|
||||||
self.emptyPageText = emptyPageText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedPageSettingsFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedPageSettingsFile {
|
|
||||||
|
|
||||||
static var `default`: LocalizedPageSettingsFile {
|
|
||||||
.init(emptyPageTitle: "Empty Page", emptyPageText: "This page is empty.")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
struct LocalizedPostSettingsFile {
|
|
||||||
|
|
||||||
/// The page title for the post feed
|
|
||||||
let feedTitle: String
|
|
||||||
|
|
||||||
/// The page description for the post feed
|
|
||||||
let feedDescription: String
|
|
||||||
|
|
||||||
/// The path to the feed in the final website, appended with the page number
|
|
||||||
let feedUrlPrefix: String
|
|
||||||
|
|
||||||
/**
|
|
||||||
The text to display when linking to a page
|
|
||||||
|
|
||||||
Each post may define a custom text.
|
|
||||||
*/
|
|
||||||
let defaultPageLinkText: String?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedPostSettingsFile: Codable { }
|
|
||||||
|
|
||||||
extension LocalizedPostSettingsFile {
|
|
||||||
|
|
||||||
static var `default`: LocalizedPostSettingsFile {
|
|
||||||
.init(feedTitle: "A title",
|
|
||||||
feedDescription: "A description",
|
|
||||||
feedUrlPrefix: "blog",
|
|
||||||
defaultPageLinkText: "View")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
struct NavigationSettingsFile {
|
|
||||||
|
|
||||||
/// The tags to show in the navigation bar
|
|
||||||
let navigationItems: [String]
|
|
||||||
|
|
||||||
let german: LocalizedNavigationSettingsFile
|
|
||||||
|
|
||||||
let english: LocalizedNavigationSettingsFile
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NavigationSettingsFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NavigationSettingsFile {
|
|
||||||
|
|
||||||
static var `default`: NavigationSettingsFile {
|
|
||||||
.init(
|
|
||||||
navigationItems: [],
|
|
||||||
german: .default,
|
|
||||||
english: .default)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
|
|
||||||
struct PageSettingsFile {
|
|
||||||
|
|
||||||
let contentWidth: Int
|
|
||||||
|
|
||||||
let largeImageWidth: Int
|
|
||||||
|
|
||||||
let pageLinkImageSize: Int
|
|
||||||
|
|
||||||
let defaultCssFile: String?
|
|
||||||
|
|
||||||
let codeHighlightingJsFile: String?
|
|
||||||
|
|
||||||
let modelViewerJsFile: String?
|
|
||||||
|
|
||||||
let imageCompareJsFile: String?
|
|
||||||
|
|
||||||
let imageCompareCssFile: String?
|
|
||||||
|
|
||||||
let german: LocalizedPageSettingsFile
|
|
||||||
|
|
||||||
let english: LocalizedPageSettingsFile
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PageSettingsFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PageSettingsFile {
|
|
||||||
|
|
||||||
static var `default`: PageSettingsFile {
|
|
||||||
.init(contentWidth: 600,
|
|
||||||
largeImageWidth: 1200,
|
|
||||||
pageLinkImageSize: 180,
|
|
||||||
defaultCssFile: nil,
|
|
||||||
codeHighlightingJsFile: nil,
|
|
||||||
modelViewerJsFile: nil,
|
|
||||||
imageCompareJsFile: nil,
|
|
||||||
imageCompareCssFile: nil,
|
|
||||||
german: .default,
|
|
||||||
english: .default)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PageSettingsFile: LocalizedItem {
|
|
||||||
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
|
|
||||||
struct PathSettingsFile {
|
|
||||||
|
|
||||||
let assetsOutputFolderPath: String
|
|
||||||
|
|
||||||
let pagesOutputFolderPath: String
|
|
||||||
|
|
||||||
let imagesOutputFolderPath: String
|
|
||||||
|
|
||||||
let filesOutputFolderPath: String
|
|
||||||
|
|
||||||
let videosOutputFolderPath: String
|
|
||||||
|
|
||||||
let tagsOutputFolderPath: String
|
|
||||||
|
|
||||||
let audioOutputFolderPath: String
|
|
||||||
|
|
||||||
init(assetsOutputFolderPath: String,
|
|
||||||
pagesOutputFolderPath: String,
|
|
||||||
imagesOutputFolderPath: String,
|
|
||||||
filesOutputFolderPath: String,
|
|
||||||
videosOutputFolderPath: String,
|
|
||||||
tagsOutputFolderPath: String,
|
|
||||||
audioOutputFolderPath: String) {
|
|
||||||
self.assetsOutputFolderPath = assetsOutputFolderPath
|
|
||||||
self.pagesOutputFolderPath = pagesOutputFolderPath
|
|
||||||
self.imagesOutputFolderPath = imagesOutputFolderPath
|
|
||||||
self.filesOutputFolderPath = filesOutputFolderPath
|
|
||||||
self.videosOutputFolderPath = videosOutputFolderPath
|
|
||||||
self.tagsOutputFolderPath = tagsOutputFolderPath
|
|
||||||
self.audioOutputFolderPath = audioOutputFolderPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PathSettingsFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PathSettingsFile {
|
|
||||||
|
|
||||||
static var `default`: PathSettingsFile {
|
|
||||||
PathSettingsFile(
|
|
||||||
assetsOutputFolderPath: "asset",
|
|
||||||
pagesOutputFolderPath: "page",
|
|
||||||
imagesOutputFolderPath: "image",
|
|
||||||
filesOutputFolderPath: "file",
|
|
||||||
videosOutputFolderPath: "video",
|
|
||||||
tagsOutputFolderPath: "tag",
|
|
||||||
audioOutputFolderPath: "audio")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
struct PostSettingsFile {
|
|
||||||
|
|
||||||
/// The number of posts to show in a single page of the news feed
|
|
||||||
let postsPerPage: Int
|
|
||||||
|
|
||||||
/// The maximum width of the main content
|
|
||||||
let contentWidth: Int
|
|
||||||
|
|
||||||
let swiperCssFile: String?
|
|
||||||
|
|
||||||
let swiperJsFile: String?
|
|
||||||
|
|
||||||
let defaultCssFile: String?
|
|
||||||
|
|
||||||
let german: LocalizedPostSettingsFile
|
|
||||||
|
|
||||||
let english: LocalizedPostSettingsFile
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PostSettingsFile: Codable { }
|
|
||||||
|
|
||||||
extension PostSettingsFile {
|
|
||||||
|
|
||||||
static var `default`: PostSettingsFile {
|
|
||||||
.init(postsPerPage: 25,
|
|
||||||
contentWidth: 600,
|
|
||||||
swiperCssFile: nil,
|
|
||||||
swiperJsFile: nil,
|
|
||||||
defaultCssFile: nil,
|
|
||||||
german: .default,
|
|
||||||
english: .default)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
struct SettingsFile {
|
|
||||||
|
|
||||||
let paths: PathSettingsFile
|
|
||||||
|
|
||||||
/// The tags to show in the navigation bar
|
|
||||||
let navigation: NavigationSettingsFile
|
|
||||||
|
|
||||||
let posts: PostSettingsFile
|
|
||||||
|
|
||||||
let pages: PageSettingsFile
|
|
||||||
|
|
||||||
let audioPlayer: AudioPlayerSettingsFile
|
|
||||||
|
|
||||||
let tagOverview: TagOverviewFile?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SettingsFile: Codable { }
|
|
||||||
|
|
||||||
extension SettingsFile {
|
|
||||||
|
|
||||||
static var `default`: SettingsFile {
|
|
||||||
.init(
|
|
||||||
paths: .default,
|
|
||||||
navigation: .default,
|
|
||||||
posts: .default,
|
|
||||||
pages: .default,
|
|
||||||
audioPlayer: AudioPlayerSettings.default.file,
|
|
||||||
tagOverview: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
struct TagOverviewFile {
|
|
||||||
|
|
||||||
let german: LocalizedTagOverviewFile
|
|
||||||
|
|
||||||
let english: LocalizedTagOverviewFile
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TagOverviewFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
The structure to store the metadata of a localized page
|
|
||||||
*/
|
|
||||||
struct LocalizedTagOverviewFile {
|
|
||||||
|
|
||||||
let url: String
|
|
||||||
|
|
||||||
let title: String
|
|
||||||
|
|
||||||
let linkPreviewImage: String?
|
|
||||||
|
|
||||||
let linkPreviewTitle: String?
|
|
||||||
|
|
||||||
let linkPreviewDescription: String?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedTagOverviewFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
struct TagFile {
|
|
||||||
|
|
||||||
let id: String
|
|
||||||
|
|
||||||
let isVisible: Bool
|
|
||||||
|
|
||||||
let german: LocalizedTagFile
|
|
||||||
|
|
||||||
let english: LocalizedTagFile
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TagFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LocalizedTagFile {
|
|
||||||
|
|
||||||
/// The id of the tag, used also as a url component
|
|
||||||
let urlComponent: String
|
|
||||||
|
|
||||||
/// A custom name, different from the tag id
|
|
||||||
let name: String
|
|
||||||
|
|
||||||
let linkPreviewTitle: String?
|
|
||||||
|
|
||||||
let linkPreviewDescription: String?
|
|
||||||
|
|
||||||
/// The image id of the thumbnail
|
|
||||||
let linkPreviewImage: String?
|
|
||||||
|
|
||||||
/// The original url in the previous site layout
|
|
||||||
let originalURL: String?
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension LocalizedTagFile: Codable {
|
|
||||||
|
|
||||||
}
|
|
@ -312,6 +312,7 @@ struct SecurityBookmark {
|
|||||||
do {
|
do {
|
||||||
data = try Data(contentsOf: url)
|
data = try Data(contentsOf: url)
|
||||||
} catch {
|
} catch {
|
||||||
|
#warning("Get these errors")
|
||||||
print("Storage: Failed to read file \(url.path()): \(error)")
|
print("Storage: Failed to read file \(url.path()): \(error)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ final class Storage: ObservableObject {
|
|||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
|
#warning("Rework to make this non-optional by creating a wrapper class")
|
||||||
@Published
|
@Published
|
||||||
var contentScope: SecurityBookmark?
|
var contentScope: SecurityBookmark?
|
||||||
|
|
||||||
@ -72,13 +73,13 @@ final class Storage: ObservableObject {
|
|||||||
return contentScope.write(pageContent, to: path)
|
return contentScope.write(pageContent, to: path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func save(pageMetadata: PageFile, for pageId: String) -> Bool {
|
func save(pageMetadata: Page.Data, for pageId: String) -> Bool {
|
||||||
guard let contentScope else { return false }
|
guard let contentScope else { return false }
|
||||||
let path = pageMetadataPath(page: pageId)
|
let path = pageMetadataPath(page: pageId)
|
||||||
return contentScope.encode(pageMetadata, to: path)
|
return contentScope.encode(pageMetadata, to: path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadAllPages() -> [String : PageFile]? {
|
func loadAllPages() -> [String : Page.Data]? {
|
||||||
contentScope?.decodeJsonFiles(in: pagesFolderName)
|
contentScope?.decodeJsonFiles(in: pagesFolderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,13 +145,13 @@ final class Storage: ObservableObject {
|
|||||||
postsFolderName + "/" + postFileName(postId)
|
postsFolderName + "/" + postFileName(postId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func save(post: PostFile, for postId: String) -> Bool {
|
func save(post: Post.Data, for postId: String) -> Bool {
|
||||||
guard let contentScope else { return false }
|
guard let contentScope else { return false }
|
||||||
let path = postFilePath(post: postId)
|
let path = postFilePath(post: postId)
|
||||||
return contentScope.encode(post, to: path)
|
return contentScope.encode(post, to: path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadAllPosts() -> [String : PostFile]? {
|
func loadAllPosts() -> [String : Post.Data]? {
|
||||||
contentScope?.decodeJsonFiles(in: postsFolderName)
|
contentScope?.decodeJsonFiles(in: postsFolderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,13 +184,13 @@ final class Storage: ObservableObject {
|
|||||||
tagsFolderName + "/" + tagFileName(tagId: tagId)
|
tagsFolderName + "/" + tagFileName(tagId: tagId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func save(tagMetadata: TagFile, for tagId: String) -> Bool {
|
func save(tagMetadata: Tag.Data, for tagId: String) -> Bool {
|
||||||
guard let contentScope else { return false }
|
guard let contentScope else { return false }
|
||||||
let path = tagFilePath(tag: tagId)
|
let path = tagFilePath(tag: tagId)
|
||||||
return contentScope.encode(tagMetadata, to: path)
|
return contentScope.encode(tagMetadata, to: path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadAllTags() -> [String : TagFile]? {
|
func loadAllTags() -> [String : Tag.Data]? {
|
||||||
contentScope?.decodeJsonFiles(in: tagsFolderName)
|
contentScope?.decodeJsonFiles(in: tagsFolderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,9 +312,9 @@ final class Storage: ObservableObject {
|
|||||||
|
|
||||||
- Returns: A dictionary with the file ids as keys and the metadata file as a value.
|
- Returns: A dictionary with the file ids as keys and the metadata file as a value.
|
||||||
*/
|
*/
|
||||||
func loadAllFiles() -> [String : (data: FileResourceFile, isExternal: Bool)]? {
|
func loadAllFiles() -> [String : (data: FileResource.Data, isExternal: Bool)]? {
|
||||||
guard let contentScope else { return nil }
|
guard let contentScope else { return nil }
|
||||||
guard let list: [String : FileResourceFile] = contentScope.decodeJsonFiles(in: fileInfoFolderName) else {
|
guard let list: [String : FileResource.Data] = contentScope.decodeJsonFiles(in: fileInfoFolderName) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard let existingFiles = contentScope.fileNames(inRelativeFolder: filesFolderName).map(Set.init) else {
|
guard let existingFiles = contentScope.fileNames(inRelativeFolder: filesFolderName).map(Set.init) else {
|
||||||
@ -326,7 +327,7 @@ final class Storage: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func save(fileInfo: FileResourceFile, for fileId: String) -> Bool {
|
func save(fileInfo: FileResource.Data, for fileId: String) -> Bool {
|
||||||
guard let contentScope else { return false }
|
guard let contentScope else { return false }
|
||||||
let path = fileInfoPath(file: fileId)
|
let path = fileInfoPath(file: fileId)
|
||||||
return contentScope.encode(fileInfo, to: path)
|
return contentScope.encode(fileInfo, to: path)
|
||||||
@ -359,12 +360,12 @@ final class Storage: ObservableObject {
|
|||||||
|
|
||||||
// MARK: Settings
|
// MARK: Settings
|
||||||
|
|
||||||
func loadSettings() -> SettingsFile? {
|
func loadSettings() -> Settings.Data? {
|
||||||
guard let contentScope else { return nil }
|
guard let contentScope else { return nil }
|
||||||
return contentScope.decode(at: settingsDataFileName)
|
return contentScope.decode(at: settingsDataFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func save(settings: SettingsFile) -> Bool {
|
func save(settings: Settings.Data) -> Bool {
|
||||||
guard let contentScope else { return false }
|
guard let contentScope else { return false }
|
||||||
return contentScope.encode(settings, to: settingsDataFileName)
|
return contentScope.encode(settings, to: settingsDataFileName)
|
||||||
}
|
}
|
||||||
|
@ -73,12 +73,12 @@ struct FileDetailView: View {
|
|||||||
|
|
||||||
switch language {
|
switch language {
|
||||||
case .english:
|
case .english:
|
||||||
StringPropertyView(
|
OptionalStringPropertyView(
|
||||||
title: "Description",
|
title: "Description",
|
||||||
text: $file.english,
|
text: $file.english,
|
||||||
footer: "The description for the file. Descriptions are used for images and to explain the content of a file.")
|
footer: "The description for the file. Descriptions are used for images and to explain the content of a file.")
|
||||||
case .german:
|
case .german:
|
||||||
StringPropertyView(
|
OptionalStringPropertyView(
|
||||||
title: "Description",
|
title: "Description",
|
||||||
text: $file.german,
|
text: $file.german,
|
||||||
footer: "The description for the file. Descriptions are used for images and to explain the content of a file.")
|
footer: "The description for the file. Descriptions are used for images and to explain the content of a file.")
|
||||||
@ -151,8 +151,7 @@ struct FileDetailView: View {
|
|||||||
file.determineFileSize()
|
file.determineFileSize()
|
||||||
// Force regeneration of images and/or file copying
|
// Force regeneration of images and/or file copying
|
||||||
file.removeFileFromOutputFolder()
|
file.removeFileFromOutputFolder()
|
||||||
// Trigger content view update to reload image
|
file.modifiedDate = .now
|
||||||
file.didChange()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ struct ItemSelectionView: View {
|
|||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if !selectedItems.contains(where: { $0 is TagOverviewPage }) {
|
if !selectedItems.contains(where: { $0.itemReference == tagOverview.itemReference }) {
|
||||||
selectedItems.append(tagOverview)
|
selectedItems.append(tagOverview)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
CHDataManagement/Views/LinkPreviewDetailView.swift
Normal file
29
CHDataManagement/Views/LinkPreviewDetailView.swift
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct LinkPreviewDetailView: View {
|
||||||
|
|
||||||
|
@ObservedObject
|
||||||
|
var linkPreview: LinkPreview
|
||||||
|
|
||||||
|
let fallbackTitle: String?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
OptionalStringPropertyView(
|
||||||
|
title: "Preview Title",
|
||||||
|
text: $linkPreview.title,
|
||||||
|
prompt: fallbackTitle,
|
||||||
|
footer: "The title to use in a link preview")
|
||||||
|
|
||||||
|
OptionalImagePropertyView(
|
||||||
|
title: "Preview Image",
|
||||||
|
selectedImage: $linkPreview.image,
|
||||||
|
footer: "The image to show in a link preview")
|
||||||
|
|
||||||
|
OptionalTextFieldPropertyView(
|
||||||
|
title: "Preview Description",
|
||||||
|
text: $linkPreview.description,
|
||||||
|
footer: "The description to show in a link preview")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,21 +30,7 @@ struct LocalizedPageDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionalStringPropertyView(
|
LinkPreviewDetailView(linkPreview: page.linkPreview, fallbackTitle: page.title)
|
||||||
title: "Preview Title",
|
|
||||||
text: $page.linkPreviewTitle,
|
|
||||||
prompt: page.title,
|
|
||||||
footer: "The title to use for the page when linking to it")
|
|
||||||
|
|
||||||
OptionalImagePropertyView(
|
|
||||||
title: "Preview Image",
|
|
||||||
selectedImage: $page.linkPreviewImage,
|
|
||||||
footer: "The image to show for previews of this page")
|
|
||||||
|
|
||||||
OptionalTextFieldPropertyView(
|
|
||||||
title: "Preview Description",
|
|
||||||
text: $page.linkPreviewDescription,
|
|
||||||
footer: "The description to show in previews of the page")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,21 +12,7 @@ struct LocalizedPostDetailView: View {
|
|||||||
text: $post.pageLinkText,
|
text: $post.pageLinkText,
|
||||||
footer: "The custom text to show for the link to the linked page")
|
footer: "The custom text to show for the link to the linked page")
|
||||||
|
|
||||||
OptionalStringPropertyView(
|
LinkPreviewDetailView(linkPreview: post.linkPreview, fallbackTitle: post.title)
|
||||||
title: "Preview Title",
|
|
||||||
text: $post.linkPreviewTitle,
|
|
||||||
prompt: post.title,
|
|
||||||
footer: "The title to use for the post when linking to it")
|
|
||||||
|
|
||||||
OptionalImagePropertyView(
|
|
||||||
title: "Preview Image",
|
|
||||||
selectedImage: $post.linkPreviewImage,
|
|
||||||
footer: "The image to show for previews of this post")
|
|
||||||
|
|
||||||
OptionalTextFieldPropertyView(
|
|
||||||
title: "Preview Description",
|
|
||||||
text: $post.linkPreviewDescription,
|
|
||||||
footer: "The description to show in previews of the post")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,6 @@ struct LocalizedPostFeedSettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
LocalizedPostFeedSettingsView(settings: .english)
|
LocalizedPostFeedSettingsView(settings: PostSettings.default.english)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,12 @@ struct PathSettingsView: View {
|
|||||||
@EnvironmentObject
|
@EnvironmentObject
|
||||||
private var content: Content
|
private var content: Content
|
||||||
|
|
||||||
|
@State
|
||||||
|
private var showLoadErrorSheet: Bool = false
|
||||||
|
|
||||||
|
@State
|
||||||
|
private var loadErrors: [String] = []
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
@ -19,7 +25,7 @@ struct PathSettingsView: View {
|
|||||||
title: "Content Folder",
|
title: "Content Folder",
|
||||||
folder: $content.storage.contentScope,
|
folder: $content.storage.contentScope,
|
||||||
footer: "The folder where the raw content of the website is stored") { url in
|
footer: "The folder where the raw content of the website is stored") { url in
|
||||||
content.update(contentPath: url)
|
content.update(contentPath: url, callback: showLoadErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
FolderOnDiskPropertyView(
|
FolderOnDiskPropertyView(
|
||||||
@ -65,8 +71,32 @@ struct PathSettingsView: View {
|
|||||||
footer: "The path in the output folder where assets are stored")
|
footer: "The path in the output folder where assets are stored")
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
|
.sheet(isPresented: $showLoadErrorSheet) {
|
||||||
|
VStack {
|
||||||
|
Text("Failed to load database")
|
||||||
|
.font(.headline)
|
||||||
|
List(loadErrors, id: \.self) { error in
|
||||||
|
HStack {
|
||||||
|
Text(error)
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(minHeight: 200)
|
||||||
|
Button("Dismiss", action: { showLoadErrorSheet = false })
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showLoadErrors(errors: [String]) {
|
||||||
|
guard !errors.isEmpty else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loadErrors = errors
|
||||||
|
showLoadErrorSheet = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
|
@ -15,8 +15,8 @@ struct TagOverviewDetailView: View {
|
|||||||
title: "Tag Overview",
|
title: "Tag Overview",
|
||||||
text: "Configure the page showing all tags")
|
text: "Configure the page showing all tags")
|
||||||
|
|
||||||
if let page = content.tagOverview?.localized(in: language) {
|
if let tag = content.tagOverview?.localized(in: language) {
|
||||||
TagOverviewDetails(page: page)
|
LocalizedTagDetailView(tag: tag)
|
||||||
.id(language)
|
.id(language)
|
||||||
} else {
|
} else {
|
||||||
Button("Create", action: createTagOverviewPage)
|
Button("Create", action: createTagOverviewPage)
|
||||||
@ -27,50 +27,10 @@ struct TagOverviewDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func createTagOverviewPage() {
|
private func createTagOverviewPage() {
|
||||||
content.tagOverview = TagOverviewPage(
|
content.tagOverview = Tag(
|
||||||
content: content,
|
content: content,
|
||||||
german: .init(content: content, title: "Alle Tags", urlString: "alle"),
|
id: "all-tags",
|
||||||
english: .init(content: content, title: "All tags", urlString: "all"))
|
german: .init(content: content, urlComponent: "alle", name: "Alle Tags"),
|
||||||
}
|
english: .init(content: content, urlComponent: "all", name: "All tags"))
|
||||||
}
|
|
||||||
|
|
||||||
private struct TagOverviewDetails: View {
|
|
||||||
|
|
||||||
@EnvironmentObject
|
|
||||||
private var content: Content
|
|
||||||
|
|
||||||
@ObservedObject
|
|
||||||
var page: LocalizedTagOverviewPage
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
StringPropertyView(
|
|
||||||
title: "Title",
|
|
||||||
text: $page.title,
|
|
||||||
footer: "The title of the overview page")
|
|
||||||
|
|
||||||
IdPropertyView(
|
|
||||||
id: $page.urlComponent,
|
|
||||||
title: "Page URL String",
|
|
||||||
footer: "The url component to use for the link to the page",
|
|
||||||
validation: page.isValid,
|
|
||||||
update: { page.urlComponent = $0 })
|
|
||||||
|
|
||||||
OptionalStringPropertyView(
|
|
||||||
title: "Preview Title",
|
|
||||||
text: $page.linkPreviewTitle,
|
|
||||||
prompt: page.title,
|
|
||||||
footer: "The title to use for the page when linking to it")
|
|
||||||
|
|
||||||
OptionalImagePropertyView(
|
|
||||||
title: "Preview Image",
|
|
||||||
selectedImage: $page.linkPreviewImage,
|
|
||||||
footer: "The image to show for previews of this page")
|
|
||||||
|
|
||||||
OptionalTextFieldPropertyView(
|
|
||||||
title: "Preview Description",
|
|
||||||
text: $page.linkPreviewDescription,
|
|
||||||
footer: "The description to show in previews of the page")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,21 +31,7 @@ struct LocalizedTagDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionalStringPropertyView(
|
LinkPreviewDetailView(linkPreview: tag.linkPreview, fallbackTitle: tag.name)
|
||||||
title: "Preview Title",
|
|
||||||
text: $tag.linkPreviewTitle,
|
|
||||||
prompt: tag.name,
|
|
||||||
footer: "The title to use for the tag in previews and on tag pages")
|
|
||||||
|
|
||||||
OptionalImagePropertyView(
|
|
||||||
title: "Preview Image",
|
|
||||||
selectedImage: $tag.linkPreviewImage,
|
|
||||||
footer: "The image to show for previews of this page")
|
|
||||||
|
|
||||||
OptionalTextFieldPropertyView(
|
|
||||||
title: "Preview Description",
|
|
||||||
text: $tag.linkPreviewDescription,
|
|
||||||
footer: "The description to show in previews of the page")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user