Notes on enhancements and bugfixes to Bhagavad Gita v1.0 Next.js web app

Last updated on 1st Aug. 2024

Quick Info

Github repo of app: https://github.com/ravisiyer/gita
App deployed at: https://gita-rsi.vercel.app/

Details

Chapter 7, Verse 3 has half-character data which is not rendered correctly by app but rendered correctly by Gita Supersite. DevTools show both use same Nirmala UI!

But my app's text in DevTools shows:
 मनुष्याणां सहस्रेषु कश्िचद्यतति सिद्धये।

यततामपि सिद्धानां कश्िचन्मां वेत्ति तत्त्वतः।।7.3।।
------
Whereas Gita Supersite's text in DevTools shows:
मनुष्याणां सहस्रेषु कश्चिद्यतति सिद्धये।
<br>
<br>

यततामपि सिद्धानां कश्चिन्मां वेत्ति तत्त्वतः।।7.3।।
------
When time permits, I can investigate the above issue.

==========================
If chapter number keyed in app navbar is 44 then console log shows this error:
layout-e57c21b52ccd05b8.js:1 getMaxVersesInChapter(): Argument chapterNumber has invalid value of: 44. As error is non-fatal, returning safe value of MAX_VERSE_NUMBER_IN_ALL_CHAPTERS: 78.
----

Code in SCV should check if chapterNumber is a valid value before calling getMaxVersesInChapter(chapterNumber).
As it is a non-fatal issue, I should fix it after version 1.0 is released even though I have spotted it during final stages of v1.0 release.  .... Done (in bugfix branch of repo).
===================

In below code, languageId is an optional parameter without a default value. If fn is called without languageId, it will be set to 'undefined' as per: https://medium.com/@kgrvr/typescript-functions-optional-parameters-b245e7a06d7d .
export async function getVerse(verseId: string, languageId?:number) {
...
Query/Mutation params: null vs undefined #236, https://github.com/neo4j/graphql/issues/236 :
Behaviour is different when passing null or undefined to a query or mutation.

query($id: ID) {
  user(where:{id: $id}) {
    name
  }
}
In this example, if $id is null then no users are returned. if $id is undefined then all users are returned.
-------

If above is documented then I can simply pass an undefined languageId in the gql query to get all languages. But I could not get graphQL documentation that says that's how it behaves. So I am going for more laborious option of constructing query string differently if languageId is undefined or not undefined.
=================================================


Data sharing / Prop drilling #45543, https://github.com/vercel/next.js/discussions/45543

Using React Context for State Management with Next.js, https://vercel.com/guides/react-context-state-management-nextjs 

[Has simple example but seems to be a little non-standard. Also 'use client' is not used in the context.js file! Perhaps compilation will give an error:] How to use Context API in a Nextjs APP, https://episyche.com/blog/how-to-use-context-api-in-a-nextjs-app , Oct. 2022, Repo: https://github.com/episyche/nextjs_context_api_example

Does rendering contexts in root layout makes my entire application client side? #58513, https://github.com/vercel/next.js/discussions/58513

Does 'use client' in Next.js 13 root layout make whole routes client component?, https://stackoverflow.com/questions/74992326/does-use-client-in-next-js-13-root-layout-make-whole-routes-client-component


I get a similar error as in article below if I do not use 'use client' in Verse page.

Do Contexts in Next.js 13 Make the Whole App Render on the Client?, https://dev.to/perssondennis/do-contexts-in-nextjs-13-make-the-whole-app-render-on-the-client-55bb : "You must remember that you will only be able to use the context within Client Components, the context is not available on the server."

Covers using Provider component while retaining some server components and making only required components client: When & Where to Add “use client” in React / Next.js (Client Components vs Server Components), https://youtu.be/Qdkg_mrniLk?t=363 , ByteGrad, Jul. 2023.

Covers issues in state management (avoiding prop drilling) using Redux in Next.js; in particular, covers using store.dispatch and store.getState instead of useDispatch and useSelector to prevent server components having to be made client components (this is very interesting as I have not come across a similar workaround in React useContext to avoid making server components that use it, to client components): State management is a nightmare in NextJs 13, https://www.youtube.com/watch?v=Fv3oZnHuAFg , 3 min. 43 secs, May 2023. Have to check if bg-io (gita-frontend-v2) uses this approach. ... It does not seem to use store.dispatch and store.getState in the main app code though store.getState is used in one place in whole app code - in src/redux/store.ts. ... src/components/Settings.tsx uses useSelector and useDispatch. But the file does not have 'use client' at the top! However 'use client' is used in many other files of app (27 results in 27 files). Seems like chapter and verse pages are client components but home page is a server component! Hmm. Perhaps I should also go for 'use client' option for my app's chaptersummary, chapter and verse pages, at least for initial version with App Settings (Home, About etc. pages could be server as they may not need App Settings). Later I could consider how to reduce 'client components' in my app. Also, if I go with 'use client' for the main data pages mentioned earlier then I could use 'useContext' to keep the code simple.

Easy explanation of context providers being used to share global application state and logic in React and how that changes in Next.js: Next.js 14 Tutorial - 58 - Context Providers, https://www.youtube.com/watch?v=Ou2IAs7W1Ig, 6 min. 7 secs. by Codevolution.

-------
Wrote a tiny Next.js 14 app which is a small trial and example of using React createContext method and useContext hook to share application settings data across some components of the app which have to be client components, with some other server component(s) using/including these client components. 
In the app, AppSettings is an object with app settings. It has languageId and setLanguageId as properties. AppSettings is used by AppSettingsContext which is created using createContext method and retrieved with useContext hook.
The components that access or set languageId of application settings need to be client components. The Home page is an example of a server component that includes a client component which outputs the value of languageId and links to another client component page which edits languageId.

------

Can Apollo client queries coded for server components be used in client components without changes? I think it will need different code but need to confirm that.

The following article is for Next.js 13 and it has different code for Nextjs server components and client components: How to use Apollo Client with Next.js 13, https://www.apollographql.com/blog/how-to-use-apollo-client-with-next-js-13 , March 2023.

This article is for NextJS 14 and it too uses different code for Nextjs server components and client components: How to set up NextJS 14 with Apollo Client?, https://medium.com/@sehrawy/how-to-set-up-nextjs-14-with-apollo-client-754a177e0a00, Jan. 2024

Not sure if this article is for NextJS 14. It too uses different code for Nextjs server components and client components: A Comprehensive Guide to Next.js Apollo Client Integration, https://www.dhiwise.com/post/a-comprehensive-guide-to-nextjs-apollo-client-integration, updated in June 2024

-------------

When I made Verse a client component, with same data fetching code as earlier when Verse was a server component, it seemed to behave strangely with data fetch not working or taking too long. Later when I tried it again, I got a clear error message: "async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server."

That settles it. If I want to change chapter and verse to client component to be able to use useContext data (app settings data), then I need to change the data fetching code.

===============================

Using cookies to manage app settings
------------------------------------
Nextjs tutorial does not seem to use cookies directly. The login user state is managed by its authentication package. It probably uses cookies but that's under the hood and so the app itself does not need to set or get cookie(s).

bg-io seems to use cookies to manage app settings state. It seems to be a rather involved approach but seems to have the great benefit of server components being able to access app settings state through cookies sent in the HTTP request header. This article seems to cover such scenarios well: A guide to cookies in Next.js, https://blog.logrocket.com/guide-cookies-next-js , Apr. 2024



To build a public API with Next.js: "If you are using the App Router, you can use Server Components or Route Handlers instead of API Routes.", https://nextjs.org/docs/pages/building-your-application/routing/api-routes

Route Handlers, https://nextjs.org/docs/app/building-your-application/routing/route-handlers : "Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs."

Using HTTP cookies, https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies : "A cookie (also known as a web cookie or browser cookie) is a small piece of data a server sends to a user's web browser. The browser may store cookies, create new cookies, modify existing ones, and send them back to the same server with later requests. Cookies enable web applications to store limited amounts of data and remember state information; by default the HTTP protocol is stateless."
What is the difference between server side cookie and client side cookie?, https://stackoverflow.com/questions/6922145/what-is-the-difference-between-server-side-cookie-and-client-side-cookie : "Server side cookies are known as "sessions". The website in this case stores a single cookie on the browser containing a unique Session Identifier. Status information (foo=10 and bar=20 above) are stored on the server and the Session Identifier is used to match the request with the data stored on the server."

App Router > Functions > headers, https://nextjs.org/docs/app/api-reference/functions/headers "The headers function allows you to read the HTTP incoming request headers from a Server Component."

App Router > Functions > cookies, https://nextjs.org/docs/app/api-reference/functions/cookies : "The cookies function allows you to read the HTTP incoming request cookies from a Server Component or write outgoing request cookies in a Server Action or Route Handler."

Server Actions and Mutations, https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations : "Server Actions are asynchronous functions that are executed on the server. They can be used in Server and Client Components to handle form submissions and data mutations in Next.js applications."

Error: Type 'FormDataEntryValue' is not assignable to type 'string'. Type 'File' is not assignable to type 'string', https://stackoverflow.com/questions/77593615/error-type-formdataentryvalue-is-not-assignable-to-type-string-type-file : " ... it means you should explicitly coerce type to string to get your field values like this`
const firstName = formData.get('firstName') as string"

How to use cookies in Client Components in Next.js 13 and read them on initial SSR request when dynamic routing is enabled?, https://stackoverflow.com/questions/76595420/how-to-use-cookies-in-client-components-in-next-js-13-and-read-them-on-initial-s

Using cookies in client-components, https://github.com/vercel/next.js/discussions/54906


Nullish coalescing operator (??), https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing : "The nullish coalescing (??) operator is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand."
e.g.     const [bgColor, setBgColor] = useState(
        getCookie('bgColor')?.toString() ?? initial.bgColor ?? '',
    )

I used:
  const [formLanguageId, setFormLanguageId] = useState(
    getCookie("languageId") ?? ""
  );
-----

Wrote a tiny trial and example app of using Cookies in Next.js to share application settings data across some components of the app. Its server component Home page retrieves the languageId cookie and shows its value. It has a link that goes to another page which is a client component and is passed current value of languageId as query string (searchParams page prop). That page allows the user to edit languageId and set the cookie.
It has another link that goes to another page which is a client component without any query string (searchParams page prop). That page retrieves current value of languageId cookie (using cookies-next) and allows the user to edit languageId and set the cookie. GitHub repo: https://github.com/ravisiyer/testcookies-njs

================================


React Server Components Change Everything, https://www.youtube.com/watch?v=rGPpQdbDbwo, 15 min. 47 secs., by Web Dev Simplified, Jan. 2024. When time permits, I need to see video fully and also read article.

=======================


You can make an input controlled by passing one of these props:

checked: A boolean. For a checkbox input or a radio button, controls whether it is selected.
value: A string. For a text input, controls its text. (For a radio button, specifies its form data.)
When you pass either of them, you must also pass an onChange handler that updates the passed value.

These <input> props are only relevant for uncontrolled inputs:

defaultChecked: A boolean. Specifies the initial value for type="checkbox" and type="radio" inputs.
defaultValue: A string. Specifies the initial value for a text input.
---

I tried using an uncontrolled input field (uses defaultValue prop and does NOT use either value prop or OnChange handler) with a form action. It worked as expected. Tiny app. project folder name: testFormUncontrld.

-----

From official Nextjs tutorial:

defaultValue vs. value / Controlled vs. Uncontrolled

If you're using state to manage the value of an input, you'd use the value attribute to make it a controlled component. This means React would manage the input's state.

However, since you're not using state, you can use defaultValue. This means the native input will manage its own state. This is okay since you're saving the search query to the URL instead of state.
----
The related code does NOT use state. It uses onChange whose handler replaces the URL with text in the input field passed as a Query String parameter. The defaultValue prop is used to pick up that same text from the Query String:
  defaultValue={searchParams.get('query')?.toString()}
----

useFormState of Nextjs version of Nextjs tutorial, seems to be from an older version of React Canary. Current version of React Canary has changed the name of useFormState to useActionState: https://react.dev/reference/react/useActionState.

export 'useFormState' (imported as 'useFormState') was not found in 'react-dom' in React, https://stackoverflow.com/questions/77456392/export-useformstate-imported-as-useformstate-was-not-found-in-react-dom :
"Up to now, Jan 2024, Next.js is the only popular framework that extends React Canary. If you're using Next.js You won't see this error."

Next.js tutorial chapter that covers useActionState: https://nextjs.org/learn/dashboard-app/improving-accessibility ... I think when I was studying the tutorial some months ago, this chapter used useFormState and not useActionState. The project that I coded-along uses useFormState and not useActionState.

----------------
I faced the problem described in article below and it took me quite some time to realize that TypeScript forcing me to convert e.target.value to Number was the cause. Hmm. Solution seems to be to always use string type variable (state variable in case of React controlled Input) for HTML input number elements: How to handle number input in typescript?, https://stackoverflow.com/questions/61070803/how-to-handle-number-input-in-typescript

============================

const ingredientsList = ["noodles", { list: ["eggs", "flour", "water"] }];
const ingredientsListDeepCopy = JSON.parse(JSON.stringify(ingredientsList));

======================

I had started a test/experimental branch named usecontext from commit point : "Updated About page version number to after 1.0 and in-progress", 3aaa403cbe4c14f7e87733dbc323bce1aa234d00. This was to explore using useContext in the gita app for settings like langauge. I later switched to a fresh and tiny test project to try out useContext in Nextjs as mentioned earlier in this post, followed by another tiny test project to try out cookies in Nextjs. After that, while it was clear that cookies would be the appropriate way for supporting app settings in the gita app, as there was some code that I had written in the usecontext branch, I wanted to stabilize that code and also take it to a point where languageIds can be specified in a test page which is used to filter the Verse page contents to the chosen languageIds. I have done that now (28 Jul. 2024) and I think this has been a useful exercise even though I will now go back to the main branch ***WITHOUT*** merging the usecontext branch to main, and then go with the cookies approach.

I am not clear how best to keep the usecontext branch source code for any future reference. As of now, I will be keeping it as an unused branch in the project (both local and on GitHub). I also plan to make a copy of the source files (around 7) that were changed as part of this test, and zip it as a separate useContext test related file outside the project directory.

.....


git diff [<options>] [--merge-base] <commit> <commit> [--] [<path>…​]
This is to view the changes between two arbitrary <commit>.
...
...[Ravi: option]
--stat[=<width>[,<name-width>[,<count>]]]
Generate a diffstat.
----
diffstat - make histogram from diff-output
...
This program reads the output of diff and displays a histogram of the insertions, deletions, and modifications per-file. Diffstat is a program that is useful for reviewing large, complex patch files. It reads from one or more input files which contain output from diff, producing a histogram of the total lines changed for each file referenced.
-----------


To view changes made in usecontext latest branch 6403c3fac3640e1539d95e3acab81cf5a9e094da from the commit point at which the branch was made and then switched to (using checkout) 3aaa403cbe4c14f7e87733dbc323bce1aa234d00 , I used:
git diff 3aaa403cbe4c14f7e87733dbc323bce1aa234d00 6403c3fac3640e1539d95e3acab81cf5a9e094da > tempdiff.txt

It did a great job! So I can have a quick view of all changes made in usecontext branch through this diff.
...
The command below creates a diffstat which lists files added, changed (or deleted) with stats. of how many lines were inserted, deleted (or changed?). (In my case, only insertions and deletions were listed. Perhaps I did not change any existing line.)
git diff --stat 3aaa403cbe4c14f7e87733dbc323bce1aa234d00 6403c3fac3640e1539d95e3acab81cf5a9e094da

I think the above commands will work with initial part of commit id but I just copy-pasted whole ids from 'git log' output.

--------------
Added a final commit point to usecontext branch: Code cleanup; Test Settings with useContext working version, https://github.com/ravisiyer/gita/commit/a4131b43c27649c298b0ffbaad93b961e45cc2db . The branch code got automatically deployed on Vercel with no deployment errors (as per my understanding of Vercel deployments, only I can view this branch code deployment app: https://gita-git-usecontext-ravi-s-iyers-projects.vercel.app/ ).

git diff 3aaa403 a4131b4 > useContextDiffwMain.txt

git diff --stat 3aaa403 a4131b4 > useContextDiffwMain-stat.txt
[Output of above command is small and so is given below:]
 app/layout.tsx          |  17 ++++---
 app/lib/data.ts         |  32 +++++++++----
 app/providers.tsx       |  29 ++++++++++++
 app/settings/page.tsx   |  81 +++++++++++++++++++++++++++++++++
 app/ui/navbar.tsx       |  10 +++--
 app/ui/verse.tsx        | 116 ++++++++++++++++++++++++++++++++++++++++++++++++
 app/verse/[id]/page.tsx |  75 ++-----------------------------
 7 files changed, 270 insertions(+), 90 deletions(-)
----

Created a directory called usecontext-branch outside project directory which has above useContextDiffwMain.txt and  useContextDiffwMain-stat.txt files. It also has an app.zip file which contains whole app directory of project at this stage with active branch being usecontext.

Ran 'git checkout main' to switch branch to main. usecontext branch is being retained and not deleted.
Now I am all set to start work on implementing App Settings support using cookies for which I will create another branch and switch to that branch.
=================================

Comparing branches & commits in GitHub


"To compare different versions of your repository, append /compare to your repository's path." Tried it and it provides an easy way in GitHub to compare main and usecontext branches: https://github.com/ravisiyer/gita/compare/main...usecontext .

But as I will change main, I want to compare specific commits - last commit of usecontext with commit in main from which point I created usecontext branch.

From the above docs.github link: "For example, this URL uses the shortened SHA codes to compare commits f75c570 and 3391dcc: https://github.com/github-linguist/linguist/compare/f75c570..3391dcc."


Quick description of key points in these changes:
  1. app/providers.tsx
    1. Creates the AppSettingsContext using createContext()
    2. In its Providers client component, creates the AppSettings object with initial values. The object properties are state (useState) variables.
    3. In its Providers component, wraps the children props passed to it in AppSettingsContext.Provider component with value prop set to AppSettings, and returns it.
  2. app/layout.tsx: In RootLayout server component,
    1. Wraps all key elements of body in Providers component.
  3. app/ui/navbar.tsx: In Navbar client component,
    1. Changes Home menu to Settings menu which takes user to "/settings"
  4. app/settings/page.tsx: In Page client component,
    1. Extracts AppSettings object from AppSettingsContext using useContext.
    2. Sets up languageIds array (fixed size) from languageIds (variable size) in AppSettings object.
    3. Has a form that allows user to see and modify the lanugageIds setting.
    4. On user setting the languagedIs values, updates state variables in AppSettings object and goes to "/verse/1"
  5. app/verse/[id]/page.tsx: In Page server component,
    1. Retains its generate Metadata (possible as it is a server component) and retains getVerse() call from it to get gitaVerse data from data.ts. If this component is made a client component then it cannot use getVerse function from data.ts as that works only in server components.
    2. Instead of processing the data, it includes Verse component (app/ui/verse.tsx) to which is passed the gitaVerse data. This Verse component is a client component which is able to access AppSettings 'context' data and show the Verse data as per AppSettings (in this test app, only languageIds).
  6. app/ui/verse.tsx: In Verse client component,
    1. Extracts AppSettings object from AppSettingsContext using useContext.
    2. Gets languageIds array (variable size) from value in AppSettings object.
    3. Filters gitaVerse prop picking up only languages specified in Settings.
    4. Displays the filtered gitaVerse data in page.
  7. app/lib/data.ts: The following changes are not required for useContext test app. They are from an early version of this test app where only one languageId related contents were displayed or all languageIds related contents were displayed. In this early version, the getVerse function was provided an additional optional languageId parameter. The GraphQL query was constructed differently based on whether languageId was specified or not. The issue with using such an approach is that in useContext approach, only client components can know the languageId in AppSettings and they cannot use getVerse as that is meant only for server components. [But in cookies approach, server components also have access to languageId, and so they could use the getVerse approach implemented here but expanded to an optional array of languageIds rather than only one languageId. ] I decided to retain this code in this test app as it works even if languageId parameter is not provided which is how verse page (app/verse/[id]/page.tsx) component uses getVerse.
==================
Based on: How to Disable the Red Underlines in VS Code, https://stackabuse.com/bytes/how-to-disable-the-red-underlines-in-vs-code/ :

Gear icon (lower-left) -> Settings 
Search 'Validate'
Uncheck Validate:Enable for languages you want.
----

I tried it on a wo-nmnx directory which does not have node_modules and .next directory, resulting in lots of wavy red lines.
I unchecked:
CSS: Validate
Tailwind CSS: Validate
JavaScript: Validate
TypeScript: Validate
----

The red wavy lines disappeared.

The problem may be that to restore this feature I have to remember what I unchecked and check it again. There does not seem to be a 'restore to default' kind of command for this.

Another way: From: Disable (or toggle) the wavy underline/squigglies (problems) in VS Code, https://stackoverflow.com/questions/43454967/disable-or-toggle-the-wavy-underline-squigglies-problems-in-vs-code

To disable wavy/squiggly underline in vscode, go to settings.json (Ctrl + Shift + P to access the Command Palette and type “settings.json”) and set underline color to fully transparent:

{
    "workbench.colorCustomizations": {
        "editorError.foreground":   "#00000000",
        "editorWarning.foreground": "#00000000",
        "editorInfo.foreground":    "#00000000"
    }
}
Though it may be better to make underline color just less vibrant:

{
    "workbench.colorCustomizations": {
        "editorError.foreground":   "#ff000088",
        "editorWarning.foreground": "#ffe60033",
        "editorInfo.foreground":    "#00ff0088"
    }
}
--- 

I think the above will face the same issue of not-easy to restore. And it may be more complicated as one will have to note down the default values VSCode uses for the above. So I have not tried this so far.
=====================

How Does The TypeScript Omit Type Work?, https://timmousk.com/blog/typescript-omit/

I used below code which worked.
File1: ./lib/addltypes-d
import { GitaLanguage } from "./gqltypes-d";

export type GitaLanguageConstant = Omit<
  GitaLanguage,
  "nodeId" | "gitaTranslationsByLanguageId" | "gitaCommentariesByLanguageId"
>;
...
File2: alllanguages.ts
import { GitaLanguageConstant } from "./lib/addltypes-d";

export const allGitaLanguages: GitaLanguageConstant[] = [
  { __typename: "GitaLanguage", id: 1, language: "english" },
  { __typename: "GitaLanguage", id: 2, language: "hindi" },
  { __typename: "GitaLanguage", id: 3, language: "sanskrit" },
];
-----------

Partial is another way to do it. Code given below:
import { GitaLanguage } from "./lib/gqltypes-d";

export const allGitaLanguages: Partial<GitaLanguage>[] = [
  { __typename: "GitaLanguage", id: 1, language: "english" },
  { __typename: "GitaLanguage", id: 2, language: "hindi" },
  { __typename: "GitaLanguage", id: 3, language: "sanskrit" },
];
----
I confirmed that if I add a non-existent property in above object literals, TS shows an error.
I think Partial may be the more appropriate path for me. 

-------
React Tutorial – How to Work with Multiple Checkboxes, https://www.freecodecamp.org/news/how-to-work-with-multiple-checkboxes-in-react/


If a component is no longer part of the rendering tree then it gets unloaded/unmounted, meaning it is discarded together with all of its state.

To prevent this from happening, you could either put that piece of state in the parent component and pass it into the child as a prop or ...
------
Use stringify while storing cookie and parse while retrieving it.

Typescript: Type 'string | undefined' is not assignable to type 'string', https://stackoverflow.com/questions/54496398/typescript-type-string-undefined-is-not-assignable-to-type-string

From my code:
    const tmp = getCookie("selectedLanguageIds");
    if (tmp) {
      const initialSelectedLanguageIds: string[] = JSON.parse(
        tmp?.toString() ?? ""
      );
      allGitaLanguages.map(({ id, language }, index) => {
        if (
          id?.toString &&
          initialSelectedLanguageIds.includes(id?.toString())
        ) {
          initialCheckedLanguageIds[index] = true;
        }
      });
    }

==========
redirect, https://nextjs.org/docs/app/api-reference/functions/redirect : "When used in a server action, it will serve a 303 HTTP redirect response to the caller."

====================

Can't assign cookies to a state #53070, https://github.com/vercel/next.js/discussions/53070
Following the above, resolved my problem of type RequestCookie not being assignable to string. My code now:
  const cookieStore = cookies();
  const tmp = cookieStore.get("selectedLanguageIds")?.value;
  const selectedLanguageIds = tmp ? JSON.parse(tmp) : [];
  if (!selectedLanguageIds.length) {
    selectedLanguageIds.push(DEFAULT_LANGUAGE_ID); // Default if no languages in selected Languages
  }
---
==================

Created branch settings to add select languages settings to app. Last commit point on it which is a working app version: verse page shows translations and commentaries in selected languages,  https://github.com/ravisiyer/gita/commit/b5bf06f0db15921202955039877b012606b3e572 . The branch code got automatically deployed on Vercel with no deployment errors (as per my understanding of Vercel deployments, only I can view this branch code deployment app: https://gita-git-settings-ravi-s-iyers-projects.vercel.app/ ).

==================

truncate overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
text-ellipsis text-overflow: ellipsis;
text-clip text-overflow: clip;
--------


Released version 1.1 .. See: Added Settings page to Gita web app (open source) to select languages for translations and commentaries; Notes for students & other self-learners,  https://raviswdev.blogspot.com/2024/08/added-settings-page-to-gita-web-app-to.html .

Comments