diff --git a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate index 9b812bc..86fd1e2 100644 Binary files a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate and b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Nextcloud Cookbook iOS Client/Data/ObservableRecipeDetail.swift b/Nextcloud Cookbook iOS Client/Data/ObservableRecipeDetail.swift index 7149abb..9a54123 100644 --- a/Nextcloud Cookbook iOS Client/Data/ObservableRecipeDetail.swift +++ b/Nextcloud Cookbook iOS Client/Data/ObservableRecipeDetail.swift @@ -93,21 +93,59 @@ class ObservableRecipeDetail: ObservableObject { ) } - static func adjustIngredient(_ ingredient: String, by factor: Double) -> AttributedString { + static func applyMarkdownStyling(_ text: String) -> AttributedString { + if var markdownString = try? AttributedString( + markdown: text, + options: .init( + allowsExtendedAttributes: true, + interpretedSyntax: .full, + failurePolicy: .returnPartiallyParsedIfPossible + ) + ) { + for (intentBlock, intentRange) in markdownString.runs[AttributeScopes.FoundationAttributes.PresentationIntentAttribute.self].reversed() { + guard let intentBlock = intentBlock else { continue } + for intent in intentBlock.components { + switch intent.kind { + case .header(level: let level): + switch level { + case 1: + markdownString[intentRange].font = .system(.title).bold() + case 2: + markdownString[intentRange].font = .system(.title2).bold() + case 3: + markdownString[intentRange].font = .system(.title3).bold() + default: + break + } + default: + break + } + } + + if intentRange.lowerBound != markdownString.startIndex { + markdownString.characters.insert(contentsOf: "\n", at: intentRange.lowerBound) + } + } + return markdownString + } + return AttributedString(text) + } + + static func adjustIngredient(_ ingredient: AttributedString, by factor: Double) -> AttributedString { if factor == 0 { - return AttributedString(ingredient) + return ingredient } // Match mixed fractions first var matches = ObservableRecipeDetail.matchPatternAndMultiply( .mixedFraction, - in: ingredient, + in: String(ingredient.characters), multFactor: factor ) // Then match fractions, exclude mixed fraction ranges matches.append(contentsOf: ObservableRecipeDetail.matchPatternAndMultiply( .fraction, - in: ingredient, + in: String(ingredient.characters), multFactor: factor, excludedRanges: matches.map({ tuple in tuple.1 }) ) @@ -116,7 +154,7 @@ class ObservableRecipeDetail: ObservableObject { matches.append(contentsOf: ObservableRecipeDetail.matchPatternAndMultiply( .number, - in: ingredient, + in: String(ingredient.characters), multFactor: factor, excludedRanges: matches.map({ tuple in tuple.1 }) ) @@ -124,7 +162,8 @@ class ObservableRecipeDetail: ObservableObject { // Sort matches by match range lower bound, descending. matches.sort(by: { a, b in a.1.lowerBound > b.1.lowerBound}) - var attributedString = AttributedString(ingredient) + var attributedString = ingredient + for (newSubstring, matchRange) in matches { guard let range = Range(matchRange, in: attributedString) else { continue } var attributedSubString = AttributedString(newSubstring) diff --git a/Nextcloud Cookbook iOS Client/Data/RecipeModels.swift b/Nextcloud Cookbook iOS Client/Data/RecipeModels.swift index f04478c..828ced3 100644 --- a/Nextcloud Cookbook iOS Client/Data/RecipeModels.swift +++ b/Nextcloud Cookbook iOS Client/Data/RecipeModels.swift @@ -109,7 +109,7 @@ struct RecipeDetail: Codable { totalTime = try container.decodeIfPresent(String.self, forKey: .totalTime) description = try container.decode(String.self, forKey: .description) url = try container.decode(String.self, forKey: .url) - recipeYield = try container.decode(Int.self, forKey: .recipeYield) + recipeYield = try container.decode(Int?.self, forKey: .recipeYield) ?? 1 recipeCategory = try container.decode(String.self, forKey: .recipeCategory) tool = try container.decode([String].self, forKey: .tool) recipeIngredient = try container.decode([String].self, forKey: .recipeIngredient) diff --git a/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeGenericViews.swift b/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeGenericViews.swift index e56a61e..5ada0c2 100644 --- a/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeGenericViews.swift +++ b/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeGenericViews.swift @@ -19,7 +19,7 @@ struct RecipeListSection: View { ForEach(list, id: \.self) { item in HStack(alignment: .top) { Text("\u{2022}") - Text("\(item)") + Text(ObservableRecipeDetail.applyMarkdownStyling(item)) .multilineTextAlignment(.leading) } .padding(4) diff --git a/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeIngredientSection.swift b/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeIngredientSection.swift index 25326d0..37e7d1b 100644 --- a/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeIngredientSection.swift +++ b/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeIngredientSection.swift @@ -129,7 +129,7 @@ fileprivate struct IngredientListItem: View { .foregroundStyle(.red) } if unmodified { - Text(ingredient) + Text(ObservableRecipeDetail.applyMarkdownStyling(ingredient)) .multilineTextAlignment(.leading) .lineLimit(5) } else { @@ -142,9 +142,9 @@ fileprivate struct IngredientListItem: View { } .onChange(of: servings) { newServings in if recipeYield == 0 { - modifiedIngredient = ObservableRecipeDetail.adjustIngredient(ingredient, by: newServings) + modifiedIngredient = ObservableRecipeDetail.adjustIngredient(ObservableRecipeDetail.applyMarkdownStyling(ingredient), by: newServings) } else { - modifiedIngredient = ObservableRecipeDetail.adjustIngredient(ingredient, by: newServings/recipeYield) + modifiedIngredient = ObservableRecipeDetail.adjustIngredient(ObservableRecipeDetail.applyMarkdownStyling(ingredient), by: newServings/recipeYield) } } .foregroundStyle(isSelected ? Color.secondary : Color.primary) diff --git a/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeInstructionSection.swift b/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeInstructionSection.swift index 4409f4b..88d8f7a 100644 --- a/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeInstructionSection.swift +++ b/Nextcloud Cookbook iOS Client/Views/Recipes/RecipeViewSections/RecipeInstructionSection.swift @@ -47,7 +47,7 @@ fileprivate struct RecipeInstructionListItem: View { HStack(alignment: .top) { Text("\(index)") .monospaced() - Text(instruction) + Text(ObservableRecipeDetail.applyMarkdownStyling(instruction)) }.padding(4) .foregroundStyle(isSelected ? Color.secondary : Color.primary) .onTapGesture {