From 9a60788cfb5fcc165f7e61fa4a1fe2d836da4651 Mon Sep 17 00:00:00 2001 From: Itay Levy Date: Sun, 26 May 2024 20:38:03 +0200 Subject: [PATCH] fix(rtl): support better rtl in view mode (#1236) --- .../src/app/pages/home/home.page.html | 8 +++- .../frontend/src/app/pages/home/home.page.ts | 5 +++ .../recipe-components/recipe/recipe.page.html | 27 +++++++---- .../recipe-components/recipe/recipe.page.ts | 34 ++++++++++++-- .../src/app/services/recipe.service.ts | 3 ++ packages/util/shared/src/parsers.ts | 45 +++++++++++++++++++ 6 files changed, 110 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/app/pages/home/home.page.html b/packages/frontend/src/app/pages/home/home.page.html index 7c5e05f0a..04f0af7d5 100644 --- a/packages/frontend/src/app/pages/home/home.page.html +++ b/packages/frontend/src/app/pages/home/home.page.html @@ -120,8 +120,14 @@ *ngIf="!preferences[preferenceKeys.ShowImages]" class="no-image-padding" > -

{{ recipe.title }}

+

+ {{ recipe.title }} +

diff --git a/packages/frontend/src/app/pages/home/home.page.ts b/packages/frontend/src/app/pages/home/home.page.ts index 32e4e4818..25e8562d0 100644 --- a/packages/frontend/src/app/pages/home/home.page.ts +++ b/packages/frontend/src/app/pages/home/home.page.ts @@ -25,6 +25,7 @@ import { PreferencesService } from "~/services/preferences.service"; import { MyRecipesPreferenceKey, GlobalPreferenceKey, + isRtlText, } from "@recipesage/util/shared"; import { HomePopoverPage } from "~/pages/home-popover/home-popover.page"; import { HomeSearchFilterPopoverPage } from "~/pages/home-search-popover/home-search-filter-popover.page"; @@ -498,6 +499,10 @@ export class HomePage { this.selectedRecipeIds = []; } + isRtlText(text: string, firstWordOnly = false): boolean { + return isRtlText(text, firstWordOnly); + } + async addLabelToSelectedRecipes() { const header = await this.translate .get("pages.home.addLabel.header") diff --git a/packages/frontend/src/app/pages/recipe-components/recipe/recipe.page.html b/packages/frontend/src/app/pages/recipe-components/recipe/recipe.page.html index f190fce32..21969b418 100644 --- a/packages/frontend/src/app/pages/recipe-components/recipe/recipe.page.html +++ b/packages/frontend/src/app/pages/recipe-components/recipe/recipe.page.html @@ -29,7 +29,7 @@ - +

{{ recipe.title }}

{{ recipe.title }} -

{{ recipe.description }}

+

+ {{ recipe.description }} +

@@ -170,10 +175,11 @@

{{ recipe.title }}

- +

{{ recipe.title }} - +

- {{instruction.content}} + + {{instruction.content}} + {{instruction.count}}   +

@@ -235,9 +245,10 @@

{{ recipe.title }}

- +

{ + if (instruction.isRtl) { + rtlCount++; + } + }); + this.isInstructionRtl = rtlCount > this.instructions.length / 2; } if (this.recipe.notes && this.recipe.notes.length > 0) { @@ -174,6 +188,13 @@ export class RecipePage { ...note, content: linkifyStr(note.content), })); + let count = 0; + this.notes.forEach((note) => { + if (note.isRtl) { + count++; + } + }); + this.isNotesRtl = count > this.notes.length / 2; } const groupIdsSet = new Set(); @@ -343,6 +364,13 @@ export class RecipePage { this.scale, true, ); + let rtlCount = 0; + this.ingredients.forEach((ingredient) => { + if (ingredient.isRtl) { + rtlCount++; + } + }); + this.isIngredientsRtl = rtlCount > this.ingredients.length / 2; } editRecipe() { diff --git a/packages/frontend/src/app/services/recipe.service.ts b/packages/frontend/src/app/services/recipe.service.ts index 57feb133e..6623e86c3 100644 --- a/packages/frontend/src/app/services/recipe.service.ts +++ b/packages/frontend/src/app/services/recipe.service.ts @@ -49,6 +49,7 @@ export interface ParsedIngredient { originalContent: string; isHeader: boolean; complete: boolean; + isRtl: boolean; } export interface ParsedInstruction { @@ -56,11 +57,13 @@ export interface ParsedInstruction { isHeader: boolean; complete: boolean; count: number; + isRtl: boolean; } export interface ParsedNote { content: string; isHeader: boolean; + isRtl: boolean; } export enum ExportFormat { diff --git a/packages/util/shared/src/parsers.ts b/packages/util/shared/src/parsers.ts index a367b8789..f4bf2704e 100644 --- a/packages/util/shared/src/parsers.ts +++ b/packages/util/shared/src/parsers.ts @@ -136,6 +136,7 @@ export const parseIngredients = ( originalContent: string; complete: boolean; isHeader: boolean; + isRtl: boolean; }[] => { if (!ingredients) return []; @@ -147,6 +148,7 @@ export const parseIngredients = ( originalContent: match, complete: false, isHeader: false, + isRtl: isRtlText(match), })) || []; for (let i = 0; i < lines.length; i++) { @@ -226,8 +228,10 @@ export const parseIngredients = ( acc + ingredientPart + (ingredientPartDelimiters[idx] || ""), "", ); + lines[i].isRtl = isRtlText(lines[i].originalContent); } else { lines[i].content = updatedIngredientParts.join(" + "); + lines[i].isRtl = isRtlText(lines[i].originalContent); } lines[i].isHeader = false; @@ -244,6 +248,7 @@ export const parseInstructions = ( isHeader: boolean; count: number; complete: boolean; + isRtl: boolean; }[] => { instructions = replaceFractionsInText(instructions); @@ -269,6 +274,7 @@ export const parseInstructions = ( isHeader: true, count: 0, complete: false, + isRtl: isRtlText(headerContent, true), }; } else { return { @@ -276,6 +282,7 @@ export const parseInstructions = ( isHeader: false, count: stepCount++, complete: false, + isRtl: isRtlText(line, true), }; } }); @@ -286,6 +293,7 @@ export const parseNotes = ( ): { content: string; isHeader: boolean; + isRtl: boolean; }[] => { // Starts with [, anything inbetween, ends with ] const headerRegexp = /^\[.*\]$/; @@ -301,12 +309,49 @@ export const parseNotes = ( return { content: headerContent, isHeader: true, + isRtl: isRtlText(headerContent), }; } else { return { content: line, isHeader: false, + isRtl: isRtlText(line), }; } }); }; + +/* eslint-disable no-control-regex */ +export const isRtlText = ( + text: string, + onlyFirstWord: boolean = true, +): boolean => { + const rtlChars = new RegExp("[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]"); + const ltrChars = new RegExp( + "[\u0000-\u0590\u2000-\u202E\u202A-\u202E\uFB00-\uFB4F]", + ); + const aToZ = new RegExp("[a-zA-Z]"); + let rtlCount = 0; + let ltrCount = 0; + if (onlyFirstWord) { + const splits = text.split(" "); + for (let i = 0; i < splits.length; i++) { + if ( + rtlChars.test(splits[i].charAt(0)) || + aToZ.test(splits[i].charAt(0)) + ) { + text = splits[i]; + break; + } + } + } + for (let i = 0; i < text.length; i++) { + if (rtlChars.test(text[i])) { + rtlCount++; + } else if (ltrChars.test(text[i])) { + ltrCount++; + } + } + + return rtlCount > ltrCount; +};