From fc2f11f8eda002679729192d5723a038ca6319f3 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Wed, 13 Nov 2024 13:20:14 +0200 Subject: [PATCH 001/265] feat(topics): basic topic landing page --- static/js/ReaderPanel.jsx | 8 ++------ static/js/TopicLandingPage/TopicsLandingPage.jsx | 5 +++++ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 static/js/TopicLandingPage/TopicsLandingPage.jsx diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 8197774615..3991ea777d 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -41,6 +41,7 @@ import { ToggleSet, InterfaceText, EnglishText, HebrewText, SignUpModal, } from './Misc'; import {ContentText} from "./ContentText"; +import {TopicsLandingPage} from "./TopicLandingPage/TopicsLandingPage"; class ReaderPanel extends Component { @@ -976,12 +977,7 @@ class ReaderPanel extends Component { ); } else { menu = ( - + ); } diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx new file mode 100644 index 0000000000..f08880fcb9 --- /dev/null +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export const TopicsLandingPage = ({}) => { + return (
Hello, would you like a serving of topics salad?
); +}; From c62a6d4c4eda09e60f6075936bf07f820bc30be2 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Sun, 17 Nov 2024 13:09:54 +0200 Subject: [PATCH 002/265] feat(topic landing page): skeleton of GeneralAutocomplete and TopicLandingSearch --- static/js/GeneralAutocomplete.jsx | 44 +++++++++++ .../TopicLandingPage/TopicLandingSearch.jsx | 73 +++++++++++++++++++ .../js/TopicLandingPage/TopicsLandingPage.jsx | 8 +- 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 static/js/GeneralAutocomplete.jsx create mode 100644 static/js/TopicLandingPage/TopicLandingSearch.jsx diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx new file mode 100644 index 0000000000..326a0cea06 --- /dev/null +++ b/static/js/GeneralAutocomplete.jsx @@ -0,0 +1,44 @@ +import Sefaria from "./sefaria/sefaria"; +import React, {useEffect, useState} from "react"; +import classNames from "classnames"; +import {EnglishText, HebrewText, InterfaceText, SearchButton} from "./Misc"; +import { useCombobox } from 'downshift'; + + + +export const GeneralAutocomplete = ({ + getSuggestions, + renderItem, + renderInput, + dropdownMenuClassString, + onSubmit, +}) => { + const [suggestions, setSuggestions] = useState([]); + const { + isOpen, + getMenuProps, + getInputProps, + getItemProps, + highlightedIndex, + setInputValue + } = useCombobox({ + items: suggestions, + itemToString: (item) => (item ? item.name : ''), + onInputValueChange: async ({ inputValue }) => { + setSuggestions(await getSuggestions(inputValue)); + } + }); + const inputProps = getInputProps(); + console.log("suggestions:", suggestions) + return ( + <> + {renderInput(inputProps, onSubmit)} +
+ {suggestions.map((item, index) => renderItem(item, index, highlightedIndex, getItemProps, onSubmit))} +
+ + ); +}; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx new file mode 100644 index 0000000000..a5bbc0aee2 --- /dev/null +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -0,0 +1,73 @@ +import React from 'react'; +import {GeneralAutocomplete} from "../GeneralAutocomplete"; +import Sefaria from "../sefaria/sefaria"; + +const getSuggestions = async (input) => { + console.log("I got called") + if (input === "") { + return []; + } + const word = input.trim(); + + const _getFormattedPath = (slug, lang) => { + const categories = Sefaria.topicTocCategories(slug); + if(!categories){return ""} + const titles = categories.map((cat) => cat[lang]); + return "("+ titles.join(" < ") + ")"; + } + + const parseSuggestions = (d) => { + let topics = []; + console.log(d) + if (d[1].length > 0) { + topics = d[1].slice(0, 10).map((e) => ({ + title: e.title + " " + _getFormattedPath(e.key, 'en'), + key: e.key, + })); + } + return topics; + }; + // const completionObjects = await Sefaria.getTopicCompletions(word, callback()); + let returnValue = await Sefaria.getTopicCompletions(word); + const completionObjects = parseSuggestions(returnValue) + const a = completionObjects.map((suggestion) => ({ + text: suggestion.title, + slug: suggestion.key, + })); + console.log(a); + return a; + }; +const renderItem = (item, index, highlightedIndex, getItemProps, otherRelevenatDownshiftProps)=>{ + const isHighlighted = index === highlightedIndex; + return ( +
+ {item.text} +
+ ); +}; + +const renderInput = (downshiftProps) =>{ + return ( + + ) +} + +export const TopicLandingSearch = ({}) => { + return (); +}; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index f08880fcb9..978db0e432 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -1,5 +1,11 @@ import React from 'react'; +import {TopicLandingSearch} from "./TopicLandingSearch"; + export const TopicsLandingPage = ({}) => { - return (
Hello, would you like a serving of topics salad?
); + return (<> +
Hello, would you like a serving of topics salad?
+ + + ) }; From d9df0d426421bbad9a1998015331777e66ac09f6 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Sun, 17 Nov 2024 14:34:22 +0200 Subject: [PATCH 003/265] feat(topic landing page): pass openTopic from Reader to renderItem --- static/js/ReaderApp.jsx | 1 + static/js/ReaderPanel.jsx | 2 +- .../js/TopicLandingPage/TopicLandingSearch.jsx | 17 ++++++++--------- .../js/TopicLandingPage/TopicsLandingPage.jsx | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index f53f02bc2b..c2f1759c10 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -2240,6 +2240,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { divineNameReplacement={this.state.divineNameReplacement} setDivineNameReplacement={this.setDivineNameReplacement} topicTestVersion={this.props.topicTestVersion} + openTopic={this.openTopic} /> ); } diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 3991ea777d..e4e666708f 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -977,7 +977,7 @@ class ReaderPanel extends Component { ); } else { menu = ( - + ); } diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index a5bbc0aee2..41383f48ff 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -3,7 +3,6 @@ import {GeneralAutocomplete} from "../GeneralAutocomplete"; import Sefaria from "../sefaria/sefaria"; const getSuggestions = async (input) => { - console.log("I got called") if (input === "") { return []; } @@ -30,14 +29,14 @@ const getSuggestions = async (input) => { // const completionObjects = await Sefaria.getTopicCompletions(word, callback()); let returnValue = await Sefaria.getTopicCompletions(word); const completionObjects = parseSuggestions(returnValue) - const a = completionObjects.map((suggestion) => ({ + return completionObjects.map((suggestion) => ({ text: suggestion.title, slug: suggestion.key, })); - console.log(a); - return a; }; -const renderItem = (item, index, highlightedIndex, getItemProps, otherRelevenatDownshiftProps)=>{ + + +const renderItem = (openTopic, item, index, highlightedIndex, getItemProps, otherRelevantDownshiftProps)=>{ const isHighlighted = index === highlightedIndex; return (
- {item.text} + onClick={(e) => openTopic(item.slug)} + >{item.text}
); }; @@ -67,7 +66,7 @@ const renderInput = (downshiftProps) =>{ ) } -export const TopicLandingSearch = ({}) => { - return ( { + return (); }; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 978db0e432..395745fbc8 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -2,10 +2,10 @@ import React from 'react'; import {TopicLandingSearch} from "./TopicLandingSearch"; -export const TopicsLandingPage = ({}) => { +export const TopicsLandingPage = ({openTopic}) => { return (<>
Hello, would you like a serving of topics salad?
- + ) }; From 7d6b8fcf66a275c647825a3c66f14a9e6f59936a Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Sun, 17 Nov 2024 15:45:04 +0200 Subject: [PATCH 004/265] feat(topic landing page): override keydown on suggestion --- static/js/GeneralAutocomplete.jsx | 6 +++--- .../TopicLandingPage/TopicLandingSearch.jsx | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index 326a0cea06..7aead51164 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -28,11 +28,11 @@ export const GeneralAutocomplete = ({ setSuggestions(await getSuggestions(inputValue)); } }); - const inputProps = getInputProps(); - console.log("suggestions:", suggestions) + const inputDownshiftProps = getInputProps(); + const highlightedSuggestion=suggestions[highlightedIndex] return ( <> - {renderInput(inputProps, onSubmit)} + {renderInput(highlightedIndex, highlightedSuggestion, inputDownshiftProps, onSubmit)}
{ + + +const renderInput = (openTopic, highlightedIndex, highlightedSuggestion, inputDownshiftProps) =>{ + console.log(inputDownshiftProps) + const { onKeyDown, ...otherInputDownshiftProps } = inputDownshiftProps; + const onKeyDownOverride = (event) => { + onKeyDown(event); + if (event.key === 'Enter') { + highlightedIndex > 0 && openTopic(highlightedSuggestion.slug) + } + + } return ( ) } export const TopicLandingSearch = ({openTopic}) => { return (); + renderInput={renderInput.bind(null, openTopic)}/>); }; \ No newline at end of file From 588e87b744de72373a6dff7e2b6ae7051c6e75ef Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Sun, 17 Nov 2024 15:52:22 +0200 Subject: [PATCH 005/265] feat(topic landing page): fix onKeyDownOverride --- static/js/TopicLandingPage/TopicLandingSearch.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 0a1ef0ea5b..9c5a94798f 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -58,13 +58,12 @@ const renderInput = (openTopic, highlightedIndex, highlightedSuggestion, inputDo const onKeyDownOverride = (event) => { onKeyDown(event); if (event.key === 'Enter') { - highlightedIndex > 0 && openTopic(highlightedSuggestion.slug) + highlightedIndex >= 0 && openTopic(highlightedSuggestion.slug) } - } return ( Date: Sun, 17 Nov 2024 16:33:53 +0200 Subject: [PATCH 006/265] feat(topic landing page): add numOfTopics to placeholder --- static/js/TopicLandingPage/TopicLandingSearch.jsx | 8 ++++---- static/js/TopicLandingPage/TopicsLandingPage.jsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 9c5a94798f..05300eb09a 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -52,7 +52,7 @@ const renderItem = (openTopic, item, index, highlightedIndex, getItemProps, othe -const renderInput = (openTopic, highlightedIndex, highlightedSuggestion, inputDownshiftProps) =>{ +const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSuggestion, inputDownshiftProps) =>{ console.log(inputDownshiftProps) const { onKeyDown, ...otherInputDownshiftProps } = inputDownshiftProps; const onKeyDownOverride = (event) => { @@ -65,7 +65,7 @@ const renderInput = (openTopic, highlightedIndex, highlightedSuggestion, inputDo { +export const TopicLandingSearch = ({openTopic, numOfTopics}) => { return (); + renderInput={renderInput.bind(null, openTopic, numOfTopics)}/>); }; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 395745fbc8..619385c596 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -5,7 +5,7 @@ import {TopicLandingSearch} from "./TopicLandingSearch"; export const TopicsLandingPage = ({openTopic}) => { return (<>
Hello, would you like a serving of topics salad?
- + ) }; From 6015a3fdf75e5502d7f138b85421b759bff9fb1c Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Sun, 17 Nov 2024 17:41:35 +0200 Subject: [PATCH 007/265] feat(topic landing page): wire getItemProps and rendered item correctly --- static/js/GeneralAutocomplete.jsx | 1 + static/js/TopicLandingPage/TopicLandingSearch.jsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index 7aead51164..c678f24be0 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -30,6 +30,7 @@ export const GeneralAutocomplete = ({ }); const inputDownshiftProps = getInputProps(); const highlightedSuggestion=suggestions[highlightedIndex] + console.log(highlightedIndex) return ( <> {renderInput(highlightedIndex, highlightedSuggestion, inputDownshiftProps, onSubmit)} diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 05300eb09a..6487829c85 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -36,7 +36,7 @@ const getSuggestions = async (input) => { }; -const renderItem = (openTopic, item, index, highlightedIndex, getItemProps, otherRelevantDownshiftProps)=>{ +const renderItem = (openTopic, item, index, highlightedIndex, getItemProps)=>{ const isHighlighted = index === highlightedIndex; return (
openTopic(item.slug)} + {...getItemProps({index})} >{item.text}
); From a873b6c4f4c6c930aebfdff6093a2920129afd21 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 18 Nov 2024 11:29:28 +0200 Subject: [PATCH 008/265] feat(topic landing page): make search suggestions mouse-clickable --- static/js/TopicLandingPage/TopicLandingSearch.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 6487829c85..ce4ccd75cd 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -1,6 +1,7 @@ import React from 'react'; import {GeneralAutocomplete} from "../GeneralAutocomplete"; import Sefaria from "../sefaria/sefaria"; +import {SearchButton} from "../Misc"; const getSuggestions = async (input) => { if (input === "") { @@ -39,15 +40,16 @@ const getSuggestions = async (input) => { const renderItem = (openTopic, item, index, highlightedIndex, getItemProps)=>{ const isHighlighted = index === highlightedIndex; return ( +
openTopic(item.slug)} {...getItemProps({index})} >{item.text}
+
); }; @@ -63,6 +65,8 @@ const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSugges } } return ( +
+ +
) } From a5941748bee3451ad36e8c25833b483dfdd56c20 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 18 Nov 2024 14:09:17 +0200 Subject: [PATCH 009/265] feat(topic landing page): style topic autocomplete --- static/css/s2.css | 77 +++++++++++++++++++ static/js/GeneralAutocomplete.jsx | 8 +- .../TopicLandingPage/TopicLandingSearch.jsx | 25 +++--- 3 files changed, 94 insertions(+), 16 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index c06d921d2b..b40f838e97 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4832,6 +4832,83 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus padding-inline-end: 0; } +.topic-landing-search-wrapper{ + display: flex; + justify-content: center; +} + +.topic-landing-search-container{ + overflow: hidden; + display: flex; + align-items: center; + padding: 0; + text-align: inherit; + background: #EDEDEC; + border-radius: 250px; + width: 634px; + height: 60px; + margin-top: 137px; +} + +.topic-landing-search-input-box-wrapper{ + display: flex; +} + +.topic-landing-search-input{ + background-color: transparent; + padding: 0; + flex: 1; + height: 100%; + margin-bottom: 1px; + /*font-size: var(--serif-body-font-size);*/ + font-size: 24px; + font-weight: 500; + border: none; + width: 600px; +} + +.topic-landing-search-suggestion{ + display: flex; + list-style-type: none; + padding: 6px 12px; + font-family: 'EB Garamond'; + font-style: normal; + font-weight: 400; + font-size: 18px; + line-height: 23px; + cursor: pointer; + /*width: max-content;*/ + flex-grow: 1; + max-width: 100%; + min-height: 10px; +} + +.topic-landing-search-suggestion.highlighted{ + background-color: #EDEDEC; +} + +.topic-landing-search-dropdown{ + background: #FFFFFF; + position: absolute; + top: 218px; + /*width: auto;*/ + /*max-width: 130%;*/ + width: 606px; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); + border-radius: 0px 0px 6px 4px; + padding: 10px; + z-index: 2; +} +.topic-landing-search-dropdown:empty { + padding: 0; + visibility: hidden; +} + +.topic-landing-search-container .readerNavMenuSearchButton{ + top: 6px; + padding-inline-start: 11px; +} + .readerNavMenu .sheet { display: flex; justify-content: space-between; diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index c678f24be0..399d029dd3 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -10,6 +10,7 @@ export const GeneralAutocomplete = ({ getSuggestions, renderItem, renderInput, + containerClassString, dropdownMenuClassString, onSubmit, }) => { @@ -30,16 +31,15 @@ export const GeneralAutocomplete = ({ }); const inputDownshiftProps = getInputProps(); const highlightedSuggestion=suggestions[highlightedIndex] - console.log(highlightedIndex) return ( - <> +
{renderInput(highlightedIndex, highlightedSuggestion, inputDownshiftProps, onSubmit)}
- {suggestions.map((item, index) => renderItem(item, index, highlightedIndex, getItemProps, onSubmit))} + {(isOpen) && suggestions.map((item, index) => renderItem(item, index, highlightedIndex, getItemProps, onSubmit))}
- +
); }; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index ce4ccd75cd..2920fc313e 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -4,7 +4,7 @@ import Sefaria from "../sefaria/sefaria"; import {SearchButton} from "../Misc"; const getSuggestions = async (input) => { - if (input === "") { + if (input.length <=1) { return []; } const word = input.trim(); @@ -21,7 +21,7 @@ const getSuggestions = async (input) => { console.log(d) if (d[1].length > 0) { topics = d[1].slice(0, 10).map((e) => ({ - title: e.title + " " + _getFormattedPath(e.key, 'en'), + title: "# " + e.title + " " + _getFormattedPath(e.key, 'en'), key: e.key, })); } @@ -39,24 +39,22 @@ const getSuggestions = async (input) => { const renderItem = (openTopic, item, index, highlightedIndex, getItemProps)=>{ const isHighlighted = index === highlightedIndex; + const highlightedClassString = isHighlighted ? "highlighted" : ''; return ( - + ); }; const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSuggestion, inputDownshiftProps) =>{ - console.log(inputDownshiftProps) const { onKeyDown, ...otherInputDownshiftProps } = inputDownshiftProps; const onKeyDownOverride = (event) => { onKeyDown(event); @@ -65,10 +63,10 @@ const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSugges } } return ( -
+
{ - return (); + return (
+ +
+ ); }; \ No newline at end of file From a20934bac31c511d52582b8b45dfb61b9f9dc3b1 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 18 Nov 2024 16:54:00 +0200 Subject: [PATCH 010/265] feat(topic landing page): make dropdown scrollable for 10 or more suggestions --- static/css/s2.css | 2 ++ static/js/TopicLandingPage/TopicLandingSearch.jsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/static/css/s2.css b/static/css/s2.css index b40f838e97..4313da6df1 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4897,6 +4897,8 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); border-radius: 0px 0px 6px 4px; padding: 10px; + max-height: calc(1.9em * 10 + 2em); /* 2.5em is an estimate of the height per suggestion, and 2em for padding */ + overflow-y: auto; z-index: 2; } .topic-landing-search-dropdown:empty { diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 2920fc313e..eb6e64cf8a 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -25,6 +25,8 @@ const getSuggestions = async (input) => { key: e.key, })); } + const dummySuggestion = {title: "# Dummy", key: 'dummy'}; + topics = topics.concat([dummySuggestion, dummySuggestion, dummySuggestion, dummySuggestion, dummySuggestion]) return topics; }; // const completionObjects = await Sefaria.getTopicCompletions(word, callback()); From 9dab7a07152682a6c8f64b44d4fbfa98b8b13bb6 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 19 Nov 2024 11:31:38 +0200 Subject: [PATCH 011/265] feat(topic landing page): recognize hebrew input and render hebrew titles accordingly --- static/js/TopicLandingPage/TopicLandingSearch.jsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index eb6e64cf8a..46a21fc455 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -13,15 +13,15 @@ const getSuggestions = async (input) => { const categories = Sefaria.topicTocCategories(slug); if(!categories){return ""} const titles = categories.map((cat) => cat[lang]); - return "("+ titles.join(" < ") + ")"; + return "("+ titles.join(` > `) + ")"; } - const parseSuggestions = (d) => { + const parseSuggestions = (d, lang) => { let topics = []; console.log(d) if (d[1].length > 0) { topics = d[1].slice(0, 10).map((e) => ({ - title: "# " + e.title + " " + _getFormattedPath(e.key, 'en'), + title: "# " + e.title + " " + _getFormattedPath(e.key, lang), key: e.key, })); } @@ -31,7 +31,9 @@ const getSuggestions = async (input) => { }; // const completionObjects = await Sefaria.getTopicCompletions(word, callback()); let returnValue = await Sefaria.getTopicCompletions(word); - const completionObjects = parseSuggestions(returnValue) + const isInputHebrew = Sefaria.hebrew.isHebrew(word); + const lang = isInputHebrew? 'he' : 'en'; + const completionObjects = parseSuggestions(returnValue, lang) return completionObjects.map((suggestion) => ({ text: suggestion.title, slug: suggestion.key, @@ -83,6 +85,7 @@ const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSugges } export const TopicLandingSearch = ({openTopic, numOfTopics}) => { + return (
From c4334126558dbffc6bd4125624b58aeaf6242b2d Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 19 Nov 2024 16:23:14 +0200 Subject: [PATCH 012/265] feat(topic landing page): refactor main header autocomplete to be using GeneralAutocomplete --- static/js/Autocomplete.jsx | 96 +++++++++---------- static/js/GeneralAutocomplete.jsx | 9 +- .../TopicLandingPage/TopicLandingSearch.jsx | 16 +++- 3 files changed, 66 insertions(+), 55 deletions(-) diff --git a/static/js/Autocomplete.jsx b/static/js/Autocomplete.jsx index 06cfac82d2..1e74eadd8a 100644 --- a/static/js/Autocomplete.jsx +++ b/static/js/Autocomplete.jsx @@ -3,6 +3,7 @@ import React, {useEffect, useState} from "react"; import classNames from "classnames"; import {EnglishText, HebrewText, InterfaceText, SearchButton} from "./Misc"; import { useCombobox } from 'downshift'; +import {GeneralAutocomplete} from "./GeneralAutocomplete"; const type_icon_map = { "Collection": "collection.svg", @@ -174,7 +175,7 @@ const EntitySearchSuggestion = ({label, onClick, type, url, ...props}) => { ); } -const SearchInputBox = ({getInputProps, suggestions, highlightedIndex, hideHebrewKeyboard, setInputValue, +const SearchInputBox = ({getInputProps, highlightedSuggestion, highlightedIndex, hideHebrewKeyboard, setInputValue, setSearchFocused, searchFocused, submitSearch, redirectToObject}) => { @@ -192,7 +193,7 @@ const SearchInputBox = ({getInputProps, suggestions, highlightedIndex, hideHebre const handleSearchKeyDown = (event) => { onKeyDown(event); if (event.keyCode !== 13) return; - const highlightedItem = highlightedIndex > -1 ? suggestions[highlightedIndex] : null + const highlightedItem = highlightedIndex > -1 ? highlightedSuggestion : null if (highlightedItem && highlightedItem.type != 'search'){ redirectToObject(highlightedItem); return; @@ -353,28 +354,12 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig }; const Autocomplete = ({onRefClick, showSearch, openTopic, openURL, onNavigate, hideHebrewKeyboard = false}) => { - const [suggestions, setSuggestions] = useState([]); const [searchFocused, setSearchFocused] = useState(false); - const { - isOpen, - getMenuProps, - getInputProps, - getItemProps, - highlightedIndex, - setInputValue - } = useCombobox({ - items: suggestions, - itemToString: (item) => (item ? item.name : ''), - onInputValueChange: ({ inputValue }) => { - fetchSuggestions(inputValue); - } - }); const fetchSuggestions = async (inputValue) => { if (inputValue.length < 3){ - setSuggestions([]); - return; + return([]); } try { const d = await Sefaria.getName(inputValue); @@ -396,24 +381,25 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig comps = sortByTypeOrder(comps) if (comps.length > 0) { const q = inputValue; - setSuggestions([{value: "SEARCH_OVERRIDE", label: q, type: "search"}].concat(comps)); + return([{value: "SEARCH_OVERRIDE", label: q, type: "search"}].concat(comps)); } else { - setSuggestions([]); + return([]); } } catch (error) { console.error('Error fetching autocomplete suggestions:', error); - setSuggestions([]); + return([]); } }; - const clearSearchBox = function () { - getInputProps().onChange({ target: { value: '' } }); + const clearSearchBox = function (onChange) { + // getInputProps().onChange({ target: { value: '' } }); + onChange({ target: { value: '' } }); } - const submitSearch = (query) => { - if (highlightedIndex > -1 && suggestions[highlightedIndex].type === 'search') + const submitSearch = (onChange, query, highlightedIndex, highlightedSuggestion) => { + if (highlightedIndex > -1 && highlightedSuggestion.type === 'search') { showSearchWrapper(query); - clearSearchBox(); + clearSearchBox(onChange); return; } getQueryObj(query).then(({ type: queryType, id: queryId, is_book: queryIsBook }) => { @@ -421,13 +407,13 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig if (queryType === 'Ref') { let action = queryIsBook ? "Search Box Navigation - Book" : "Search Box Navigation - Citation"; Sefaria.track.event("Search", action, queryId); - clearSearchBox(); + clearSearchBox(onChange); onRefClick(queryId); onNavigate && onNavigate(); } else if (queryType === 'Topic') { Sefaria.track.event("Search", "Search Box Navigation - Topic", query); - clearSearchBox(); + clearSearchBox(onChange); openTopic(queryId); onNavigate && onNavigate(); } @@ -437,7 +423,7 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig else { Sefaria.track.event("Search", "Search Box Search", queryId); showSearchWrapper(queryId); - clearSearchBox(); + clearSearchBox(onChange); } } ) @@ -455,9 +441,9 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig onNavigate && onNavigate(); }; - const redirectToObject = (item) => { + const redirectToObject = (onChange, item) => { Sefaria.track.event("Search", `Search Box Navigation - ${item.type}`, item.key); - clearSearchBox(); + clearSearchBox(onChange); const url = item.url const handled = openURL(url); if (!handled) { @@ -466,12 +452,12 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig onNavigate && onNavigate(); } + const renderInput = (highlightedIndex, highlightedSuggestion, getInputProps, setInputValue)=> { - return ( -
- -
- {/*//debug: make following condition always truthy:*/} - {(isOpen && searchFocused) && - { + + return( + - } -
-
+ ) + }; + + + return ( + {return isOpen && searchFocused}} + /> ); }; export {Autocomplete}; \ No newline at end of file diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index 399d029dd3..85e2f33b17 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -8,10 +8,11 @@ import { useCombobox } from 'downshift'; export const GeneralAutocomplete = ({ getSuggestions, - renderItem, + renderItems, renderInput, containerClassString, dropdownMenuClassString, + shouldDisplaySuggestions, onSubmit, }) => { const [suggestions, setSuggestions] = useState([]); @@ -31,14 +32,16 @@ export const GeneralAutocomplete = ({ }); const inputDownshiftProps = getInputProps(); const highlightedSuggestion=suggestions[highlightedIndex] + shouldDisplaySuggestions = shouldDisplaySuggestions || (() => {return isOpen}) return (
- {renderInput(highlightedIndex, highlightedSuggestion, inputDownshiftProps, onSubmit)} + {renderInput(highlightedIndex, highlightedSuggestion, getInputProps, setInputValue)}
- {(isOpen) && suggestions.map((item, index) => renderItem(item, index, highlightedIndex, getItemProps, onSubmit))} + {/*{(shouldDisplaySuggestions(isOpen)) && suggestions.map((item, index) => renderItem(item, index, highlightedIndex, getItemProps, onSubmit))}*/} + {(shouldDisplaySuggestions(isOpen)) && renderItems(suggestions, highlightedIndex, getItemProps, getInputProps)}
); diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 46a21fc455..c0876dcf9d 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -56,10 +56,20 @@ const renderItem = (openTopic, item, index, highlightedIndex, getItemProps)=>{ ); }; +const renderItems = (openTopic, suggestions, highlightedIndex, getItemProps) => { + return ( + <> + {suggestions.map((item, index) => + renderItem(openTopic, item, index, highlightedIndex, getItemProps) + )} + + ); +}; + -const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSuggestion, inputDownshiftProps) =>{ - const { onKeyDown, ...otherInputDownshiftProps } = inputDownshiftProps; +const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSuggestion, getInputProps) =>{ + const { onKeyDown, ...otherInputDownshiftProps } = getInputProps(); const onKeyDownOverride = (event) => { onKeyDown(event); if (event.key === 'Enter') { @@ -87,7 +97,7 @@ const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSugges export const TopicLandingSearch = ({openTopic, numOfTopics}) => { return (
-
); From 214a6e883bb427243f641c9ef441a414691263bb Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Wed, 20 Nov 2024 11:49:36 +0200 Subject: [PATCH 013/265] chore(topic landing page): delete obsolete code lines --- static/js/GeneralAutocomplete.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index 85e2f33b17..cc12c7f11c 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -13,7 +13,6 @@ export const GeneralAutocomplete = ({ containerClassString, dropdownMenuClassString, shouldDisplaySuggestions, - onSubmit, }) => { const [suggestions, setSuggestions] = useState([]); const { @@ -30,9 +29,10 @@ export const GeneralAutocomplete = ({ setSuggestions(await getSuggestions(inputValue)); } }); - const inputDownshiftProps = getInputProps(); + const highlightedSuggestion=suggestions[highlightedIndex] shouldDisplaySuggestions = shouldDisplaySuggestions || (() => {return isOpen}) + return (
{renderInput(highlightedIndex, highlightedSuggestion, getInputProps, setInputValue)} @@ -40,7 +40,6 @@ export const GeneralAutocomplete = ({ {...getMenuProps()} className={dropdownMenuClassString} > - {/*{(shouldDisplaySuggestions(isOpen)) && suggestions.map((item, index) => renderItem(item, index, highlightedIndex, getItemProps, onSubmit))}*/} {(shouldDisplaySuggestions(isOpen)) && renderItems(suggestions, highlightedIndex, getItemProps, getInputProps)}
From 4291d87143461c97bf47bc319484f09f3b439028 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Wed, 20 Nov 2024 12:11:35 +0200 Subject: [PATCH 014/265] feat(topic landing page): fix he-interface of topic landing page to be rtl --- static/css/s2.css | 4 ++++ static/js/TopicLandingPage/TopicsLandingPage.jsx | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 4313da6df1..fb27758d61 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4837,6 +4837,10 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus justify-content: center; } +.interface-hebrew .topic-landing-page-wrapper{ + direction: rtl; +} + .topic-landing-search-container{ overflow: hidden; display: flex; diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 619385c596..43ead2e7bd 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -3,9 +3,9 @@ import {TopicLandingSearch} from "./TopicLandingSearch"; export const TopicsLandingPage = ({openTopic}) => { - return (<> + return (
Hello, would you like a serving of topics salad?
- +
) }; From 1c37b174398a6a581df480ca55b4617c0fedac23 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Wed, 20 Nov 2024 12:59:50 +0200 Subject: [PATCH 015/265] feat(topic landing page): hebrew placeholder --- static/js/GeneralAutocomplete.jsx | 3 +-- static/js/TopicLandingPage/TopicLandingSearch.jsx | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index cc12c7f11c..bc64496bc2 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -1,5 +1,4 @@ -import Sefaria from "./sefaria/sefaria"; -import React, {useEffect, useState} from "react"; +import React, {useState} from "react"; import classNames from "classnames"; import {EnglishText, HebrewText, InterfaceText, SearchButton} from "./Misc"; import { useCombobox } from 'downshift'; diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index c0876dcf9d..b98632362f 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -75,16 +75,15 @@ const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSugges if (event.key === 'Enter') { highlightedIndex >= 0 && openTopic(highlightedSuggestion.slug) } - } + }; + const placeHolder = Sefaria._v({"he": `חפש ${numOfTopics} אנשים, מקומות, חפצים`, "en": `Find ${numOfTopics} People, Places, Things`}) return (
Date: Wed, 20 Nov 2024 13:08:18 +0200 Subject: [PATCH 016/265] refactor(topic landing page): rename Autocomplete to HeaderAutocomplete --- static/js/GeneralAutocomplete.jsx | 2 -- static/js/Header.jsx | 6 +++--- static/js/{Autocomplete.jsx => HeaderAutocomplete.jsx} | 4 ++-- .../lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css | 2 +- .../jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css | 2 +- .../themes/base/jquery.ui.autocomplete.css | 2 +- .../themes/black-tie/jquery-ui-1.8.7.custom.css | 2 +- .../themes/black-tie/jquery.ui.autocomplete.css | 2 +- .../development-bundle/ui/jquery-ui-1.8.7.custom.js | 2 +- .../development-bundle/ui/jquery.ui.autocomplete.js | 2 +- static/js/sefaria/strings.js | 2 +- 11 files changed, 13 insertions(+), 15 deletions(-) rename static/js/{Autocomplete.jsx => HeaderAutocomplete.jsx} (99%) diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index bc64496bc2..ae3512ffca 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -1,6 +1,4 @@ import React, {useState} from "react"; -import classNames from "classnames"; -import {EnglishText, HebrewText, InterfaceText, SearchButton} from "./Misc"; import { useCombobox } from 'downshift'; diff --git a/static/js/Header.jsx b/static/js/Header.jsx index d097d85f90..d44cb5cf9a 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -14,7 +14,7 @@ import { LanguageToggleButton, DonateLink } from './Misc'; -import {Autocomplete} from './Autocomplete' +import {HeaderAutocomplete} from './HeaderAutocomplete' class Header extends Component { constructor(props) { @@ -56,7 +56,7 @@ class Header extends Component {
-
- { + const HeaderAutocomplete = ({onRefClick, showSearch, openTopic, openURL, onNavigate, hideHebrewKeyboard = false}) => { const [searchFocused, setSearchFocused] = useState(false); @@ -494,4 +494,4 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig /> ); }; -export {Autocomplete}; \ No newline at end of file +export {HeaderAutocomplete}; \ No newline at end of file diff --git a/static/js/lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css b/static/js/lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css index 2cd410309c..e8ad93d45c 100644 --- a/static/js/lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css +++ b/static/js/lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css @@ -340,7 +340,7 @@ .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } .ui-accordion .ui-accordion-content-active { display: block; }/* - * jQuery UI Autocomplete 1.8.7 + * jQuery UI HeaderAutocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css b/static/js/lib/jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css index e9a1a66ebc..4ff9f3388c 100755 --- a/static/js/lib/jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css +++ b/static/js/lib/jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css @@ -333,7 +333,7 @@ .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } .ui-accordion .ui-accordion-content-active { display: block; } /*! - * jQuery UI Autocomplete 1.8.22 + * jQuery UI HeaderAutocomplete 1.8.22 * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/themes/base/jquery.ui.autocomplete.css b/static/js/lib/jquery-ui/development-bundle/themes/base/jquery.ui.autocomplete.css index 8f81e68fda..ea215987ba 100644 --- a/static/js/lib/jquery-ui/development-bundle/themes/base/jquery.ui.autocomplete.css +++ b/static/js/lib/jquery-ui/development-bundle/themes/base/jquery.ui.autocomplete.css @@ -1,5 +1,5 @@ /* - * jQuery UI Autocomplete 1.8.7 + * jQuery UI HeaderAutocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery-ui-1.8.7.custom.css b/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery-ui-1.8.7.custom.css index 2cd410309c..e8ad93d45c 100644 --- a/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery-ui-1.8.7.custom.css +++ b/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery-ui-1.8.7.custom.css @@ -340,7 +340,7 @@ .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } .ui-accordion .ui-accordion-content-active { display: block; }/* - * jQuery UI Autocomplete 1.8.7 + * jQuery UI HeaderAutocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery.ui.autocomplete.css b/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery.ui.autocomplete.css index 8f81e68fda..ea215987ba 100644 --- a/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery.ui.autocomplete.css +++ b/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery.ui.autocomplete.css @@ -1,5 +1,5 @@ /* - * jQuery UI Autocomplete 1.8.7 + * jQuery UI HeaderAutocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/ui/jquery-ui-1.8.7.custom.js b/static/js/lib/jquery-ui/development-bundle/ui/jquery-ui-1.8.7.custom.js index 907eaebb80..ee8ab2ee11 100644 --- a/static/js/lib/jquery-ui/development-bundle/ui/jquery-ui-1.8.7.custom.js +++ b/static/js/lib/jquery-ui/development-bundle/ui/jquery-ui-1.8.7.custom.js @@ -4802,7 +4802,7 @@ $.extend( $.ui.accordion, { })( jQuery ); /* - * jQuery UI Autocomplete 1.8.7 + * jQuery UI HeaderAutocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/ui/jquery.ui.autocomplete.js b/static/js/lib/jquery-ui/development-bundle/ui/jquery.ui.autocomplete.js index 8047a7e021..16ecd88a4a 100644 --- a/static/js/lib/jquery-ui/development-bundle/ui/jquery.ui.autocomplete.js +++ b/static/js/lib/jquery-ui/development-bundle/ui/jquery.ui.autocomplete.js @@ -1,5 +1,5 @@ /* - * jQuery UI Autocomplete 1.8.7 + * jQuery UI HeaderAutocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index e99b29c75f..9b607b81f5 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -25,7 +25,7 @@ const Strings = { "Sign up": "להרשמה", "Sign Up": "להרשמה", - //Autocomplete + //HeaderAutocomplete "Books": "ספרים", "Terms": "מונחים", "Users": "משתמשים", From d3c1881f24c86295a4eb226d2723de090b284f58 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Wed, 20 Nov 2024 13:16:15 +0200 Subject: [PATCH 017/265] fix(topic landing page): revert jquery docs comment autocomplete renaming --- .../js/lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css | 2 +- .../js/lib/jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css | 2 +- .../development-bundle/themes/base/jquery.ui.autocomplete.css | 2 +- .../themes/black-tie/jquery-ui-1.8.7.custom.css | 2 +- .../themes/black-tie/jquery.ui.autocomplete.css | 2 +- .../jquery-ui/development-bundle/ui/jquery-ui-1.8.7.custom.js | 2 +- .../jquery-ui/development-bundle/ui/jquery.ui.autocomplete.js | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/static/js/lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css b/static/js/lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css index e8ad93d45c..2cd410309c 100644 --- a/static/js/lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css +++ b/static/js/lib/jquery-ui/css/black-tie/jquery-ui-1.8.7.custom.css @@ -340,7 +340,7 @@ .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } .ui-accordion .ui-accordion-content-active { display: block; }/* - * jQuery UI HeaderAutocomplete 1.8.7 + * jQuery UI Autocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css b/static/js/lib/jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css index 4ff9f3388c..e9a1a66ebc 100755 --- a/static/js/lib/jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css +++ b/static/js/lib/jquery-ui/css/smoothness/jquery-ui-1.8.22.custom.css @@ -333,7 +333,7 @@ .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } .ui-accordion .ui-accordion-content-active { display: block; } /*! - * jQuery UI HeaderAutocomplete 1.8.22 + * jQuery UI Autocomplete 1.8.22 * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/themes/base/jquery.ui.autocomplete.css b/static/js/lib/jquery-ui/development-bundle/themes/base/jquery.ui.autocomplete.css index ea215987ba..8f81e68fda 100644 --- a/static/js/lib/jquery-ui/development-bundle/themes/base/jquery.ui.autocomplete.css +++ b/static/js/lib/jquery-ui/development-bundle/themes/base/jquery.ui.autocomplete.css @@ -1,5 +1,5 @@ /* - * jQuery UI HeaderAutocomplete 1.8.7 + * jQuery UI Autocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery-ui-1.8.7.custom.css b/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery-ui-1.8.7.custom.css index e8ad93d45c..2cd410309c 100644 --- a/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery-ui-1.8.7.custom.css +++ b/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery-ui-1.8.7.custom.css @@ -340,7 +340,7 @@ .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } .ui-accordion .ui-accordion-content-active { display: block; }/* - * jQuery UI HeaderAutocomplete 1.8.7 + * jQuery UI Autocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery.ui.autocomplete.css b/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery.ui.autocomplete.css index ea215987ba..8f81e68fda 100644 --- a/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery.ui.autocomplete.css +++ b/static/js/lib/jquery-ui/development-bundle/themes/black-tie/jquery.ui.autocomplete.css @@ -1,5 +1,5 @@ /* - * jQuery UI HeaderAutocomplete 1.8.7 + * jQuery UI Autocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/ui/jquery-ui-1.8.7.custom.js b/static/js/lib/jquery-ui/development-bundle/ui/jquery-ui-1.8.7.custom.js index ee8ab2ee11..907eaebb80 100644 --- a/static/js/lib/jquery-ui/development-bundle/ui/jquery-ui-1.8.7.custom.js +++ b/static/js/lib/jquery-ui/development-bundle/ui/jquery-ui-1.8.7.custom.js @@ -4802,7 +4802,7 @@ $.extend( $.ui.accordion, { })( jQuery ); /* - * jQuery UI HeaderAutocomplete 1.8.7 + * jQuery UI Autocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. diff --git a/static/js/lib/jquery-ui/development-bundle/ui/jquery.ui.autocomplete.js b/static/js/lib/jquery-ui/development-bundle/ui/jquery.ui.autocomplete.js index 16ecd88a4a..8047a7e021 100644 --- a/static/js/lib/jquery-ui/development-bundle/ui/jquery.ui.autocomplete.js +++ b/static/js/lib/jquery-ui/development-bundle/ui/jquery.ui.autocomplete.js @@ -1,5 +1,5 @@ /* - * jQuery UI HeaderAutocomplete 1.8.7 + * jQuery UI Autocomplete 1.8.7 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. From 8faf3f4b6a23a9d64dead68de3dde1ec29eb6336 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Wed, 20 Nov 2024 13:21:09 +0200 Subject: [PATCH 018/265] refactor(topic landing page): better var name rawCompletions --- static/js/TopicLandingPage/TopicLandingSearch.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index b98632362f..34ef5cacbf 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -18,7 +18,6 @@ const getSuggestions = async (input) => { const parseSuggestions = (d, lang) => { let topics = []; - console.log(d) if (d[1].length > 0) { topics = d[1].slice(0, 10).map((e) => ({ title: "# " + e.title + " " + _getFormattedPath(e.key, lang), @@ -29,11 +28,12 @@ const getSuggestions = async (input) => { topics = topics.concat([dummySuggestion, dummySuggestion, dummySuggestion, dummySuggestion, dummySuggestion]) return topics; }; - // const completionObjects = await Sefaria.getTopicCompletions(word, callback()); - let returnValue = await Sefaria.getTopicCompletions(word); + const isInputHebrew = Sefaria.hebrew.isHebrew(word); const lang = isInputHebrew? 'he' : 'en'; - const completionObjects = parseSuggestions(returnValue, lang) + + const rawCompletions = await Sefaria.getTopicCompletions(word); + const completionObjects = parseSuggestions(rawCompletions, lang) return completionObjects.map((suggestion) => ({ text: suggestion.title, slug: suggestion.key, From 76cf7945bea06f37d933db34d571feb77bfae7e9 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Thu, 21 Nov 2024 12:18:31 +0200 Subject: [PATCH 019/265] chore(topic landing page): fix PR Review comments --- static/js/GeneralAutocomplete.jsx | 12 ++ static/js/HeaderAutocomplete.jsx | 148 +++++++++--------- .../TopicLandingPage/TopicLandingSearch.jsx | 70 +++++---- 3 files changed, 124 insertions(+), 106 deletions(-) diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index ae3512ffca..50d1f93195 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -11,6 +11,18 @@ export const GeneralAutocomplete = ({ dropdownMenuClassString, shouldDisplaySuggestions, }) => { + /** + * @param {Function} getSuggestions - A function that takes the current input value as a parameter + * and returns a promise resolving to an array of suggestions. + * @param {Function} renderItems - A function to render the list of suggestions. It receives the following arguments: + * (suggestions, highlightedIndex, getItemProps, getInputProps). + * @param {Function} renderInput - A function to render the input box. It receives the following arguments: + * (highlightedIndex, highlightedSuggestion, getInputProps, setInputValue). + * @param {string} containerClassString - CSS class string for styling the main container of the autocomplete component. + * @param {string} dropdownMenuClassString - CSS class string for styling the dropdown menu containing the suggestions. + * @param {Function} shouldDisplaySuggestions - An optional function to determine whether the suggestions dropdown should + * be displayed. Defaults to checking if the dropdown is open. + */ const [suggestions, setSuggestions] = useState([]); const { isOpen, diff --git a/static/js/HeaderAutocomplete.jsx b/static/js/HeaderAutocomplete.jsx index b3ccbb625c..00581b6ff6 100644 --- a/static/js/HeaderAutocomplete.jsx +++ b/static/js/HeaderAutocomplete.jsx @@ -1,8 +1,7 @@ import Sefaria from "./sefaria/sefaria"; import React, {useEffect, useState} from "react"; import classNames from "classnames"; -import {EnglishText, HebrewText, InterfaceText, SearchButton} from "./Misc"; -import { useCombobox } from 'downshift'; +import {InterfaceText, SearchButton} from "./Misc"; import {GeneralAutocomplete} from "./GeneralAutocomplete"; const type_icon_map = { @@ -353,46 +352,45 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig ); }; - const HeaderAutocomplete = ({onRefClick, showSearch, openTopic, openURL, onNavigate, hideHebrewKeyboard = false}) => { - const [searchFocused, setSearchFocused] = useState(false); +export const HeaderAutocomplete = ({onRefClick, showSearch, openTopic, openURL, onNavigate, hideHebrewKeyboard = false}) => { + const [searchFocused, setSearchFocused] = useState(false); - const fetchSuggestions = async (inputValue) => { - if (inputValue.length < 3){ - return([]); - } - try { - const d = await Sefaria.getName(inputValue); - - let comps = d["completion_objects"].map(o => { - const c = {...o}; - c["value"] = `${o['title']}${o["type"] === "ref" ? "" : `(${o["type"]})`}`; - c["label"] = o["title"]; - c["url"] = getURLForObject(c["type"], c["key"]); - - //"Topic" and "PersonTopic" considered same type: - const currentType = c["type"]; - const newType = ["Topic", "PersonTopic"].includes(currentType) ? "Topic" : currentType; - c["type"] = newType; - - - return c; - }); - comps = sortByTypeOrder(comps) - if (comps.length > 0) { - const q = inputValue; - return([{value: "SEARCH_OVERRIDE", label: q, type: "search"}].concat(comps)); - - } else { - return([]); - } - } catch (error) { - console.error('Error fetching autocomplete suggestions:', error); - return([]); - } -}; + const fetchSuggestions = async (inputValue) => { + if (inputValue.length < 3){ + return[]; + } + try { + const d = await Sefaria.getName(inputValue); + + let comps = d["completion_objects"].map(o => { + const c = {...o}; + c["value"] = `${o['title']}${o["type"] === "ref" ? "" : `(${o["type"]})`}`; + c["label"] = o["title"]; + c["url"] = getURLForObject(c["type"], c["key"]); + + //"Topic" and "PersonTopic" considered same type: + const currentType = c["type"]; + const newType = ["Topic", "PersonTopic"].includes(currentType) ? "Topic" : currentType; + c["type"] = newType; + + + return c; + }); + comps = sortByTypeOrder(comps) + if (comps.length > 0) { + const q = inputValue; + return([{value: "SEARCH_OVERRIDE", label: q, type: "search"}].concat(comps)); + + } else { + return[]; + } + } catch (error) { + console.error('Error fetching autocomplete suggestions:', error); + return[]; + } + }; const clearSearchBox = function (onChange) { - // getInputProps().onChange({ target: { value: '' } }); onChange({ target: { value: '' } }); } const submitSearch = (onChange, query, highlightedIndex, highlightedSuggestion) => { @@ -429,32 +427,32 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig ) }; - const showSearchWrapper = (query) => { - query = query.trim(); - if (typeof sjs !== "undefined") { - query = encodeURIComponent(query); - window.location = `/search?q=${query}`; - return; - } - showSearch(query); + const showSearchWrapper = (query) => { + query = query.trim(); + if (typeof sjs !== "undefined") { + query = encodeURIComponent(query); + window.location = `/search?q=${query}`; + return; + } + showSearch(query); - onNavigate && onNavigate(); - }; + onNavigate && onNavigate(); + }; - const redirectToObject = (onChange, item) => { - Sefaria.track.event("Search", `Search Box Navigation - ${item.type}`, item.key); - clearSearchBox(onChange); - const url = item.url - const handled = openURL(url); - if (!handled) { - window.location = url; + const redirectToObject = (onChange, item) => { + Sefaria.track.event("Search", `Search Box Navigation - ${item.type}`, item.key); + clearSearchBox(onChange); + const url = item.url + const handled = openURL(url); + if (!handled) { + window.location = url; + } + onNavigate && onNavigate(); } - onNavigate && onNavigate(); - } - const renderInput = (highlightedIndex, highlightedSuggestion, getInputProps, setInputValue)=> { + const renderInput = (highlightedIndex, highlightedSuggestion, getInputProps, setInputValue)=> { - return( + return( - ) - }; + /> + ) + }; - const renderItems =(suggestions, highlightedIndex, getItemProps, getInputProps) => { + const renderItems =(suggestions, highlightedIndex, getItemProps, getInputProps) => { - return( - - ) - }; + ) + }; return ( @@ -490,8 +491,7 @@ const SuggestionsGroup = ({ suggestions, initialIndexForGroup, getItemProps, hig renderInput={renderInput} renderItems={renderItems} getSuggestions={fetchSuggestions} - shouldDisplaySuggestions={(isOpen)=> {return isOpen && searchFocused}} + shouldDisplaySuggestions={isOpen=> isOpen && searchFocused} /> ); -}; -export {HeaderAutocomplete}; \ No newline at end of file +}; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 34ef5cacbf..84285f5b37 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import classNames from 'classnames'; import {GeneralAutocomplete} from "../GeneralAutocomplete"; import Sefaria from "../sefaria/sefaria"; import {SearchButton} from "../Misc"; @@ -13,19 +14,17 @@ const getSuggestions = async (input) => { const categories = Sefaria.topicTocCategories(slug); if(!categories){return ""} const titles = categories.map((cat) => cat[lang]); - return "("+ titles.join(` > `) + ")"; + return `(${titles.join(' > ')})`; } - const parseSuggestions = (d, lang) => { + const _parseSuggestions = (completionObjs, lang) => { let topics = []; - if (d[1].length > 0) { - topics = d[1].slice(0, 10).map((e) => ({ - title: "# " + e.title + " " + _getFormattedPath(e.key, lang), + if (completionObjs.length > 0) { + topics = completionObjs.slice(0, 10).map((e) => ({ + title: `# ${e.title} ${_getFormattedPath(e.key, lang)}`, key: e.key, })); } - const dummySuggestion = {title: "# Dummy", key: 'dummy'}; - topics = topics.concat([dummySuggestion, dummySuggestion, dummySuggestion, dummySuggestion, dummySuggestion]) return topics; }; @@ -33,37 +32,39 @@ const getSuggestions = async (input) => { const lang = isInputHebrew? 'he' : 'en'; const rawCompletions = await Sefaria.getTopicCompletions(word); - const completionObjects = parseSuggestions(rawCompletions, lang) + const completionObjects = _parseSuggestions(rawCompletions[1], lang) return completionObjects.map((suggestion) => ({ text: suggestion.title, slug: suggestion.key, })); - }; +}; const renderItem = (openTopic, item, index, highlightedIndex, getItemProps)=>{ - const isHighlighted = index === highlightedIndex; - const highlightedClassString = isHighlighted ? "highlighted" : ''; - return ( + const highlightedClassString = classNames({ + highlighted: index === highlightedIndex, + }); + return (
-
{item.text} -
-
- ); +
+ {item.text} +
+
+ ); }; const renderItems = (openTopic, suggestions, highlightedIndex, getItemProps) => { - return ( - <> - {suggestions.map((item, index) => - renderItem(openTopic, item, index, highlightedIndex, getItemProps) - )} - - ); + return ( + <> + {suggestions.map((item, index) => + renderItem(openTopic, item, index, highlightedIndex, getItemProps) + )} + + ); }; @@ -89,15 +90,20 @@ const renderInput = (openTopic, numOfTopics, highlightedIndex, highlightedSugges title={Sefaria._("Search for Texts or Keywords Here")} {...otherInputDownshiftProps} /> -
+
) } export const TopicLandingSearch = ({openTopic, numOfTopics}) => { - return (
- -
+ return ( +
+ +
); }; \ No newline at end of file From 8eaf8e8bf889099dc094102f7b8a181d4bf50c33 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Thu, 21 Nov 2024 12:23:49 +0200 Subject: [PATCH 020/265] chore(topic landing page): remove slicing of completions --- static/js/TopicLandingPage/TopicLandingSearch.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 84285f5b37..bff54b5b24 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -20,7 +20,7 @@ const getSuggestions = async (input) => { const _parseSuggestions = (completionObjs, lang) => { let topics = []; if (completionObjs.length > 0) { - topics = completionObjs.slice(0, 10).map((e) => ({ + topics = completionObjs.map((e) => ({ title: `# ${e.title} ${_getFormattedPath(e.key, lang)}`, key: e.key, })); From 69859ec02336c6b6fe6d956359f9a424ad598fa9 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Sun, 24 Nov 2024 17:53:08 +0200 Subject: [PATCH 021/265] feat(name api): add topic pools to autocompleter titles trie --- sefaria/model/autospell.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index dd1673f082..20c3869e55 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -499,7 +499,8 @@ def add_titles_from_set(self, recordset, all_names_method, primary_name_method, "type": obj.__class__.__name__, "key": tuple(key) if isinstance(key, list) else key, "is_primary": True, - "order": base_order + sub_order + "order": base_order + sub_order, + "pools": obj.get_pools() if obj.__class__.__name__ == 'Topic' else None } titles = getattr(obj, all_names_method)(self.lang) @@ -513,7 +514,8 @@ def add_titles_from_set(self, recordset, all_names_method, primary_name_method, "type": obj.__class__.__name__, "key": tuple(key) if isinstance(key, list) else key, "is_primary": False, - "order": base_order + sub_order + "order": base_order + sub_order, + "pools": obj.get_pools() if obj.__class__.__name__ == 'Topic' else None } From ba7b9e55e7ba4d84529797ebfcfbbbaf6675d50b Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Mon, 25 Nov 2024 12:02:03 +0200 Subject: [PATCH 022/265] fix(topics): force cast to str --- sefaria/model/topic.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 59cfc6198a..e834db90ea 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -187,7 +187,9 @@ def load(self, query, proj=None): def _set_derived_attributes(self): self.set_titles(getattr(self, "titles", None)) slug = getattr(self, "slug", None) - self.pools = list(DjangoTopic.objects.get_pools_by_topic_slug(slug)) if slug is not None else [] + if not isinstance(slug, str): + logger.info(f"Slug must be a str", slug) + self.pools = list(DjangoTopic.objects.get_pools_by_topic_slug(str(slug))) if slug is not None else [] if self.__class__ != Topic and not getattr(self, "subclass", False): # in a subclass. set appropriate "subclass" attribute setattr(self, "subclass", self.reverse_subclass_map[self.__class__.__name__]) From d9558bbde1994bfb33085a1beec899946477dab2 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Mon, 25 Nov 2024 12:47:00 +0200 Subject: [PATCH 023/265] fix(topics): remove pools for now --- sefaria/model/topic.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index e834db90ea..246e5583e8 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -187,9 +187,7 @@ def load(self, query, proj=None): def _set_derived_attributes(self): self.set_titles(getattr(self, "titles", None)) slug = getattr(self, "slug", None) - if not isinstance(slug, str): - logger.info(f"Slug must be a str", slug) - self.pools = list(DjangoTopic.objects.get_pools_by_topic_slug(str(slug))) if slug is not None else [] + self.pools = [] #list(DjangoTopic.objects.get_pools_by_topic_slug(str(slug))) if slug is not None else [] if self.__class__ != Topic and not getattr(self, "subclass", False): # in a subclass. set appropriate "subclass" attribute setattr(self, "subclass", self.reverse_subclass_map[self.__class__.__name__]) From 58d8b2391794db74272a90bb35687451280c903f Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Mon, 25 Nov 2024 13:22:13 +0200 Subject: [PATCH 024/265] fix(topics): add pools back --- sefaria/model/topic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 246e5583e8..d12eab0861 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -187,7 +187,7 @@ def load(self, query, proj=None): def _set_derived_attributes(self): self.set_titles(getattr(self, "titles", None)) slug = getattr(self, "slug", None) - self.pools = [] #list(DjangoTopic.objects.get_pools_by_topic_slug(str(slug))) if slug is not None else [] + self.pools = list(DjangoTopic.objects.get_pools_by_topic_slug(str(slug))) if slug is not None else [] if self.__class__ != Topic and not getattr(self, "subclass", False): # in a subclass. set appropriate "subclass" attribute setattr(self, "subclass", self.reverse_subclass_map[self.__class__.__name__]) From ce3129f06e69963b6ce5ace6f4f1df2b3a9b9c48 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Mon, 25 Nov 2024 14:00:38 +0200 Subject: [PATCH 025/265] fix(topics): dont allow adding or deleting topics --- django_topics/admin.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/django_topics/admin.py b/django_topics/admin.py index 9854e3c3f3..308ab924a2 100644 --- a/django_topics/admin.py +++ b/django_topics/admin.py @@ -67,6 +67,12 @@ class TopicAdmin(admin.ModelAdmin): create_remove_from_pool_action(PoolType.TORAH_TAB.value), ] + def has_add_permission(self, request): + return False + + def has_delete_permission(self, request, obj=None): + return False + def get_queryset(self, request): queryset = super().get_queryset(request) return queryset.filter(pools__name=PoolType.LIBRARY.value) From d2652f4f6143b33c1bea9d3a0035da61c4af37f9 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Mon, 25 Nov 2024 14:01:42 +0200 Subject: [PATCH 026/265] fix(topics): refer to field as topic slug --- django_topics/admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/django_topics/admin.py b/django_topics/admin.py index 308ab924a2..348634e399 100644 --- a/django_topics/admin.py +++ b/django_topics/admin.py @@ -104,7 +104,7 @@ class TopicOfTheDayAdmin(admin.ModelAdmin): def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "topic": - kwargs["label"] = "Topic ID num (not slug)" + kwargs["label"] = "Topic slug" kwargs["help_text"] = "Use the magnifying glass button to select a topic." return super().formfield_for_foreignkey(db_field, request, **kwargs) @@ -161,10 +161,10 @@ class SeasonalTopicAdmin(admin.ModelAdmin): def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "topic": - kwargs["label"] = "Topic ID num (not slug)" + kwargs["label"] = "Topic slug" kwargs["help_text"] = "Use the magnifying glass button to select a topic." if db_field.name == "secondary_topic": - kwargs["label"] = "Secondary Topic ID num (not slug)" + kwargs["label"] = "Secondary topic slug" return super().formfield_for_foreignkey(db_field, request, **kwargs) def save_model(self, request, obj, form, change): From 0957a2fd0a1aadb27a8d101ce8345b9132e6de31 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 25 Nov 2024 15:59:26 +0200 Subject: [PATCH 027/265] feat(name api): extend name_api to limit results by type and topic pool --- reader/views.py | 10 ++++--- sefaria/model/autospell.py | 58 ++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/reader/views.py b/reader/views.py index 853cf8bfe4..e90ed933b9 100644 --- a/reader/views.py +++ b/reader/views.py @@ -2588,7 +2588,7 @@ def _internal_do_post(request, uid): return jsonResponse({"error": "Unsupported HTTP method."}) -def get_name_completions(name, limit, ref_only, topic_override=False): +def get_name_completions(name, limit, ref_only, topic_override=False, type=None, topic_pool=None): lang = "he" if has_hebrew(name) else "en" completer = library.ref_auto_completer(lang) if ref_only else library.full_auto_completer(lang) object_data = None @@ -2614,7 +2614,7 @@ def get_name_completions(name, limit, ref_only, topic_override=False): completion_objects = [o for n in completions for o in lexicon_ac.get_data(n)] else: - completions, completion_objects = completer.complete(name, limit) + completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool) object_data = completer.get_object(name) except DictionaryEntryNotFoundError as e: @@ -2624,7 +2624,7 @@ def get_name_completions(name, limit, ref_only, topic_override=False): completions = list(OrderedDict.fromkeys(t)) # filter out dupes completion_objects = [o for n in completions for o in lexicon_ac.get_data(n)] except InputError: # Not a Ref - completions, completion_objects = completer.complete(name, limit) + completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool) object_data = completer.get_object(name) return { @@ -2653,7 +2653,9 @@ def name_api(request, name): # Number of results to return. 0 indicates no limit LIMIT = int(request.GET.get("limit", 10)) ref_only = bool(int(request.GET.get("ref_only", False))) - completions_dict = get_name_completions(name, LIMIT, ref_only, topic_override) + type = request.GET.get("type", None) + topic_pool = request.GET.get("topic_pool", None) + completions_dict = get_name_completions(name, LIMIT, ref_only, topic_override, type=type, topic_pool=topic_pool) ref = completions_dict["ref"] topic = completions_dict["topic"] d = { diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index 20c3869e55..9026bde2ea 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -12,6 +12,7 @@ from django.contrib.auth.models import User from sefaria.model import * from sefaria.model.schema import SheetLibraryNode +from sefaria.model.tests.topic_test import topic_pool from sefaria.utils import hebrew from sefaria.model.following import aggregate_profiles import re2 as re @@ -201,7 +202,7 @@ def get_data(self, instring): except KeyError: return None - def complete(self, instring, limit=0, redirected=False): + def complete(self, instring, limit=0, redirected=False, type=None, topic_pool=None): """ Wrapper for Completions object - prioritizes and aggregates completion results. In the case where there are no results, tries to swap keyboards and get completion results from the other language. @@ -215,7 +216,7 @@ def complete(self, instring, limit=0, redirected=False): if len(instring) >= self.max_completion_length: return [], [] cm = Completions(self, self.lang, instring, limit, - do_autocorrect=len(instring) < self.max_autocorrect_length) + do_autocorrect=len(instring) < self.max_autocorrect_length, type=type, topic_pool=topic_pool) cm.process() if cm.has_results(): return cm.get_completion_strings(), cm.get_completion_objects() @@ -247,7 +248,7 @@ def next_steps_from_node(self, instring): class Completions(object): - def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = True): + def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = True, type=None, topic_pool=None): """ An object that contains a single search, delegates to different methods of completions, and aggregates results. :param auto_completer: @@ -272,6 +273,17 @@ def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = Tru self._completion_objects = [] self._candidate_type_counters = defaultdict(int) self._type_limit = 3 + self.type = type + self._type_norm_map = { + "Collection": "Collection", + "AuthorTopic": "Topic", + "TocCategory": "TocCategory", + "PersonTopic": "Topic", + "Topic": "Topic", + "ref": "ref", + "Term": "Term", + "User": "User"} + self.topic_pool = topic_pool def has_results(self): return len(self._completion_objects) > 0 @@ -319,12 +331,36 @@ def _candidate_order(self, c): else: return c[1]["order"] * 100 + def _filter_suggestions_by_type(self, suggestions): + if not self.type: + return suggestions + + filtered_suggestions = [suggestion for suggestion in suggestions if self._type_norm_map[suggestion[1]["type"]] == self.type] + if not self.topic_pool: + return filtered_suggestions + pool_filtered_suggestions = [suggestion for suggestion in filtered_suggestions if + self.topic_pool in suggestion[1]['topic_pools']] + return pool_filtered_suggestions + + def _filter_values_by_type(self, values): + if not self.type: + return values + + filtered_values = [value for value in values if self._type_norm_map[value["type"]] == self.type] + if not self.topic_pool: + return filtered_values + pool_filtered_values = [value for value in filtered_values if + self.topic_pool in value['topic_pools']] + return pool_filtered_values + + def _collect_candidates(self): # Match titles that begin exactly this way [cs, co] = self.get_new_continuations_from_string(self.normal_string) joined = list(zip(cs, co)) if len(joined): + joined = self._filter_suggestions_by_type(joined) # joined.sort(key=lambda w: w[1]["order"]) joined.sort(key=self._candidate_order) self._raw_completion_strings, self._completion_objects = [list(_) for _ in zip(*joined)] @@ -343,7 +379,12 @@ def _collect_candidates(self): # single misspellings single_edits = self.auto_completer.spell_checker.single_edits(self.normal_string) for edit in single_edits: - [cs, co] = self.get_new_continuations_from_string(edit) + cs, co = self.get_new_continuations_from_string(edit) + filtered_suggestions = self._filter_suggestions_by_type(zip(cs, co)) + if filtered_suggestions: + cs, co = zip(*filtered_suggestions) + else: + cs, co = [], [] self._raw_completion_strings += cs self._completion_objects += co if self._is_past_limit(): @@ -368,10 +409,15 @@ def _collect_candidates_later_in_string(self, do_autocorrect=True): k = normalizer(self.lang)(suggestion) try: all_v = self.auto_completer.title_trie[k] + all_v = self._filter_values_by_type(all_v) except KeyError: all_v = [] for v in all_v: if (v["type"], v["key"]) not in self.keys_covered: + # if self.type and self._type_norm_map[v["type"]] != self.type: + # break + # if self.topic_pool and self.topic_pool not in v['topic_pools']: + # break self._completion_objects += [v] self._raw_completion_strings += [v["title"]] self.keys_covered.add((v["type"], v["key"])) @@ -500,7 +546,7 @@ def add_titles_from_set(self, recordset, all_names_method, primary_name_method, "key": tuple(key) if isinstance(key, list) else key, "is_primary": True, "order": base_order + sub_order, - "pools": obj.get_pools() if obj.__class__.__name__ == 'Topic' else None + "topic_pools": obj.get_pools() if obj.__class__.__name__ == 'Topic' else [] } titles = getattr(obj, all_names_method)(self.lang) @@ -515,7 +561,7 @@ def add_titles_from_set(self, recordset, all_names_method, primary_name_method, "key": tuple(key) if isinstance(key, list) else key, "is_primary": False, "order": base_order + sub_order, - "pools": obj.get_pools() if obj.__class__.__name__ == 'Topic' else None + "topic_pools": obj.get_pools() if obj.__class__.__name__ == 'Topic' else [] } From 9b23efb53aff5685520b5ec64c4a21809ef2e946 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 25 Nov 2024 16:37:04 +0200 Subject: [PATCH 028/265] feat(topic landing page): connect frontend Topic search to use renewed name_api endpoint --- sefaria/model/autospell.py | 6 +++--- static/js/TopicLandingPage/TopicLandingSearch.jsx | 6 ++++-- static/js/sefaria/sefaria.js | 4 +++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index 9026bde2ea..fa5ef2dfed 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -359,8 +359,8 @@ def _collect_candidates(self): [cs, co] = self.get_new_continuations_from_string(self.normal_string) joined = list(zip(cs, co)) + joined = self._filter_suggestions_by_type(joined) if len(joined): - joined = self._filter_suggestions_by_type(joined) # joined.sort(key=lambda w: w[1]["order"]) joined.sort(key=self._candidate_order) self._raw_completion_strings, self._completion_objects = [list(_) for _ in zip(*joined)] @@ -380,8 +380,8 @@ def _collect_candidates(self): single_edits = self.auto_completer.spell_checker.single_edits(self.normal_string) for edit in single_edits: cs, co = self.get_new_continuations_from_string(edit) - filtered_suggestions = self._filter_suggestions_by_type(zip(cs, co)) - if filtered_suggestions: + filtered_suggestions = list(self._filter_suggestions_by_type(zip(cs, co))) + if len(filtered_suggestions): cs, co = zip(*filtered_suggestions) else: cs, co = [], [] diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index bff54b5b24..ce7a29a8d7 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -31,8 +31,10 @@ const getSuggestions = async (input) => { const isInputHebrew = Sefaria.hebrew.isHebrew(word); const lang = isInputHebrew? 'he' : 'en'; - const rawCompletions = await Sefaria.getTopicCompletions(word); - const completionObjects = _parseSuggestions(rawCompletions[1], lang) + // const rawCompletions = await Sefaria.getTopicCompletions(word); + const rawCompletions = await Sefaria.getName(word, undefined, 20, "Topic", "library") + // const completionObjects = _parseSuggestions(rawCompletions[1], lang) + const completionObjects = _parseSuggestions(rawCompletions["completion_objects"], lang) return completionObjects.map((suggestion) => ({ text: suggestion.title, slug: suggestion.key, diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index be2da1ea30..a60dc44b14 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1206,11 +1206,13 @@ Sefaria = extend(Sefaria, { _lookups: {}, // getName w/ refOnly true should work as a replacement for parseRef - it uses a callback rather than return value. Besides that - same data. - getName: function(name, refOnly = false, limit = undefined) { + getName: function(name, refOnly = false, limit = undefined, type=undefined, topicPool=undefined) { const trimmed_name = name.trim(); let params = {}; if (refOnly) { params["ref_only"] = 1; } if (limit != undefined) { params["limit"] = limit; } + if (type != undefined) { params["type"] = type; } + if (topicPool != undefined) { params["topic_pool"] = topicPool; } let queryString = Object.keys(params).map(key => key + '=' + params[key]).join('&'); queryString = (queryString ? "?" + queryString : ""); return this._cachedApiPromise({ From da3a2f0b990e0f8839f5e5cfd0ba54dde06ac189 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 25 Nov 2024 19:00:54 +0200 Subject: [PATCH 029/265] feat(topic landing page): adapt autocomplete to new design --- static/css/s2.css | 9 ++++++++- static/js/GeneralAutocomplete.jsx | 2 +- static/js/TopicLandingPage/TopicLandingSearch.jsx | 10 ++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 7714c9d042..0700dda050 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4875,7 +4875,8 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus display: flex; list-style-type: none; padding: 6px 12px; - font-family: 'EB Garamond'; + /*font-family: 'EB Garamond';*/ + font-family: 'Adobe Garamond Pro'; font-style: normal; font-weight: 400; font-size: 18px; @@ -4886,6 +4887,12 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus max-width: 100%; min-height: 10px; } +.topic-landing-search-suggestion-title{ + font-weight: 600; +} +.topic-landing-search-suggestion-category-path{ + color: #333333; +} .topic-landing-search-suggestion.highlighted{ background-color: #EDEDEC; diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index 50d1f93195..3a9c85a864 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -49,7 +49,7 @@ export const GeneralAutocomplete = ({ {...getMenuProps()} className={dropdownMenuClassString} > - {(shouldDisplaySuggestions(isOpen)) && renderItems(suggestions, highlightedIndex, getItemProps, getInputProps)} + {(shouldDisplaySuggestions(isOpen) || true) && renderItems(suggestions, highlightedIndex, getItemProps, getInputProps)}
); diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index ce7a29a8d7..d1c1e7c7f3 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -10,6 +10,10 @@ const getSuggestions = async (input) => { } const word = input.trim(); + const _capitalizeFirstLetter = (text)=> { + return String(text).charAt(0).toUpperCase() + String(text).slice(1); + } + const _getFormattedPath = (slug, lang) => { const categories = Sefaria.topicTocCategories(slug); if(!categories){return ""} @@ -21,7 +25,8 @@ const getSuggestions = async (input) => { let topics = []; if (completionObjs.length > 0) { topics = completionObjs.map((e) => ({ - title: `# ${e.title} ${_getFormattedPath(e.key, lang)}`, + title: `#${_capitalizeFirstLetter(e.title)}`, + categoryPathTitle: `${_getFormattedPath(e.key, lang)}`, key: e.key, })); } @@ -37,6 +42,7 @@ const getSuggestions = async (input) => { const completionObjects = _parseSuggestions(rawCompletions["completion_objects"], lang) return completionObjects.map((suggestion) => ({ text: suggestion.title, + categoryText: suggestion.categoryPathTitle, slug: suggestion.key, })); }; @@ -53,7 +59,7 @@ const renderItem = (openTopic, item, index, highlightedIndex, getItemProps)=>{ key={item.slug} {...getItemProps({index})} > - {item.text} + {item.text}  {item.categoryText} ); From 6d5d8407fbd9ce98ff38196218d1f28e9482c082 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 25 Nov 2024 19:03:04 +0200 Subject: [PATCH 030/265] chore(topic landing page): remove debug set-up from GeneralAutocomplete.jsx --- static/js/GeneralAutocomplete.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/GeneralAutocomplete.jsx b/static/js/GeneralAutocomplete.jsx index 3a9c85a864..1e2562f920 100644 --- a/static/js/GeneralAutocomplete.jsx +++ b/static/js/GeneralAutocomplete.jsx @@ -49,7 +49,7 @@ export const GeneralAutocomplete = ({ {...getMenuProps()} className={dropdownMenuClassString} > - {(shouldDisplaySuggestions(isOpen) || true) && renderItems(suggestions, highlightedIndex, getItemProps, getInputProps)} + {shouldDisplaySuggestions(isOpen) && renderItems(suggestions, highlightedIndex, getItemProps, getInputProps)} ); From 98e0ecb36415816978154fc27d10652a66dec8aa Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 26 Nov 2024 11:20:21 +0200 Subject: [PATCH 031/265] refactor(name api): cleaner way to implement filtering out of suggestions in name_api --- sefaria/model/autospell.py | 56 ++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index fa5ef2dfed..96ba30a98d 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -8,6 +8,7 @@ from typing import List, Iterable import math import datrie +from sympy import false from unidecode import unidecode from django.contrib.auth.models import User from sefaria.model import * @@ -331,35 +332,39 @@ def _candidate_order(self, c): else: return c[1]["order"] * 100 - def _filter_suggestions_by_type(self, suggestions): - if not self.type: - return suggestions - filtered_suggestions = [suggestion for suggestion in suggestions if self._type_norm_map[suggestion[1]["type"]] == self.type] - if not self.topic_pool: - return filtered_suggestions - pool_filtered_suggestions = [suggestion for suggestion in filtered_suggestions if - self.topic_pool in suggestion[1]['topic_pools']] - return pool_filtered_suggestions + def _filter_completions_by_type(self, completion_strings, completion_objects): + filtered_completion_strings = [] + filtered_completion_objects = [] + for cs, co in zip(completion_strings, completion_objects): + if self._has_required_type(co): + filtered_completion_strings.append(cs) + filtered_completion_objects.append(co) + return filtered_completion_strings, filtered_completion_objects - def _filter_values_by_type(self, values): - if not self.type: - return values - filtered_values = [value for value in values if self._type_norm_map[value["type"]] == self.type] - if not self.topic_pool: - return filtered_values - pool_filtered_values = [value for value in filtered_values if - self.topic_pool in value['topic_pools']] - return pool_filtered_values + def _has_required_type(self, completion_object): + co_type = completion_object["type"] + if not self.type: + return True + if self._type_norm_map[co_type] == self.type: + if self._type_norm_map[co_type] != 'Topic' or not self.topic_pool: + return True + else: + if self.topic_pool in completion_object["topic_pools"]: + return True + else: + return False + else: + return False def _collect_candidates(self): # Match titles that begin exactly this way [cs, co] = self.get_new_continuations_from_string(self.normal_string) + cs, co = self._filter_completions_by_type(cs, co) joined = list(zip(cs, co)) - joined = self._filter_suggestions_by_type(joined) if len(joined): # joined.sort(key=lambda w: w[1]["order"]) joined.sort(key=self._candidate_order) @@ -380,11 +385,8 @@ def _collect_candidates(self): single_edits = self.auto_completer.spell_checker.single_edits(self.normal_string) for edit in single_edits: cs, co = self.get_new_continuations_from_string(edit) - filtered_suggestions = list(self._filter_suggestions_by_type(zip(cs, co))) - if len(filtered_suggestions): - cs, co = zip(*filtered_suggestions) - else: - cs, co = [], [] + cs, co = self._filter_completions_by_type(cs, co) + self._raw_completion_strings += cs self._completion_objects += co if self._is_past_limit(): @@ -409,15 +411,11 @@ def _collect_candidates_later_in_string(self, do_autocorrect=True): k = normalizer(self.lang)(suggestion) try: all_v = self.auto_completer.title_trie[k] - all_v = self._filter_values_by_type(all_v) + _, all_v = self._filter_completions_by_type([0] * len(all_v), all_v) except KeyError: all_v = [] for v in all_v: if (v["type"], v["key"]) not in self.keys_covered: - # if self.type and self._type_norm_map[v["type"]] != self.type: - # break - # if self.topic_pool and self.topic_pool not in v['topic_pools']: - # break self._completion_objects += [v] self._raw_completion_strings += [v["title"]] self.keys_covered.add((v["type"], v["key"])) From be06cabaee5b3a0982e4f98ae0ed7809f3ab628a Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 26 Nov 2024 11:22:31 +0200 Subject: [PATCH 032/265] chore(name api): remove frivolous import --- sefaria/model/autospell.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index 96ba30a98d..f72f3f15ad 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -8,7 +8,6 @@ from typing import List, Iterable import math import datrie -from sympy import false from unidecode import unidecode from django.contrib.auth.models import User from sefaria.model import * From 699953df69f7f2c0bc83aac031fc763bda8de15d Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 26 Nov 2024 11:27:50 +0200 Subject: [PATCH 033/265] chore(name api): remove commented out code --- static/js/TopicLandingPage/TopicLandingSearch.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index d1c1e7c7f3..85eff5aba8 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -36,9 +36,7 @@ const getSuggestions = async (input) => { const isInputHebrew = Sefaria.hebrew.isHebrew(word); const lang = isInputHebrew? 'he' : 'en'; - // const rawCompletions = await Sefaria.getTopicCompletions(word); const rawCompletions = await Sefaria.getName(word, undefined, 20, "Topic", "library") - // const completionObjects = _parseSuggestions(rawCompletions[1], lang) const completionObjects = _parseSuggestions(rawCompletions["completion_objects"], lang) return completionObjects.map((suggestion) => ({ text: suggestion.title, From 7ad48611e56685339bbb49f1eeed12fe8d3670f3 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 26 Nov 2024 15:27:02 +0200 Subject: [PATCH 034/265] refactor(name_api): PR review fixes --- sefaria/model/autospell.py | 61 ++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index f72f3f15ad..3a612dd75b 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -248,6 +248,17 @@ def next_steps_from_node(self, instring): class Completions(object): + + _type_norm_map = { + "Collection": "Collection", + "AuthorTopic": "Topic", + "TocCategory": "TocCategory", + "PersonTopic": "Topic", + "Topic": "Topic", + "ref": "ref", + "Term": "Term", + "User": "User"} + def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = True, type=None, topic_pool=None): """ An object that contains a single search, delegates to different methods of completions, and aggregates results. @@ -274,15 +285,6 @@ def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = Tru self._candidate_type_counters = defaultdict(int) self._type_limit = 3 self.type = type - self._type_norm_map = { - "Collection": "Collection", - "AuthorTopic": "Topic", - "TocCategory": "TocCategory", - "PersonTopic": "Topic", - "Topic": "Topic", - "ref": "ref", - "Term": "Term", - "User": "User"} self.topic_pool = topic_pool def has_results(self): @@ -331,36 +333,31 @@ def _candidate_order(self, c): else: return c[1]["order"] * 100 - def _filter_completions_by_type(self, completion_strings, completion_objects): - filtered_completion_strings = [] - filtered_completion_objects = [] - for cs, co in zip(completion_strings, completion_objects): - if self._has_required_type(co): - filtered_completion_strings.append(cs) - filtered_completion_objects.append(co) - return filtered_completion_strings, filtered_completion_objects - + filtered_completions = ( + (cs, co) for cs, co in zip(completion_strings, completion_objects) if self._has_required_type(co) + ) + return list(zip(*filtered_completions)) if any(filtered_completions) else ([], []) def _has_required_type(self, completion_object): - co_type = completion_object["type"] if not self.type: return True - if self._type_norm_map[co_type] == self.type: - if self._type_norm_map[co_type] != 'Topic' or not self.topic_pool: - return True - else: - if self.topic_pool in completion_object["topic_pools"]: - return True - else: - return False - else: + + co_type = completion_object["type"] + normalized_type = self._type_norm_map[co_type] + + if normalized_type != self.type: return False + if normalized_type == 'Topic' and self.topic_pool: + return self.topic_pool in completion_object["topic_pools"] + + return True + def _collect_candidates(self): # Match titles that begin exactly this way - [cs, co] = self.get_new_continuations_from_string(self.normal_string) + cs, co = self.get_new_continuations_from_string(self.normal_string) cs, co = self._filter_completions_by_type(cs, co) joined = list(zip(cs, co)) @@ -410,7 +407,7 @@ def _collect_candidates_later_in_string(self, do_autocorrect=True): k = normalizer(self.lang)(suggestion) try: all_v = self.auto_completer.title_trie[k] - _, all_v = self._filter_completions_by_type([0] * len(all_v), all_v) + _, all_v = self._filter_completions_by_type(all_v, all_v) except KeyError: all_v = [] for v in all_v: @@ -543,7 +540,7 @@ def add_titles_from_set(self, recordset, all_names_method, primary_name_method, "key": tuple(key) if isinstance(key, list) else key, "is_primary": True, "order": base_order + sub_order, - "topic_pools": obj.get_pools() if obj.__class__.__name__ == 'Topic' else [] + "topic_pools": obj.get_pools() if isinstance(obj, Topic) else [] } titles = getattr(obj, all_names_method)(self.lang) @@ -558,7 +555,7 @@ def add_titles_from_set(self, recordset, all_names_method, primary_name_method, "key": tuple(key) if isinstance(key, list) else key, "is_primary": False, "order": base_order + sub_order, - "topic_pools": obj.get_pools() if obj.__class__.__name__ == 'Topic' else [] + "topic_pools": obj.get_pools() if isinstance(obj, Topic) else [] } From 9d8dddc73036b0a713c8e143ad4b8ed95f4dfa5f Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 26 Nov 2024 16:16:08 +0200 Subject: [PATCH 035/265] refactor(name_api backend and clients): do away with ref only param --- reader/views.py | 11 +++++------ sefaria/model/autospell.py | 11 +++++++---- static/js/AboutSheet.jsx | 2 +- static/js/AdminEditor.jsx | 2 +- static/js/Editor.jsx | 2 +- static/js/SheetMetadata.jsx | 2 +- static/js/SourceEditor.jsx | 2 +- static/js/TopicLandingPage/TopicLandingSearch.jsx | 2 +- static/js/categorize_sheets.jsx | 2 +- static/js/s1/editor.js | 2 +- static/js/sefaria/sefaria.js | 6 +++--- static/js/sefaria/util.js | 4 ++-- static/js/sheets.js | 4 ++-- 13 files changed, 27 insertions(+), 25 deletions(-) diff --git a/reader/views.py b/reader/views.py index e90ed933b9..f9e34c13eb 100644 --- a/reader/views.py +++ b/reader/views.py @@ -1625,7 +1625,7 @@ def search_autocomplete_redirecter(request): query = request.GET.get("q", "") topic_override = query.startswith('#') query = query[1:] if topic_override else query - completions_dict = get_name_completions(query, 1, False, topic_override) + completions_dict = get_name_completions(query, 1, topic_override) ref = completions_dict['ref'] object_data = completions_dict['object_data'] if ref: @@ -1643,7 +1643,7 @@ def search_autocomplete_redirecter(request): def opensearch_suggestions_api(request): # see here for docs: http://www.opensearch.org/Specifications/OpenSearch/Extensions/Suggestions/1.1 query = request.GET.get("q", "") - completions_dict = get_name_completions(query, 5, False) + completions_dict = get_name_completions(query, 5) ret_data = [ query, completions_dict["completions"] @@ -2588,9 +2588,9 @@ def _internal_do_post(request, uid): return jsonResponse({"error": "Unsupported HTTP method."}) -def get_name_completions(name, limit, ref_only, topic_override=False, type=None, topic_pool=None): +def get_name_completions(name, limit, topic_override=False, type=None, topic_pool=None): lang = "he" if has_hebrew(name) else "en" - completer = library.ref_auto_completer(lang) if ref_only else library.full_auto_completer(lang) + completer = library.full_auto_completer(lang) object_data = None ref = None topic = None @@ -2652,10 +2652,9 @@ def name_api(request, name): name = name[1:] if topic_override else name # Number of results to return. 0 indicates no limit LIMIT = int(request.GET.get("limit", 10)) - ref_only = bool(int(request.GET.get("ref_only", False))) type = request.GET.get("type", None) topic_pool = request.GET.get("topic_pool", None) - completions_dict = get_name_completions(name, LIMIT, ref_only, topic_override, type=type, topic_pool=topic_pool) + completions_dict = get_name_completions(name, LIMIT, topic_override, type=type, topic_pool=topic_pool) ref = completions_dict["ref"] topic = completions_dict["topic"] d = { diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index 3a612dd75b..84fb8980ac 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -334,10 +334,13 @@ def _candidate_order(self, c): return c[1]["order"] * 100 def _filter_completions_by_type(self, completion_strings, completion_objects): - filtered_completions = ( - (cs, co) for cs, co in zip(completion_strings, completion_objects) if self._has_required_type(co) - ) - return list(zip(*filtered_completions)) if any(filtered_completions) else ([], []) + filtered_completions = [ + (cs, co) + for cs, co in zip(completion_strings, completion_objects) + if self._has_required_type(co) + ] + list1, list2 = zip(*filtered_completions) if filtered_completions else ([], []) + return list(list1), list(list2) def _has_required_type(self, completion_object): if not self.type: diff --git a/static/js/AboutSheet.jsx b/static/js/AboutSheet.jsx index 2c1a9454ca..0ee8cd0860 100644 --- a/static/js/AboutSheet.jsx +++ b/static/js/AboutSheet.jsx @@ -67,7 +67,7 @@ const AboutSheet = ({ masterPanelSheetId, toggleSignUpModal }) => { const updateSuggestedTags = (input) => { if (input == "") return - Sefaria.getName(input, false, 0).then(d => { + Sefaria.getName(input, 0).then(d => { const topics = d.completion_objects .filter(obj => obj.type === "Topic") .map((filteredObj, index) => ({ diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 0aade1c9e4..16ab59b37d 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -103,7 +103,7 @@ const validateMarkdownLinks = async (input) => { d = await Sefaria.getTopicCompletions(name, (x) => x[1]); } else { - d = await Sefaria.getName(name, false); + d = await Sefaria.getName(name); if (d.is_ref) { continue; } diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index 73cd538567..f5e3fd7d15 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -943,7 +943,7 @@ const AddInterfaceInput = ({ inputType, resetInterface }) => { if (input === "") { return results; } - const d = await Sefaria.getName(input, true, 5); + const d = await Sefaria.getName(input, 5, 'ref'); if (d.is_section || d.is_segment) { results.helperPromptText = null; results.currentSuggestions = null; diff --git a/static/js/SheetMetadata.jsx b/static/js/SheetMetadata.jsx index a6a04d2c5f..6da073db68 100644 --- a/static/js/SheetMetadata.jsx +++ b/static/js/SheetMetadata.jsx @@ -256,7 +256,7 @@ class SheetMetadata extends Component { updateSuggestedTags(input) { if (input == "") return - Sefaria.getName(input, false, 0).then(d => { + Sefaria.getName(input, 0).then(d => { const topics = d.completion_objects .filter(obj => obj.type === "Topic") .map((filteredObj, index) => ({ diff --git a/static/js/SourceEditor.jsx b/static/js/SourceEditor.jsx index dbf7b2d0c3..4b1f1219f7 100644 --- a/static/js/SourceEditor.jsx +++ b/static/js/SourceEditor.jsx @@ -73,7 +73,7 @@ const SourceEditor = ({topic, close, origData={}}) => { if (input === "") { // this occurs when there was text in the inputbox and user just erased it return results; } - const d = await Sefaria.getName(input, true, 5); + const d = await Sefaria.getName(input, 0, 'ref'); if (d.is_section || d.is_segment) { results.helperPromptText = null; results.currentSuggestions = null; diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 85eff5aba8..0ddb9c9a23 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -36,7 +36,7 @@ const getSuggestions = async (input) => { const isInputHebrew = Sefaria.hebrew.isHebrew(word); const lang = isInputHebrew? 'he' : 'en'; - const rawCompletions = await Sefaria.getName(word, undefined, 20, "Topic", "library") + const rawCompletions = await Sefaria.getName(word,20, "Topic", "library") const completionObjects = _parseSuggestions(rawCompletions["completion_objects"], lang) return completionObjects.map((suggestion) => ({ text: suggestion.title, diff --git a/static/js/categorize_sheets.jsx b/static/js/categorize_sheets.jsx index 0e7e290764..5eb6b33e4a 100644 --- a/static/js/categorize_sheets.jsx +++ b/static/js/categorize_sheets.jsx @@ -51,7 +51,7 @@ class SheetCategorizer extends React.Component { updateSuggestedTags(input) { if (input == "") return; - Sefaria.getName(input, false, 0) + Sefaria.getName(input, 0) .then((d) => { const topics = d.completion_objects .filter((obj) => obj.type === "Topic") diff --git a/static/js/s1/editor.js b/static/js/s1/editor.js index 93925068ab..b0d03b785b 100644 --- a/static/js/s1/editor.js +++ b/static/js/s1/editor.js @@ -635,7 +635,7 @@ $(function() { $("#newTextOK").click(function(){ var ref = $("#newTextName").val(); - Sefaria.getName(ref, true) + Sefaria.getName(ref, undefined, 'ref') .then(function(q) { if(!q.is_ref) { // This is an unknown text diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index a60dc44b14..5d75f39f1e 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1129,7 +1129,7 @@ Sefaria = extend(Sefaria, { }, postSegment: function(ref, versionTitle, language, text, success, error) { if (!versionTitle || !language) { return; } - this.getName(ref, true) + this.getName(ref, undefined, 'ref') .then(data => { if (!data.is_segment) { return; } var d = {json: JSON.stringify({ @@ -1206,10 +1206,10 @@ Sefaria = extend(Sefaria, { _lookups: {}, // getName w/ refOnly true should work as a replacement for parseRef - it uses a callback rather than return value. Besides that - same data. - getName: function(name, refOnly = false, limit = undefined, type=undefined, topicPool=undefined) { + getName: function(name, limit = undefined, type=undefined, topicPool=undefined) { const trimmed_name = name.trim(); let params = {}; - if (refOnly) { params["ref_only"] = 1; } + // if (refOnly) { params["ref_only"] = 1; } if (limit != undefined) { params["limit"] = limit; } if (type != undefined) { params["type"] = type; } if (topicPool != undefined) { params["topic_pool"] = topicPool; } diff --git a/static/js/sefaria/util.js b/static/js/sefaria/util.js index 31c5406d80..645179424e 100644 --- a/static/js/sefaria/util.js +++ b/static/js/sefaria/util.js @@ -842,7 +842,7 @@ class Util { }.bind(this)) .autocomplete({ source: function(request, response) { - Sefaria.getName(request.term, true) + Sefaria.getName(request.term, undefined, 'ref') .then(d => d.completions) .then(response); }, @@ -938,7 +938,7 @@ Util.RefValidator.prototype = { }, _lookupAndRoute: function(inString) { if (this.current_lookup_ajax) {this.current_lookup_ajax.cancel();} - this.current_lookup_ajax = Sefaria.makeCancelable(Sefaria.getName(inString, true)); + this.current_lookup_ajax = Sefaria.makeCancelable(Sefaria.getName(inString, undefined, 'ref')); this.current_lookup_ajax.promise.then(data => { // If this query has been outpaced by typing, just return. if (this.$input.val() != inString) { this.current_lookup_ajax = null; return; } diff --git a/static/js/sheets.js b/static/js/sheets.js index ca955eff1b..b367e8f652 100755 --- a/static/js/sheets.js +++ b/static/js/sheets.js @@ -160,7 +160,7 @@ $(function() { $(document).on("click", "#inlineAddSourceOK", function() { var $target = $("#addInterface").prev(".sheetItem"); var ref = $("#inlineAdd").val(); - Sefaria.getName(ref, true).then(function(q) { + Sefaria.getName(ref, undefined, 'ref').then(function(q) { addSource(q, undefined, "insert", $target); $('#inlineAdd').val(''); $("#inlineTextPreview").html(""); @@ -2123,7 +2123,7 @@ sjs.sheetTagger = { }) .autocomplete({ source: function(request, response) { - Sefaria.getName(request.term, false, 0) + Sefaria.getName(request.term, 0) .then(function(d) { var topics = []; d.completion_objects.map(function(obj) { From 9aedfd7e7d2e4a4a42f5a16a61ca0020bb72ba7c Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 26 Nov 2024 17:38:56 +0200 Subject: [PATCH 036/265] refactor(admin topic search in reader): modify search to use the name api instead of the topic completion api --- static/js/TopicSearch.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/static/js/TopicSearch.jsx b/static/js/TopicSearch.jsx index 6f4578805d..715e809fe1 100644 --- a/static/js/TopicSearch.jsx +++ b/static/js/TopicSearch.jsx @@ -26,17 +26,18 @@ class TopicSearch extends Component { return results; } const word = input.trim(); - const callback = (d) => { + const parseCompletions = (rawCompletionsObjs) => { let topics = []; - if (d[1].length > 0) { - topics = d[1].slice(0, 4).map(function (e) { + if (rawCompletionsObjs.length > 0) { + topics = rawCompletionsObjs.slice(0, 4).map(function (e) { return {title: e.title, key: e.key} }); } topics.push({title: this.props.createNewTopicStr+word, key: ""}) return topics; }; - const completion_objects = await Sefaria.getTopicCompletions(word, callback); + const rawCompletions = await Sefaria.getName(word, undefined, 'Topic'); + const completion_objects = parseCompletions(rawCompletions['completion_objects']) results.currentSuggestions = completion_objects .map(suggestion => ({ name: suggestion.title, From 919b3ebc75c9653024253cc8d0b9b5aa6793e90c Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Wed, 27 Nov 2024 00:00:45 +0200 Subject: [PATCH 037/265] refactor(topic completion): deprecate topic completion endpoint as well as topic autocomplete object --- reader/startup.py | 2 -- reader/views.py | 13 +++---------- sefaria/model/text.py | 19 ------------------- sefaria/urls.py | 1 - sefaria/views.py | 1 - static/js/BookPage.jsx | 4 ++-- static/js/TopicSearch.jsx | 2 +- static/js/sefaria/sefaria.js | 8 -------- 8 files changed, 6 insertions(+), 44 deletions(-) diff --git a/reader/startup.py b/reader/startup.py index bbdf519c1d..39927f751e 100644 --- a/reader/startup.py +++ b/reader/startup.py @@ -27,8 +27,6 @@ def init_library_cache(): logger.info("Initializing Cross Lexicon Auto Completer") library.build_cross_lexicon_auto_completer() - logger.info("Initializing Topic Auto Completer") - library.build_topic_auto_completer() if settings.ENABLE_LINKER: logger.info("Initializing Linker") diff --git a/reader/views.py b/reader/views.py index f9e34c13eb..0e54d80c40 100644 --- a/reader/views.py +++ b/reader/views.py @@ -2637,13 +2637,6 @@ def get_name_completions(name, limit, topic_override=False, type=None, topic_poo } -@catch_error_as_json -def topic_completion_api(request, topic): - limit = int(request.GET.get("limit", 10)) - result = library.topic_auto_completer().complete(topic, limit=limit) - return jsonResponse(result) - - @catch_error_as_json def name_api(request, name): if request.method != "GET": @@ -3124,7 +3117,7 @@ def add_new_topic_api(request): t.image = data["image"] t.save() - library.build_topic_auto_completer() + library.build_full_auto_completer() library.get_topic_toc(rebuild=True) library.get_topic_toc_json(rebuild=True) library.get_topic_toc_category_mapping(rebuild=True) @@ -3141,7 +3134,7 @@ def delete_topic(request, topic): topic_obj = Topic().load({"slug": topic}) if topic_obj: topic_obj.delete() - library.build_topic_auto_completer() + library.build_full_auto_completer() library.get_topic_toc(rebuild=True) library.get_topic_toc_json(rebuild=True) library.get_topic_toc_category_mapping(rebuild=True) @@ -3176,7 +3169,7 @@ def topics_api(request, topic, v2=False): author_status_changed = (topic_data["category"] == "authors") ^ (topic_data["origCategory"] == "authors") topic = update_topic(topic, **topic_data) if author_status_changed: - library.build_topic_auto_completer() + library.build_full_auto_completer() def protected_index_post(request): return jsonResponse(topic.contents()) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 3bc15026e3..44a51b692d 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -4948,7 +4948,6 @@ def __init__(self): self._ref_auto_completer = {} self._lexicon_auto_completer = {} self._cross_lexicon_auto_completer = None - self._topic_auto_completer = {} # Term Mapping self._simple_term_mapping = {} @@ -5338,24 +5337,6 @@ def build_cross_lexicon_auto_completer(self): self._cross_lexicon_auto_completer = AutoCompleter("he", library, include_titles=False, include_lexicons=True) self._cross_lexicon_auto_completer_is_ready = True - def build_topic_auto_completer(self): - """ - Builds the topic auto complete including topics with no sources - """ - from .autospell import AutoCompleter - self._topic_auto_completer = AutoCompleter("en", library, include_topics=True, include_titles=False, min_topics=0) - self._topic_auto_completer_is_ready = True - - def topic_auto_completer(self): - """ - Returns the topic auto completer. If the auto completer was not initially loaded, - it rebuilds before returning, emitting warnings to the logger. - """ - if self._topic_auto_completer is None: - logger.warning("Failed to load topic auto completer. rebuilding") - self.build_topic_auto_completer() - logger.warning("Built topic auto completer") - return self._topic_auto_completer def cross_lexicon_auto_completer(self): """ diff --git a/sefaria/urls.py b/sefaria/urls.py index fb95509e47..f07ec2338e 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -102,7 +102,6 @@ url(r'^topics/?$', reader_views.topics_page), url(r'^topics/b/(?P.+)$', reader_views.topic_page_b), url(r'^topics/(?P.+)$', reader_views.topic_page), - url(r'^api/topic/completion/(?P.+)', reader_views.topic_completion_api), url(r'^api/topics/images/(?P.+)$', reader_views.topic_upload_photo) ] diff --git a/sefaria/views.py b/sefaria/views.py index 80f3939e0d..27a88c4ca1 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -676,7 +676,6 @@ def rebuild_auto_completer(request): library.build_ref_auto_completer() library.build_lexicon_auto_completers() library.build_cross_lexicon_auto_completer() - library.build_topic_auto_completer() if MULTISERVER_ENABLED: server_coordinator.publish_event("library", "build_full_auto_completer") diff --git a/static/js/BookPage.jsx b/static/js/BookPage.jsx index 4c87581a41..bce9b2bf77 100644 --- a/static/js/BookPage.jsx +++ b/static/js/BookPage.jsx @@ -1219,8 +1219,8 @@ const EditTextInfo = function({initTitle, close}) { } const addAuthor = function (newAuthor) { const lowerCaseName = newAuthor.name.toLowerCase(); - Sefaria._ApiPromise(Sefaria.apiHost + "/api/topic/completion/" + newAuthor.name).then(d => { - const matches = d[1].filter((t) => t.type === 'AuthorTopic'); + Sefaria._ApiPromise(Sefaria.apiHost + "/api/name/" + newAuthor.name).then(d => { + const matches = d['completion_objects'].filter((t) => t.type === 'AuthorTopic'); const exactMatch = matches.find((t) => t.title.toLowerCase() === lowerCaseName); if (!exactMatch) { const closestMatches = matches.map((t) => t.title); diff --git a/static/js/TopicSearch.jsx b/static/js/TopicSearch.jsx index 715e809fe1..fff836d5c1 100644 --- a/static/js/TopicSearch.jsx +++ b/static/js/TopicSearch.jsx @@ -69,7 +69,7 @@ class TopicSearch extends Component { const srefs = this.props.srefs; const update = this.props.update; const reset = this.reset; - Sefaria.postRefTopicLink(Sefaria.normRef(this.props.srefs), postJSON).then(async () => { + Sefaria.postRefTopicLink(Sefaria.normRef(this.props.srefs), postJSON).then(async (data) => { const sectionRef = await Sefaria.getRef(Sefaria.normRef(srefs)).sectionRef; srefs.map(sref => { if (!Sefaria._refTopicLinks[sref]) { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 5d75f39f1e..dd5f3086b8 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1238,14 +1238,6 @@ Sefaria = extend(Sefaria, { }.bind(this) }); }, - _topicCompletions: {}, - getTopicCompletions: function (word, callback) { - return this._cachedApiPromise({ - url: `${Sefaria.apiHost}/api/topic/completion/${word}`, key: word, - store: Sefaria._topicCompletions, - processor: callback - }); // this API is used instead of api/name because when we want all topics. api/name only gets topics with a minimum amount of sources - }, _lexiconLookups: {}, getLexiconWords: function(words, ref) { // Returns Promise which resolve to a list of lexicon entries for the given words From 4d3f984b137b6b68508e7659a12a98b1175095a6 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Wed, 27 Nov 2024 06:29:34 +0200 Subject: [PATCH 038/265] feat(topics): split calendar into en and he calendar --- django_topics/admin.py | 21 +++++++++- .../migrations/0004_auto_20241126_2359.py | 31 +++++++++++++++ .../migrations/0005_auto_20241127_0004.py | 20 ++++++++++ ...easonaltopicenglish_seasonaltopichebrew.py | 39 +++++++++++++++++++ django_topics/models/__init__.py | 2 +- django_topics/models/seasonal_topic.py | 29 ++++++++++++++ 6 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 django_topics/migrations/0004_auto_20241126_2359.py create mode 100644 django_topics/migrations/0005_auto_20241127_0004.py create mode 100644 django_topics/migrations/0006_seasonaltopicenglish_seasonaltopichebrew.py diff --git a/django_topics/admin.py b/django_topics/admin.py index 348634e399..5c8ac8f8e7 100644 --- a/django_topics/admin.py +++ b/django_topics/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin, messages -from django_topics.models import Topic, TopicPool, TopicOfTheDay, SeasonalTopic +from django_topics.models import Topic, TopicPool, TopicOfTheDay, SeasonalTopicHebrew, SeasonalTopicEnglish from django_topics.models.pool import PoolType @@ -109,11 +109,13 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs): return super().formfield_for_foreignkey(db_field, request, **kwargs) -@admin.register(SeasonalTopic) class SeasonalTopicAdmin(admin.ModelAdmin): + exclude = ("lang",) # not for manual editing list_display = ( 'start_date', 'topic', + 'display_date_prefix', + 'display_date_suffix', 'secondary_topic', 'display_start_date_israel', 'display_end_date_israel', @@ -173,3 +175,18 @@ def save_model(self, request, obj, form, change): """ obj.clean() super().save_model(request, obj, form, change) + + +@admin.register(SeasonalTopicEnglish) +class SeasonalTopicAdminEnglish(SeasonalTopicAdmin): + + def get_queryset(self, request): + qs = super().get_queryset(request) + return qs.filter(lang="en") + + +@admin.register(SeasonalTopicHebrew) +class SeasonalTopicAdminHebrew(SeasonalTopicAdmin): + def get_queryset(self, request): + qs = super().get_queryset(request) + return qs.filter(lang="he") diff --git a/django_topics/migrations/0004_auto_20241126_2359.py b/django_topics/migrations/0004_auto_20241126_2359.py new file mode 100644 index 0000000000..075a93cbf5 --- /dev/null +++ b/django_topics/migrations/0004_auto_20241126_2359.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-11-27 03:59 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_topics', '0003_auto_20241121_0757'), + ] + + operations = [ + migrations.AddField( + model_name='seasonaltopic', + name='display_date_prefix', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='seasonaltopic', + name='display_date_suffix', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='seasonaltopic', + name='lang', + field=models.CharField(default='en', max_length=255), + preserve_default=False, + ), + ] diff --git a/django_topics/migrations/0005_auto_20241127_0004.py b/django_topics/migrations/0005_auto_20241127_0004.py new file mode 100644 index 0000000000..af7ca982fd --- /dev/null +++ b/django_topics/migrations/0005_auto_20241127_0004.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-11-27 04:04 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_topics', '0004_auto_20241126_2359'), + ] + + operations = [ + migrations.AlterField( + model_name='seasonaltopic', + name='lang', + field=models.CharField(choices=[('en', 'English'), ('he', 'Hebrew')], max_length=2), + ), + ] diff --git a/django_topics/migrations/0006_seasonaltopicenglish_seasonaltopichebrew.py b/django_topics/migrations/0006_seasonaltopicenglish_seasonaltopichebrew.py new file mode 100644 index 0000000000..7cdded5723 --- /dev/null +++ b/django_topics/migrations/0006_seasonaltopicenglish_seasonaltopichebrew.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-11-27 04:25 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_topics', '0005_auto_20241127_0004'), + ] + + operations = [ + migrations.CreateModel( + name='SeasonalTopicEnglish', + fields=[ + ], + options={ + 'verbose_name': 'English Seasonal Topic', + 'verbose_name_plural': 'English Seasonal Topics', + 'proxy': True, + 'indexes': [], + }, + bases=('django_topics.seasonaltopic',), + ), + migrations.CreateModel( + name='SeasonalTopicHebrew', + fields=[ + ], + options={ + 'verbose_name': 'Hebrew Seasonal Topic', + 'verbose_name_plural': 'Hebrew Seasonal Topics', + 'proxy': True, + 'indexes': [], + }, + bases=('django_topics.seasonaltopic',), + ), + ] diff --git a/django_topics/models/__init__.py b/django_topics/models/__init__.py index 7f02438730..51266b641b 100644 --- a/django_topics/models/__init__.py +++ b/django_topics/models/__init__.py @@ -1,4 +1,4 @@ from .topic import Topic from .pool import TopicPool, PoolType from .topic_of_the_day import TopicOfTheDay -from .seasonal_topic import SeasonalTopic +from .seasonal_topic import SeasonalTopic, SeasonalTopicEnglish, SeasonalTopicHebrew diff --git a/django_topics/models/seasonal_topic.py b/django_topics/models/seasonal_topic.py index da945be27a..ff45333529 100644 --- a/django_topics/models/seasonal_topic.py +++ b/django_topics/models/seasonal_topic.py @@ -22,6 +22,9 @@ class SeasonalTopic(models.Model): display_end_date_israel = models.DateField(blank=True, null=True) display_start_date_diaspora = models.DateField(blank=True, null=True) display_end_date_diaspora = models.DateField(blank=True, null=True) + display_date_prefix = models.CharField(max_length=255, blank=True, null=True) + display_date_suffix = models.CharField(max_length=255, blank=True, null=True) + lang = models.CharField(max_length=2, choices=[('en', 'English'), ('he', 'Hebrew')]) class Meta: unique_together = ('topic', 'start_date') @@ -50,6 +53,32 @@ def clean(self): raise ValidationError("If diaspora date is defined, Israel date must also be defined.") self.validate_start_end_dates('display_start_date_israel', 'display_end_date_israel') self.validate_start_end_dates('display_start_date_diaspora', 'display_end_date_diaspora') + if self.display_date_prefix: + self.display_date_prefix = self.display_date_prefix.strip() + if self.display_date_suffix: + self.display_date_suffix = self.display_date_suffix.strip() def __str__(self): return f"{self.topic.slug} ({self.start_date})" + + +class SeasonalTopicEnglish(SeasonalTopic): + class Meta: + proxy = True + verbose_name = "Landing Page - Calendar (EN)" + verbose_name_plural = "Landing Page - Calendar (EN)" + + def save(self, *args, **kwargs): + self.lang = "en" + super().save(*args, **kwargs) + + +class SeasonalTopicHebrew(SeasonalTopic): + class Meta: + proxy = True + verbose_name = "Landing Page - Calendar (HE)" + verbose_name_plural = "Landing Page - Calendar (HE)" + + def save(self, *args, **kwargs): + self.lang = "he" + super().save(*args, **kwargs) From 02b7241a7cc8a55b87ca9af421771c744cb30904 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Wed, 27 Nov 2024 06:36:15 +0200 Subject: [PATCH 039/265] feat(topics): split topic of the day into en and he calendar --- django_topics/admin.py | 20 ++++++- .../migrations/0007_auto_20241127_0034.py | 53 +++++++++++++++++++ django_topics/models/__init__.py | 2 +- django_topics/models/topic_of_the_day.py | 23 ++++++++ 4 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 django_topics/migrations/0007_auto_20241127_0034.py diff --git a/django_topics/admin.py b/django_topics/admin.py index 5c8ac8f8e7..dfd56b06a7 100644 --- a/django_topics/admin.py +++ b/django_topics/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin, messages -from django_topics.models import Topic, TopicPool, TopicOfTheDay, SeasonalTopicHebrew, SeasonalTopicEnglish +from django_topics.models import Topic, TopicPool, TopicOfTheDayEnglish, TopicOfTheDayHebrew, SeasonalTopicEnglish, SeasonalTopicHebrew from django_topics.models.pool import PoolType @@ -88,8 +88,8 @@ def is_in_pool_torah_tab(self, obj): is_in_pool_torah_tab.short_description = "TorahTab Pool" -@admin.register(TopicOfTheDay) class TopicOfTheDayAdmin(admin.ModelAdmin): + exclude = ("lang",) # not for manual editing list_display = ('start_date', 'topic') list_filter = ('start_date',) raw_id_fields = ('topic',) @@ -109,6 +109,22 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs): return super().formfield_for_foreignkey(db_field, request, **kwargs) +@admin.register(TopicOfTheDayEnglish) +class TopicOfTheDayAdminEnglish(TopicOfTheDayAdmin): + + def get_queryset(self, request): + qs = super().get_queryset(request) + return qs.filter(lang="en") + + +@admin.register(TopicOfTheDayHebrew) +class TopicOfTheDayAdminHebrew(TopicOfTheDayAdmin): + + def get_queryset(self, request): + qs = super().get_queryset(request) + return qs.filter(lang="he") + + class SeasonalTopicAdmin(admin.ModelAdmin): exclude = ("lang",) # not for manual editing list_display = ( diff --git a/django_topics/migrations/0007_auto_20241127_0034.py b/django_topics/migrations/0007_auto_20241127_0034.py new file mode 100644 index 0000000000..f375c1622f --- /dev/null +++ b/django_topics/migrations/0007_auto_20241127_0034.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-11-27 04:34 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_topics', '0006_seasonaltopicenglish_seasonaltopichebrew'), + ] + + operations = [ + migrations.CreateModel( + name='TopicOfTheDayEnglish', + fields=[ + ], + options={ + 'verbose_name': 'Landing Page - Topic of the Day (EN)', + 'verbose_name_plural': 'Landing Page - Topic of the Day (EN)', + 'proxy': True, + 'indexes': [], + }, + bases=('django_topics.topicoftheday',), + ), + migrations.CreateModel( + name='TopicOfTheDayHebrew', + fields=[ + ], + options={ + 'verbose_name': 'Landing Page - Topic of the Day (HE)', + 'verbose_name_plural': 'Landing Page - Topic of the Day (HE)', + 'proxy': True, + 'indexes': [], + }, + bases=('django_topics.topicoftheday',), + ), + migrations.AlterModelOptions( + name='seasonaltopicenglish', + options={'verbose_name': 'Landing Page - Calendar (EN)', 'verbose_name_plural': 'Landing Page - Calendar (EN)'}, + ), + migrations.AlterModelOptions( + name='seasonaltopichebrew', + options={'verbose_name': 'Landing Page - Calendar (HE)', 'verbose_name_plural': 'Landing Page - Calendar (HE)'}, + ), + migrations.AddField( + model_name='topicoftheday', + name='lang', + field=models.CharField(choices=[('en', 'English'), ('he', 'Hebrew')], default='en', max_length=2), + preserve_default=False, + ), + ] diff --git a/django_topics/models/__init__.py b/django_topics/models/__init__.py index 51266b641b..e05c9bccf9 100644 --- a/django_topics/models/__init__.py +++ b/django_topics/models/__init__.py @@ -1,4 +1,4 @@ from .topic import Topic from .pool import TopicPool, PoolType -from .topic_of_the_day import TopicOfTheDay +from .topic_of_the_day import TopicOfTheDay, TopicOfTheDayEnglish, TopicOfTheDayHebrew from .seasonal_topic import SeasonalTopic, SeasonalTopicEnglish, SeasonalTopicHebrew diff --git a/django_topics/models/topic_of_the_day.py b/django_topics/models/topic_of_the_day.py index 93aee212dc..30e8f22cbc 100644 --- a/django_topics/models/topic_of_the_day.py +++ b/django_topics/models/topic_of_the_day.py @@ -10,6 +10,7 @@ class TopicOfTheDay(models.Model): related_name='topic_of_the_day' ) start_date = models.DateField() + lang = models.CharField(max_length=2, choices=[('en', 'English'), ('he', 'Hebrew')]) class Meta: unique_together = ('topic', 'start_date') @@ -18,3 +19,25 @@ class Meta: def __str__(self): return f"{self.topic.slug} ({self.start_date})" + + +class TopicOfTheDayEnglish(TopicOfTheDay): + class Meta: + proxy = True + verbose_name = "Landing Page - Topic of the Day (EN)" + verbose_name_plural = "Landing Page - Topic of the Day (EN)" + + def save(self, *args, **kwargs): + self.lang = "en" + super().save(*args, **kwargs) + + +class TopicOfTheDayHebrew(TopicOfTheDay): + class Meta: + proxy = True + verbose_name = "Landing Page - Topic of the Day (HE)" + verbose_name_plural = "Landing Page - Topic of the Day (HE)" + + def save(self, *args, **kwargs): + self.lang = "he" + super().save(*args, **kwargs) From dbf6f05076204c32362c62b622bb8dcbfc30b00a Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Wed, 27 Nov 2024 10:43:05 +0200 Subject: [PATCH 040/265] feat(topics): make two general pools, one for en and one for he --- django_topics/admin.py | 24 ++++++++++++------- django_topics/models/pool.py | 1 - .../migrate_good_to_promote_to_topic_pools.py | 2 +- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/django_topics/admin.py b/django_topics/admin.py index dfd56b06a7..d44fd86223 100644 --- a/django_topics/admin.py +++ b/django_topics/admin.py @@ -41,7 +41,8 @@ class PoolFilter(admin.SimpleListFilter): def lookups(self, request, model_admin): return [ - (PoolType.GENERAL.value, 'General Pool'), + ('general_en', 'General Pool EN'), + ('general_he', 'General Pool HE'), (PoolType.TORAH_TAB.value, 'TorahTab Pool'), ] @@ -55,15 +56,17 @@ def queryset(self, request, queryset): @admin.register(Topic) class TopicAdmin(admin.ModelAdmin): - list_display = ('slug', 'en_title', 'he_title', 'is_in_pool_general', 'is_in_pool_torah_tab') + list_display = ('slug', 'en_title', 'he_title', 'is_in_pool_general_en', 'is_in_pool_general_he', 'is_in_pool_torah_tab') list_filter = (PoolFilter,) filter_horizontal = ('pools',) search_fields = ('slug', 'en_title', 'he_title') readonly_fields = ('slug', 'en_title', 'he_title') actions = [ - create_add_to_pool_action(PoolType.GENERAL.value), + create_add_to_pool_action('general_en'), + create_add_to_pool_action('general_he'), create_add_to_pool_action(PoolType.TORAH_TAB.value), - create_remove_from_pool_action(PoolType.GENERAL.value), + create_remove_from_pool_action('general_en'), + create_remove_from_pool_action('general_he'), create_remove_from_pool_action(PoolType.TORAH_TAB.value), ] @@ -77,10 +80,15 @@ def get_queryset(self, request): queryset = super().get_queryset(request) return queryset.filter(pools__name=PoolType.LIBRARY.value) - def is_in_pool_general(self, obj): - return obj.pools.filter(name=PoolType.GENERAL.value).exists() - is_in_pool_general.boolean = True - is_in_pool_general.short_description = "General Pool" + def is_in_pool_general_en(self, obj): + return obj.pools.filter(name='general_en').exists() + is_in_pool_general_en.boolean = True + is_in_pool_general_en.short_description = "General Pool EN" + + def is_in_pool_general_he(self, obj): + return obj.pools.filter(name='general_he').exists() + is_in_pool_general_he.boolean = True + is_in_pool_general_he.short_description = "General Pool HE" def is_in_pool_torah_tab(self, obj): return obj.pools.filter(name=PoolType.TORAH_TAB.value).exists() diff --git a/django_topics/models/pool.py b/django_topics/models/pool.py index b84df46fec..37f32be50f 100644 --- a/django_topics/models/pool.py +++ b/django_topics/models/pool.py @@ -6,7 +6,6 @@ class PoolType(Enum): LIBRARY = "library" SHEETS = "sheets" TORAH_TAB = "torah_tab" - GENERAL = "general" class TopicPool(models.Model): diff --git a/scripts/migrations/migrate_good_to_promote_to_topic_pools.py b/scripts/migrations/migrate_good_to_promote_to_topic_pools.py index b42a680bb6..7b10f547d4 100644 --- a/scripts/migrations/migrate_good_to_promote_to_topic_pools.py +++ b/scripts/migrations/migrate_good_to_promote_to_topic_pools.py @@ -66,7 +66,7 @@ def add_topics(): def add_pools(): print('Adding pools') - for pool_name in [PoolType.LIBRARY.value, PoolType.SHEETS.value, PoolType.GENERAL.value, PoolType.TORAH_TAB.value]: + for pool_name in [PoolType.LIBRARY.value, PoolType.SHEETS.value, 'general_en', 'general_he', PoolType.TORAH_TAB.value]: TopicPool.objects.create(name=pool_name) From 39f0ef097105a4ac9621048867e78111998fe14f Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Wed, 27 Nov 2024 14:21:34 +0200 Subject: [PATCH 041/265] refactor(ref autocomplete): deprecate ref autocomplete --- reader/startup.py | 2 -- sefaria/model/tests/autospell_test.py | 1 - sefaria/model/text.py | 29 +-------------------------- sefaria/views.py | 2 -- 4 files changed, 1 insertion(+), 33 deletions(-) diff --git a/reader/startup.py b/reader/startup.py index 39927f751e..cfe32d772d 100644 --- a/reader/startup.py +++ b/reader/startup.py @@ -18,8 +18,6 @@ def init_library_cache(): logger.info("Initializing Full Auto Completer") library.build_full_auto_completer() - logger.info("Initializing Ref Auto Completer") - library.build_ref_auto_completer() logger.info("Initializing Lexicon Auto Completers") library.build_lexicon_auto_completers() diff --git a/sefaria/model/tests/autospell_test.py b/sefaria/model/tests/autospell_test.py index ef47f33909..866feccb6f 100644 --- a/sefaria/model/tests/autospell_test.py +++ b/sefaria/model/tests/autospell_test.py @@ -14,7 +14,6 @@ def setup_module(module): library.get_toc_tree() library.build_full_auto_completer() - library.build_ref_auto_completer() library.build_lexicon_auto_completers() library.build_cross_lexicon_auto_completer() diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 44a51b692d..b90a8d0554 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -4945,7 +4945,6 @@ def __init__(self): # Spell Checking and Autocompleting self._full_auto_completer = {} - self._ref_auto_completer = {} self._lexicon_auto_completer = {} self._cross_lexicon_auto_completer = None @@ -4965,7 +4964,6 @@ def __init__(self): # These values are set to True once their initialization is complete self._toc_tree_is_ready = False self._full_auto_completer_is_ready = False - self._ref_auto_completer_is_ready = False self._lexicon_auto_completer_is_ready = False self._cross_lexicon_auto_completer_is_ready = False self._topic_auto_completer_is_ready = False @@ -5301,20 +5299,6 @@ def build_full_auto_completer(self): self._full_auto_completer[lang].set_other_lang_ac(self._full_auto_completer["he" if lang == "en" else "en"]) self._full_auto_completer_is_ready = True - def build_ref_auto_completer(self): - """ - Builds the autocomplete for Refs across the languages in the library - Sets internal boolean to True upon successful completion to indicate Ref auto completer is ready. - """ - from .autospell import AutoCompleter - self._ref_auto_completer = { - lang: AutoCompleter(lang, library, include_people=False, include_categories=False, include_parasha=False) for lang in self.langs - } - - for lang in self.langs: - self._ref_auto_completer[lang].set_other_lang_ac(self._ref_auto_completer["he" if lang == "en" else "en"]) - self._ref_auto_completer_is_ready = True - def build_lexicon_auto_completers(self): """ Sets lexicon autocompleter for each lexicon in LexiconSet using a LexiconTrie @@ -5374,15 +5358,6 @@ def full_auto_completer(self, lang): logger.warning("Built full {} auto completer.".format(lang)) return self._full_auto_completer[lang] - def ref_auto_completer(self, lang): - try: - return self._ref_auto_completer[lang] - except KeyError: - logger.warning("Failed to load {} ref auto completer, rebuilding.".format(lang)) - self.build_ref_auto_completer() # I worry that these could pile up. - logger.warning("Built {} ref auto completer.".format(lang)) - return self._ref_auto_completer[lang] - def recount_index_in_toc(self, indx): # This is used in the case of a remotely triggered multiserver update if isinstance(indx, str): @@ -6365,7 +6340,6 @@ def is_initialized(self): Returns True if the following fields are initialized * self._toc_tree * self._full_auto_completer - * self._ref_auto_completer * self._lexicon_auto_completer * self._cross_lexicon_auto_completer """ @@ -6374,13 +6348,12 @@ def is_initialized(self): # I will likely have to add fields to the object to be changed once # Avoid allocation here since it will be called very frequently - are_autocompleters_ready = self._full_auto_completer_is_ready and self._ref_auto_completer_is_ready and self._lexicon_auto_completer_is_ready and self._cross_lexicon_auto_completer_is_ready + are_autocompleters_ready = self._full_auto_completer_is_ready and self._lexicon_auto_completer_is_ready and self._cross_lexicon_auto_completer_is_ready is_initialized = self._toc_tree_is_ready and (DISABLE_AUTOCOMPLETER or are_autocompleters_ready) if not is_initialized: logger.warning({"message": "Application not fully initialized", "Current State": { "toc_tree_is_ready": self._toc_tree_is_ready, "full_auto_completer_is_ready": self._full_auto_completer_is_ready, - "ref_auto_completer_is_ready": self._ref_auto_completer_is_ready, "lexicon_auto_completer_is_ready": self._lexicon_auto_completer_is_ready, "cross_lexicon_auto_completer_is_ready": self._cross_lexicon_auto_completer_is_ready, }}) diff --git a/sefaria/views.py b/sefaria/views.py index 27a88c4ca1..e8ef191407 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -673,13 +673,11 @@ def rebuild_toc(request): @staff_member_required def rebuild_auto_completer(request): library.build_full_auto_completer() - library.build_ref_auto_completer() library.build_lexicon_auto_completers() library.build_cross_lexicon_auto_completer() if MULTISERVER_ENABLED: server_coordinator.publish_event("library", "build_full_auto_completer") - server_coordinator.publish_event("library", "build_ref_auto_completer") server_coordinator.publish_event("library", "build_lexicon_auto_completers") server_coordinator.publish_event("library", "build_cross_lexicon_auto_completer") From a7c4cf6bce77b734f74a425196a819ab13091a98 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Wed, 27 Nov 2024 15:38:52 +0200 Subject: [PATCH 042/265] feat(topics): add column with sefaria link --- django_topics/admin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/django_topics/admin.py b/django_topics/admin.py index d44fd86223..166dde9ad9 100644 --- a/django_topics/admin.py +++ b/django_topics/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin, messages +from django.utils.html import format_html from django_topics.models import Topic, TopicPool, TopicOfTheDayEnglish, TopicOfTheDayHebrew, SeasonalTopicEnglish, SeasonalTopicHebrew from django_topics.models.pool import PoolType @@ -56,7 +57,7 @@ def queryset(self, request, queryset): @admin.register(Topic) class TopicAdmin(admin.ModelAdmin): - list_display = ('slug', 'en_title', 'he_title', 'is_in_pool_general_en', 'is_in_pool_general_he', 'is_in_pool_torah_tab') + list_display = ('slug', 'en_title', 'he_title', 'is_in_pool_general_en', 'is_in_pool_general_he', 'is_in_pool_torah_tab', 'sefaria_link') list_filter = (PoolFilter,) filter_horizontal = ('pools',) search_fields = ('slug', 'en_title', 'he_title') @@ -95,6 +96,11 @@ def is_in_pool_torah_tab(self, obj): is_in_pool_torah_tab.boolean = True is_in_pool_torah_tab.short_description = "TorahTab Pool" + def sefaria_link(self, obj): + url = f"https://www.sefaria.org/topics/{obj.slug}" + return format_html('{}', url, obj.slug) + sefaria_link.short_description = "Sefaria Link" + class TopicOfTheDayAdmin(admin.ModelAdmin): exclude = ("lang",) # not for manual editing From 9905725ca04820c276173f74f669b1843f67195c Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Wed, 27 Nov 2024 20:44:20 +0200 Subject: [PATCH 043/265] feat(topics): make display data prefix/suffix editable --- django_topics/admin.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/django_topics/admin.py b/django_topics/admin.py index 166dde9ad9..f9ce67aadc 100644 --- a/django_topics/admin.py +++ b/django_topics/admin.py @@ -170,6 +170,13 @@ class SeasonalTopicAdmin(admin.ModelAdmin): 'start_date' ) }), + ('Display Date Prefix/Suffix', { + 'fields': ( + 'display_date_prefix', + 'display_date_suffix', + ), + 'description': 'Prefix/Suffix that will be displayed around the secondary topic.', + }), ('Israel Display Dates', { 'fields': ( 'display_start_date_israel', From 97f3466804372ab21768d2ce93efd8a375203253 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Thu, 28 Nov 2024 13:29:53 +0200 Subject: [PATCH 044/265] test(topics): add test for topic of the day --- .../models/tests/topic_of_the_day_test.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 django_topics/models/tests/topic_of_the_day_test.py diff --git a/django_topics/models/tests/topic_of_the_day_test.py b/django_topics/models/tests/topic_of_the_day_test.py new file mode 100644 index 0000000000..c773eeb47b --- /dev/null +++ b/django_topics/models/tests/topic_of_the_day_test.py @@ -0,0 +1,50 @@ +import django +django.setup() + +import pytest +from datetime import date +from django_topics.models import TopicOfTheDay, Topic + + +@pytest.fixture +def topic(db): + """Fixture to create a Topic instance.""" + return Topic.objects.create(slug="test-topic") + + +@pytest.fixture +def topics_of_the_day(db, topic): + """Fixture to create TopicOfTheDay instances.""" + topics = [ + TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 26), lang="en"), + TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 25), lang="en"), + TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 24), lang="en"), + ] + return topics + + +@pytest.mark.django_db +def test_get_topic_of_the_day_with_exact_date_db(topics_of_the_day): + """Test for exact match.""" + result = TopicOfTheDay.objects.get_topic_of_the_day(lang="en", date=date(2024, 11, 26)) + + assert result.start_date == date(2024, 11, 26) + assert result.lang == "en" + + +@pytest.mark.django_db +def test_get_topic_of_the_day_with_closest_date_db(topics_of_the_day): + """Test for the closest date less than or equal to the given date.""" + result = TopicOfTheDay.objects.get_topic_of_the_day(lang="en", date=date(2024, 11, 27)) + + assert result.start_date == date(2024, 11, 26) + assert result.lang == "en" + + +@pytest.mark.django_db +def test_get_topic_of_the_day_with_no_matching_date_db(db, topic): + """Test when there is no matching date.""" + TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 20), lang="en") + result = TopicOfTheDay.objects.get_topic_of_the_day(lang="en", date=date(2024, 11, 19)) + + assert result is None From c22abe43be029dde5e5cd6174f49800896001296 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Thu, 28 Nov 2024 13:30:05 +0200 Subject: [PATCH 045/265] feat(topics): add topic of the day api --- django_topics/models/topic_of_the_day.py | 21 ++++++++++++++++++++- reader/views.py | 14 ++++++++++++++ sefaria/urls.py | 1 + 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/django_topics/models/topic_of_the_day.py b/django_topics/models/topic_of_the_day.py index 30e8f22cbc..6670df0912 100644 --- a/django_topics/models/topic_of_the_day.py +++ b/django_topics/models/topic_of_the_day.py @@ -1,6 +1,24 @@ from django.db import models +from datetime import datetime +from django.utils.timezone import now from django_topics.models import Topic -from django.core.exceptions import ValidationError + + +class TopicOfTheDayManager(models.Manager): + + def get_topic_of_the_day(self, lang: str, date: datetime = None) -> 'TopicOfTheDay': + """ + Return topic of day for given date or closest date that is less than or equal to given date + @param lang: language code, "en" or "he" + @param date: datetime object + @return: + """ + date = date or now().date() + return ( + self.filter(start_date__lte=date, lang=lang) + .order_by('-start_date') + .first() + ) class TopicOfTheDay(models.Model): @@ -11,6 +29,7 @@ class TopicOfTheDay(models.Model): ) start_date = models.DateField() lang = models.CharField(max_length=2, choices=[('en', 'English'), ('he', 'Hebrew')]) + objects = TopicOfTheDayManager() class Meta: unique_together = ('topic', 'start_date') diff --git a/reader/views.py b/reader/views.py index 0b41c10820..5c74ad116f 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3208,6 +3208,20 @@ def topic_pool_api(request, pool_name): return jsonResponse(response, callback=request.GET.get("callback", None)) +@catch_error_as_json +def topic_of_the_day_api(request): + from django_topics.models import TopicOfTheDay + + lang = request.GET.get("lang") + cb = request.GET.get("callback", None) + tod = TopicOfTheDay.objects.get_topic_of_the_day(lang) + if not tod: + return jsonResponse({'error': f'No topic of the day found for lang "{lang}"'}, status=404) + mongo_topic = Topic.init(tod.topic.slug) + response = {'topic': mongo_topic.contents(), 'date': tod.start_date.isoformat()} + return jsonResponse(response, callback=cb) + + @staff_member_required def reorder_topics(request): topics = json.loads(request.POST["json"]).get("topics", []) diff --git a/sefaria/urls.py b/sefaria/urls.py index f07ec2338e..893c5fe607 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -264,6 +264,7 @@ url(r'^api/topics/generate-prompts/(?P.+)$', reader_views.generate_topic_prompts_api), url(r'^api/topics-graph/(?P.+)$', reader_views.topic_graph_api), url(r'^api/topics/pools/(?P.+)$', reader_views.topic_pool_api), + url(r'^_api/topics/topic-of-the-day/?$', reader_views.topic_of_the_day_api), url(r'^api/ref-topic-links/bulk$', reader_views.topic_ref_bulk_api), url(r'^api/ref-topic-links/(?P.+)$', reader_views.topic_ref_api), url(r'^api/v2/topics/(?P.+)$', reader_views.topics_api, {'v2': True}), From f6e69599b61d1ad4ddfcee60d911ffcbbfa7f9a0 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Thu, 28 Nov 2024 13:31:02 +0200 Subject: [PATCH 046/265] chore(topics): remove unnecessary django setup now that we have pytest-django --- django_topics/models/tests/topic_of_the_day_test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/django_topics/models/tests/topic_of_the_day_test.py b/django_topics/models/tests/topic_of_the_day_test.py index c773eeb47b..2faa853cdd 100644 --- a/django_topics/models/tests/topic_of_the_day_test.py +++ b/django_topics/models/tests/topic_of_the_day_test.py @@ -1,6 +1,3 @@ -import django -django.setup() - import pytest from datetime import date from django_topics.models import TopicOfTheDay, Topic From b7bea0c7c023bf2e095b46dec33cf073e172b7dc Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Thu, 28 Nov 2024 13:37:06 +0200 Subject: [PATCH 047/265] chore(topics): add pytest django as a dependency --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index c1219d9635..ab56da61c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -69,6 +69,7 @@ undecorated==0.3.0 unicodecsv==0.14.1 unidecode==1.1.1 user-agents==2.2.0 +pytest-django==4.9.* #opentelemetry-distro From 2b39b16d5be9ef5bb72b0fbd83a4053937649168 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Thu, 28 Nov 2024 14:37:30 +0200 Subject: [PATCH 048/265] chore(topics): add topic of the day function in Sefaria to call corresponding API --- static/js/sefaria/sefaria.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index af7a749983..c092febddf 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2746,6 +2746,14 @@ _media: {}, const key = this._getTopicCacheKey(slug, {annotated, with_html}); return this._topics[key]; }, + _topicOfTheDay: {}, + getTopicOfTheDay: function() { + return this._cachedApiPromise({ + url: `${Sefaria.apiHost}/_api/topics/topic-of-the-day?lang=${Sefaria.interfaceLang.slice(0, 2)}`, + key: (new Date()).toLocaleDateString(), + store: this._topicOfTheDay, + }); + }, _topicSlugsToTitles: null, slugsToTitles: function() { //initializes _topicSlugsToTitles for Topic Editor tool and adds necessary "Choose a Category" and "Main Menu" for From 9219d5c958888db75424addaf97d293075f299a2 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Thu, 28 Nov 2024 14:38:07 +0200 Subject: [PATCH 049/265] feat(topics): basic topic of the day rendering --- static/js/TopicLandingPage/TopicOfTheDay.jsx | 11 +++++++++++ static/js/TopicLandingPage/TopicsLandingPage.jsx | 9 ++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 static/js/TopicLandingPage/TopicOfTheDay.jsx diff --git a/static/js/TopicLandingPage/TopicOfTheDay.jsx b/static/js/TopicLandingPage/TopicOfTheDay.jsx new file mode 100644 index 0000000000..0a2f788014 --- /dev/null +++ b/static/js/TopicLandingPage/TopicOfTheDay.jsx @@ -0,0 +1,11 @@ +import React, {useState} from 'react'; +import Sefaria from "../sefaria/sefaria"; + +export const TopicOfTheDay = () => { + let [topic, setTopic] = useState(null); + Sefaria.getTopicOfTheDay().then(result => setTopic(result.topic)); + return (<> +
Topic of the day
+
{topic?.slug}
+ ); +}; diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 43ead2e7bd..084e4fd8f7 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -1,11 +1,14 @@ import React from 'react'; import {TopicLandingSearch} from "./TopicLandingSearch"; +import {TopicOfTheDay} from "./TopicOfTheDay"; export const TopicsLandingPage = ({openTopic}) => { - return (
-
Hello, would you like a serving of topics salad?
- + return ( +
+
Hello, would you like a serving of topics salad?
+ +
) }; From 347167a54370038194cfdf18f63cd9aaeb8e5c21 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Thu, 28 Nov 2024 15:15:20 +0200 Subject: [PATCH 050/265] feat(topics): add ImageWithAltText --- static/js/Misc.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index bc988e2ae8..8f07f43ada 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -3334,13 +3334,15 @@ return Sefaria._v(caption) || Sefaria._('Illustrative image'); const ImageWithCaption = ({photoLink, caption }) => { return (
- {getImgAltText(caption)}/ +
); } +const ImageWithAltText = ({photoLink, altText}) => ({getImgAltText(altText)}/); + const AppStoreButton = ({ platform, href, altText }) => { const isIOS = platform === 'ios'; const aClasses = classNames({button: 1, small: 1, white: 1, appButton: 1, ios: isIOS}); @@ -3538,6 +3540,7 @@ export { OnInView, TopicPictureUploader, ImageWithCaption, + ImageWithAltText, handleAnalyticsOnMarkdown, LangSelectInterface, PencilSourceEditor From d7f8846f8bda22b9a29467eaf7f03b1d85a611dc Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Sat, 30 Nov 2024 21:01:57 +0200 Subject: [PATCH 051/265] feat(topics): topic of the day frontend WIP --- static/css/s2.css | 30 +++++++++++++++++++ static/js/TopicLandingPage/TopicOfTheDay.jsx | 19 +++++++++--- .../js/TopicLandingPage/TopicsLandingPage.jsx | 2 +- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 9dbc6f74a7..8ac793b017 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4922,6 +4922,36 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus padding-inline-start: 11px; } +.topicOfTheDay { + display: flex; + flex-direction: column; +} + +.topicOfTheDayContent { + display: flex; + flex-direction: row; +} + +.topicOfTheDayContent img { + width: 300px; +} + +.topicOfTheDayContent h3 { + font-size: var(--serif-h3-font-size); + color: #333; + text-transform: none; +} + +.topicOfTheDayText { + display: flex; + flex-direction: column; +} + +.topicOfTheDay h1 { + font-size: 30px; + border-bottom: 1px solid #ccc; +} + .readerNavMenu .sheet { display: flex; justify-content: space-between; diff --git a/static/js/TopicLandingPage/TopicOfTheDay.jsx b/static/js/TopicLandingPage/TopicOfTheDay.jsx index 0a2f788014..a341ec37ac 100644 --- a/static/js/TopicLandingPage/TopicOfTheDay.jsx +++ b/static/js/TopicLandingPage/TopicOfTheDay.jsx @@ -1,11 +1,22 @@ import React, {useState} from 'react'; import Sefaria from "../sefaria/sefaria"; +import {ImageWithAltText, InterfaceText} from "../Misc"; export const TopicOfTheDay = () => { let [topic, setTopic] = useState(null); Sefaria.getTopicOfTheDay().then(result => setTopic(result.topic)); - return (<> -
Topic of the day
-
{topic?.slug}
- ); + console.log("topic", topic) + if (!topic) { return null; } + return ( +
+

Topic of the Day

+
+ +
+

+ +
+
+
+ ); }; diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 084e4fd8f7..5cfc4bcb28 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -5,7 +5,7 @@ import {TopicOfTheDay} from "./TopicOfTheDay"; export const TopicsLandingPage = ({openTopic}) => { return ( -
+
Hello, would you like a serving of topics salad?
From 6e55702bd2cb8ccfaca8cf6600f31fc9dc2dfd88 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Sun, 1 Dec 2024 12:23:20 +0200 Subject: [PATCH 052/265] feat(Topic Salad): basic implementation of WordSalad component by a new helper npm library 'line-clamp' --- package-lock.json | 7 ++++++ package.json | 1 + static/css/s2.css | 6 +++++ .../js/TopicLandingPage/TopicsLandingPage.jsx | 7 ++++++ static/js/TopicLandingPage/WordSalad.jsx | 23 +++++++++++++++++++ 5 files changed, 44 insertions(+) create mode 100644 static/js/TopicLandingPage/WordSalad.jsx diff --git a/package-lock.json b/package-lock.json index 7bd59c0f6d..840bd6fa0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "jquery.cookie": "^1.4.1", "jquery.scrollto": "^2.1.2", "js-cookie": "^2.2.1", + "line-clamp": "^1.0.0", "patch-package": "^6.2.2", "prop-types": "^15.6.0", "querystring": "^0.2.0", @@ -8583,6 +8584,12 @@ "node": ">= 0.8.0" } }, + "node_modules/line-clamp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/line-clamp/-/line-clamp-1.0.0.tgz", + "integrity": "sha512-dCDlvMj572RIRBQ3x9aIX0DTdt2St1bMdpi64jVTAi5vqBck7wf+J97//+J7+pS80rFJaYa8HiyXCTp0flpnBA==", + "license": "MIT" + }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", diff --git a/package.json b/package.json index e3675733d2..5fb1fed622 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "jquery.cookie": "^1.4.1", "jquery.scrollto": "^2.1.2", "js-cookie": "^2.2.1", + "line-clamp": "^1.0.0", "patch-package": "^6.2.2", "prop-types": "^15.6.0", "querystring": "^0.2.0", diff --git a/static/css/s2.css b/static/css/s2.css index 45ac1d4ab4..9b778c62cc 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4915,6 +4915,12 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus padding-inline-start: 11px; } +.salad-container{ + overflow: hidden; + word-wrap: break-word; + line-height: 1.5rem; +} + .readerNavMenu .sheet { display: flex; justify-content: space-between; diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 43ead2e7bd..e50b0a458e 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -1,11 +1,18 @@ import React from 'react'; import {TopicLandingSearch} from "./TopicLandingSearch"; +import {WordSalad} from "./WordSalad"; +const renderSaladItem = (item) => { + return {item} +} export const TopicsLandingPage = ({openTopic}) => { return (
Hello, would you like a serving of topics salad?
+
) }; diff --git a/static/js/TopicLandingPage/WordSalad.jsx b/static/js/TopicLandingPage/WordSalad.jsx new file mode 100644 index 0000000000..acecbd5c43 --- /dev/null +++ b/static/js/TopicLandingPage/WordSalad.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import {TopicLandingSearch} from "./TopicLandingSearch"; +import classNames from "classnames"; +import {useRef, useEffect, useState} from "react"; +import lineClamp from 'line-clamp'; + + +export const WordSalad = ({ numLines, salad, renderItem }) => { + const containerRef = useRef(null); + const zeroWidthSpace = '\u200B'; + + useEffect(() => { + if (containerRef.current) { + lineClamp(containerRef.current, numLines, {ellipsis: zeroWidthSpace}); // Apply lineClamp to the actual DOM element + } + }, [numLines, salad]); // Reapply if numLines or salad changes + + return ( +
+ {salad.map((item, index) => renderItem(item))} +
+ ); +}; \ No newline at end of file From 844f4ad3b7b6641e81f8eb9272b685625e15507c Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Sun, 1 Dec 2024 15:50:44 +0200 Subject: [PATCH 053/265] feat(Topic Salad): implementation of the salad without external libraries using pure css line-clamp --- package-lock.json | 7 ---- package.json | 1 - static/css/s2.css | 34 +++++++++++++++++-- .../js/TopicLandingPage/TopicsLandingPage.jsx | 21 ++++++++++-- static/js/TopicLandingPage/WordSalad.jsx | 19 ++++------- 5 files changed, 57 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 840bd6fa0c..7bd59c0f6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,6 @@ "jquery.cookie": "^1.4.1", "jquery.scrollto": "^2.1.2", "js-cookie": "^2.2.1", - "line-clamp": "^1.0.0", "patch-package": "^6.2.2", "prop-types": "^15.6.0", "querystring": "^0.2.0", @@ -8584,12 +8583,6 @@ "node": ">= 0.8.0" } }, - "node_modules/line-clamp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/line-clamp/-/line-clamp-1.0.0.tgz", - "integrity": "sha512-dCDlvMj572RIRBQ3x9aIX0DTdt2St1bMdpi64jVTAi5vqBck7wf+J97//+J7+pS80rFJaYa8HiyXCTp0flpnBA==", - "license": "MIT" - }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", diff --git a/package.json b/package.json index 5fb1fed622..e3675733d2 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,6 @@ "jquery.cookie": "^1.4.1", "jquery.scrollto": "^2.1.2", "js-cookie": "^2.2.1", - "line-clamp": "^1.0.0", "patch-package": "^6.2.2", "prop-types": "^15.6.0", "querystring": "^0.2.0", diff --git a/static/css/s2.css b/static/css/s2.css index 9b778c62cc..07fd00e391 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4915,10 +4915,40 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus padding-inline-start: 11px; } +@font-face { + font-family: "ellipsis-font"; + src: local("Courier"); + unicode-range: U+2026; + size-adjust: 0%; +} + + .salad-container{ overflow: hidden; - word-wrap: break-word; - line-height: 1.5rem; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: var(--num-lines); + text-overflow: clip; + font-family: ellipsis-font; +} + +.salad-item-wrapper{ + white-space: nowrap; +} + +.topic-salad{ + /*font-family: Roboto;*/ + width: 843px; + margin: 0 auto; + font-size: 22px; + font-weight: 400; + line-height: 32px; + text-align: center; + text-underline-position: from-font; + text-decoration-skip-ink: none; +} +.topic-salad-item{ + margin: 11px; } .readerNavMenu .sheet { diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index e50b0a458e..05b82f7230 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -2,17 +2,32 @@ import React from 'react'; import {TopicLandingSearch} from "./TopicLandingSearch"; import {WordSalad} from "./WordSalad"; + +const exampleSlugs = [ + "Potiphar’s wife", "Og", "vines", "diversity", "humanity", "ravens", + "Terach", "Fast of Gedaliah", "harmful forces", "Purim", "energy", + "camels", "relationships", "Melchizedek", "prayer", "growth", "Iddo", + "maror", "Yitro", "menorah", "alacrity", "authority", "Yom Kippur", + "bal tashchit", "four questions", "Moses and Joseph’s coffin", "fertility", + "freedom", "the future to come", "Jacob’s dream", "Ezekiel", "social justice", + "compassion", + "hate" +] +const exampleItems = exampleSlugs.map(slug => ({text: slug})) + const renderSaladItem = (item) => { - return {item} + return {item.text} } export const TopicsLandingPage = ({openTopic}) => { return (
Hello, would you like a serving of topics salad?
+
+ numLines={5} + salad={exampleItems}/> +
) }; diff --git a/static/js/TopicLandingPage/WordSalad.jsx b/static/js/TopicLandingPage/WordSalad.jsx index acecbd5c43..de22a4aafd 100644 --- a/static/js/TopicLandingPage/WordSalad.jsx +++ b/static/js/TopicLandingPage/WordSalad.jsx @@ -1,22 +1,17 @@ import React from 'react'; -import {TopicLandingSearch} from "./TopicLandingSearch"; -import classNames from "classnames"; -import {useRef, useEffect, useState} from "react"; -import lineClamp from 'line-clamp'; export const WordSalad = ({ numLines, salad, renderItem }) => { - const containerRef = useRef(null); - const zeroWidthSpace = '\u200B'; + const nonWhitespaceInvisibleChar = '\u00A0' + + salad = salad.map(saladItem => ({ + ...saladItem, + text: saladItem.text.replace(/ /g, nonWhitespaceInvisibleChar), + })); - useEffect(() => { - if (containerRef.current) { - lineClamp(containerRef.current, numLines, {ellipsis: zeroWidthSpace}); // Apply lineClamp to the actual DOM element - } - }, [numLines, salad]); // Reapply if numLines or salad changes return ( -
+
{salad.map((item, index) => renderItem(item))}
); From 9136d91c936bd846212666070441aeb0b6a823d7 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Sun, 1 Dec 2024 16:44:20 +0200 Subject: [PATCH 054/265] feat(Topic Salad): change to required font --- static/css/s2.css | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 07fd00e391..02e181d833 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4932,10 +4932,6 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus font-family: ellipsis-font; } -.salad-item-wrapper{ - white-space: nowrap; -} - .topic-salad{ /*font-family: Roboto;*/ width: 843px; @@ -4949,6 +4945,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus } .topic-salad-item{ margin: 11px; + font-family: 'Roboto'; } .readerNavMenu .sheet { From 633c80167856ba324d15bf953332063c0cb2095a Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Mon, 2 Dec 2024 10:57:51 +0200 Subject: [PATCH 055/265] fix(topics): change text transform --- static/css/s2.css | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 8ac793b017..0258e1d3e8 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4916,37 +4916,33 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus padding: 0; visibility: hidden; } - .topic-landing-search-container .readerNavMenuSearchButton{ top: 6px; padding-inline-start: 11px; } - .topicOfTheDay { display: flex; flex-direction: column; } - .topicOfTheDayContent { display: flex; flex-direction: row; } - .topicOfTheDayContent img { width: 300px; } - .topicOfTheDayContent h3 { font-size: var(--serif-h3-font-size); color: #333; text-transform: none; } - .topicOfTheDayText { display: flex; flex-direction: column; } - +.topicOfTheDayText h3 { + text-transform: none; +} .topicOfTheDay h1 { font-size: 30px; border-bottom: 1px solid #ccc; From b9a7a2d29e705cd7be042cdc7fe7eb2630078613 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Mon, 2 Dec 2024 11:15:48 +0200 Subject: [PATCH 056/265] fix(topics): fix bad find and replace --- static/js/Header.jsx | 4 ++-- static/js/NavSidebar.jsx | 2 +- static/js/SourceEditor.jsx | 4 ++-- static/js/TopicEditor.jsx | 2 +- static/js/sheets.js | 2 +- templates/static/nash-bravmann-collection.html | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index dd5e40dc2f..d44cb5cf9a 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -50,7 +50,7 @@ class Header extends Component { { Sefaria._siteSettings.TORAH_SPECIFIC ? {logo} : null } Texts - Topics + Topics Community Donate
@@ -211,7 +211,7 @@ const MobileNavMenu = ({onRefClick, showSearch, openTopic, openURL, close, visib Texts - + Topics diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index dedc39a7a3..38d0355549 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -495,7 +495,7 @@ const WeeklyTorahPortion = () => {
- + All Portions › פרשות השבוע › diff --git a/static/js/SourceEditor.jsx b/static/js/SourceEditor.jsx index 4b1f1219f7..fecc79bd70 100644 --- a/static/js/SourceEditor.jsx +++ b/static/js/SourceEditor.jsx @@ -56,7 +56,7 @@ const SourceEditor = ({topic, close, origData={}}) => { const currentUrlObj = new URL(window.location.href); const tabName = currentUrlObj.searchParams.get('tab'); Sefaria.postRefTopicLink(refInUrl, payload) - .then(() => window.location.href = `../../django_topics/${topic}?sort=Relevance&tab=${tabName}`) + .then(() => window.location.href = `../../topics/${topic}?sort=Relevance&tab=${tabName}`) .finally(() => setSavingStatus(false)); } @@ -96,7 +96,7 @@ const SourceEditor = ({topic, close, origData={}}) => { const deleteTopicSource = function() { const url = `/api/ref-topic-links/${Sefaria.normRef(origData.ref)}?topic=${topic}&interface_lang=${Sefaria.interfaceLang}`; Sefaria.adminEditorApiRequest(url, null, null, "DELETE") - .then(() => window.location.href = `../../django_topics/${topic}`); + .then(() => window.location.href = `../../topics/${topic}`); } const previousTitleItemRef = useRef(data.enTitle ? "Previous Title" : null); //use useRef to make value null even if component re-renders const previousPromptItemRef = useRef(data.prompt ? "Previous Prompt" : null); diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 392d3ce0e8..0341e82cbe 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -206,7 +206,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { onCreateSuccess(newSlug); } else { - window.location.href = `../../django_topics/${newSlug}`; + window.location.href = `../../topics/${newSlug}`; } } }).fail(function (xhr, status, errorThrown) { diff --git a/static/js/sheets.js b/static/js/sheets.js index b367e8f652..5c8d64d63d 100755 --- a/static/js/sheets.js +++ b/static/js/sheets.js @@ -2172,7 +2172,7 @@ sjs.sheetTagger = { } var html = ""; for (var i = 0; i < topics.length; i++) { - html = html + ''+topics[i].asTyped+''; + html = html + ''+topics[i].asTyped+''; } $("#sheetTags").html(html); }, diff --git a/templates/static/nash-bravmann-collection.html b/templates/static/nash-bravmann-collection.html index 7a3d79abac..bec3974a0d 100644 --- a/templates/static/nash-bravmann-collection.html +++ b/templates/static/nash-bravmann-collection.html @@ -110,7 +110,7 @@

The Jack Nash and Ludwig Bravmann Collection is a free, online library of Rabbi Adin Even-Israel Steinsaltz's major commentaries in Hebrew and English. Interlinked with Sefaria’s vast and ever-growing corpus of Jewish text, the Nash-Bravmann Collection further integrates Rabbi Steinsaltz’s Torah into the Jewish library, while also bringing these already-renowned commentaries to an even wider global audience. האוסף על שם ג׳ק נאש ולודוויג ברוומן הינו - + ספריה מקוונת חינמית הכוללת את הפרשנויות המרכזיות של הרב עדין אבן-ישראל שטיינזלץ בעברית ובאנגלית. בעצם שילובם של כתבי הרב שטיינזלץ באופן שוטף בספרייה ההולכת ומתרחבת של ספריא, אוסף נאש-ברוומן משמש כאמצעי להטמעה מעמיקה של תורתו בתוך שאר עולם המקורות היהודי, תוך כדי הנגשת הפרשנויות הנודעות האלו לקהילתנו העולמית.

From f95f360d87468cbf4fd31c711fd89d5b98c8cfd5 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 2 Dec 2024 11:34:07 +0200 Subject: [PATCH 057/265] feat(Topic Salad): create the TopicSalad component --- static/css/s2.css | 2 +- static/js/TopicLandingPage/TopicSalad.jsx | 30 +++++++++++++++++++ .../js/TopicLandingPage/TopicsLandingPage.jsx | 22 ++------------ 3 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 static/js/TopicLandingPage/TopicSalad.jsx diff --git a/static/css/s2.css b/static/css/s2.css index b3fc236319..aab3dc2768 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4926,7 +4926,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus font-family: "ellipsis-font"; src: local("Courier"); unicode-range: U+2026; - size-adjust: 0%; + size-adjust: 0%; } diff --git a/static/js/TopicLandingPage/TopicSalad.jsx b/static/js/TopicLandingPage/TopicSalad.jsx new file mode 100644 index 0000000000..031a59000d --- /dev/null +++ b/static/js/TopicLandingPage/TopicSalad.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import {WordSalad} from "./WordSalad"; + +const exampleSlugs = [ + "Potiphar’s wife", "Og", "vines", "diversity", "humanity", "ravens", + "Terach", "Fast of Gedaliah", "harmful forces", "Purim", "energy", + "camels", "relationships", "Melchizedek", "prayer", "growth", "Iddo", + "maror", "Yitro", "menorah", "alacrity", "authority", "Yom Kippur", + "bal tashchit", "four questions", "Moses and Joseph’s coffin", "fertility", + "freedom", "the future to come", "Jacob’s dream", "Ezekiel", "social justice", + "compassion", + "hate" +] +const exampleItems = exampleSlugs.map(slug => ({text: slug})) + +const renderSaladItem = (item) => { + return {item.text} +} + + +export const TopicSalad = ({ numLines, salad, renderItem }) => { + + return ( +
+ +
+ ); +}; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 05b82f7230..86dead7b5f 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -1,33 +1,15 @@ import React from 'react'; import {TopicLandingSearch} from "./TopicLandingSearch"; import {WordSalad} from "./WordSalad"; +import {TopicSalad} from "./TopicSalad"; -const exampleSlugs = [ - "Potiphar’s wife", "Og", "vines", "diversity", "humanity", "ravens", - "Terach", "Fast of Gedaliah", "harmful forces", "Purim", "energy", - "camels", "relationships", "Melchizedek", "prayer", "growth", "Iddo", - "maror", "Yitro", "menorah", "alacrity", "authority", "Yom Kippur", - "bal tashchit", "four questions", "Moses and Joseph’s coffin", "fertility", - "freedom", "the future to come", "Jacob’s dream", "Ezekiel", "social justice", - "compassion", - "hate" -] -const exampleItems = exampleSlugs.map(slug => ({text: slug})) - -const renderSaladItem = (item) => { - return {item.text} -} export const TopicsLandingPage = ({openTopic}) => { return (
Hello, would you like a serving of topics salad?
-
- -
+
) }; From 5f51398d018d5587b7a48314a4233da78a92de9b Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Mon, 2 Dec 2024 12:01:33 +0200 Subject: [PATCH 058/265] feat(topics): basic layout of topic landing page --- .../js/TopicLandingPage/TopicsLandingPage.jsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 43ead2e7bd..a5dfa14104 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -1,11 +1,24 @@ import React from 'react'; import {TopicLandingSearch} from "./TopicLandingSearch"; +import {NavSidebar} from "../NavSidebar"; +import Footer from "../Footer"; export const TopicsLandingPage = ({openTopic}) => { - return (
-
Hello, would you like a serving of topics salad?
- + const sidebarModules = [ + {type: 'TrendingTopics'}, + ]; + return ( +
+
+
+
+ +
+ +
+
+
) }; From daf59ee7b76cf9d7ea710bf8cfa2431c20c1c3ae Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 2 Dec 2024 12:57:28 +0200 Subject: [PATCH 059/265] feat(Topic Pools): create sefaria.js function to interact with endpoint --- static/js/sefaria/sefaria.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index af7a749983..494ab58c09 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1946,6 +1946,24 @@ _media: {}, const topics = Sefaria.topicsByRef(refs); return topics && topics.length; }, + _TopicsByPool: {}, + getTopicsByPool: function(poolName, numOfTopics, order) { + let params = {}; + if (numOfTopics != undefined) { params["n"] = numOfTopics; } + if (order != undefined) { params["order"] = order; } + let queryString = Object.keys(params).map(key => key + '=' + params[key]).join('&'); + queryString = (queryString ? "?" + queryString : ""); + const url = this.apiHost + "/api/topics/pools/" + encodeURIComponent(poolName) + queryString; + + const shouldBeCached = order != undefined && order != 'random'; + if (!shouldBeCached) {return this._ApiPromise(url)} + + return this._cachedApiPromise({ + url: url, + key: poolName + queryString, + store: this._TopicsByPool + }); + }, _related: {}, related: function(ref, callback) { // Single API to bundle public links, sheets, and notes by ref. From 6a6d9b7fc52fff65dca503243a897b9b885d5f6c Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 2 Dec 2024 13:48:21 +0200 Subject: [PATCH 060/265] feat(Topic Landing Page): turn content container into flex box and center child elements --- static/css/s2.css | 8 +++++++- static/js/TopicLandingPage/TopicsLandingPage.jsx | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index aab3dc2768..76ff365498 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4837,6 +4837,13 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus justify-content: center; } +.topic-lading-page-content{ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + .interface-hebrew .topic-landing-page-wrapper{ direction: rtl; } @@ -4942,7 +4949,6 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-salad{ /*font-family: Roboto;*/ width: 843px; - margin: 0 auto; font-size: 22px; font-weight: 400; line-height: 32px; diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index a5dfa14104..1bef7189e2 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -2,6 +2,7 @@ import React from 'react'; import {TopicLandingSearch} from "./TopicLandingSearch"; import {NavSidebar} from "../NavSidebar"; import Footer from "../Footer"; +import {TopicSalad} from "./TopicSalad"; export const TopicsLandingPage = ({openTopic}) => { @@ -12,8 +13,9 @@ export const TopicsLandingPage = ({openTopic}) => {
-
+
+
From 12f022310fdff59b0b9ad001e5e61b79bac4ad06 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Mon, 2 Dec 2024 14:01:42 +0200 Subject: [PATCH 061/265] feat(topics): polish topic of the day (to be known henceforth as featured topic) --- static/css/s2.css | 25 +++++++++++++------- static/js/TopicLandingPage/TopicOfTheDay.jsx | 12 ++++++---- static/js/sefaria/strings.js | 2 ++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 0258e1d3e8..0a5ad1191f 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -121,6 +121,7 @@ a, a:hover { --highlight-blue: #DDEEFF; --highlight-blue-light: #F0F7FF; --beit-midrash-grey: #333333; + --darkest-grey: #333333; --dark-grey: #666666; --medium-grey: #999999; --light-grey: #CCCCCC; @@ -4837,10 +4838,6 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus justify-content: center; } -.interface-hebrew .topic-landing-page-wrapper{ - direction: rtl; -} - .topic-landing-search-container{ overflow: hidden; display: flex; @@ -4923,29 +4920,41 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topicOfTheDay { display: flex; flex-direction: column; + margin-top: 68px; } .topicOfTheDayContent { display: flex; flex-direction: row; } .topicOfTheDayContent img { - width: 300px; + width: 239px; } .topicOfTheDayContent h3 { font-size: var(--serif-h3-font-size); - color: #333; text-transform: none; } -.topicOfTheDayText { +.topicOfTheDayText .int-en, +.topicOfTheDayText .int-he { display: flex; flex-direction: column; + color: var(--dark-grey); + margin-inline-start: 30px; } .topicOfTheDayText h3 { text-transform: none; + margin: 0 0 15px 0; +} +.topicOfTheDayGoToLink { + margin-top: 25px; +} +.topicOfTheDayText .topicDescription { + font-size: var(--sans-serif-small-font-size); } .topicOfTheDay h1 { - font-size: 30px; + font-size: var(--sans-serif-h2-font-size); border-bottom: 1px solid #ccc; + width: fit-content; + padding-bottom: 10px; } .readerNavMenu .sheet { diff --git a/static/js/TopicLandingPage/TopicOfTheDay.jsx b/static/js/TopicLandingPage/TopicOfTheDay.jsx index a341ec37ac..690d130e35 100644 --- a/static/js/TopicLandingPage/TopicOfTheDay.jsx +++ b/static/js/TopicLandingPage/TopicOfTheDay.jsx @@ -1,20 +1,24 @@ import React, {useState} from 'react'; import Sefaria from "../sefaria/sefaria"; -import {ImageWithAltText, InterfaceText} from "../Misc"; +import {ImageWithAltText, InterfaceText, SimpleLinkedBlock} from "../Misc"; export const TopicOfTheDay = () => { let [topic, setTopic] = useState(null); Sefaria.getTopicOfTheDay().then(result => setTopic(result.topic)); - console.log("topic", topic) if (!topic) { return null; } return (
-

Topic of the Day

+

Featured Topic

- +
+ +
+ ")} /> +
+
diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index 9b607b81f5..5c78d47a93 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -114,6 +114,8 @@ const Strings = { // Topic Images "Illustrative image" : "תמונה להמחשה", + // Topic Landing Page + "Featured Topic": "Featured Topic", // Community Page "From the Community: Today on Sefaria": "מן הקהילה: היום בספריא", From 1f20cbc54481c587e530d6b040509f76e5e750d0 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Mon, 2 Dec 2024 14:38:36 +0200 Subject: [PATCH 062/265] feat(Topic Salad): basic dynamic salad functionality --- static/js/TopicLandingPage/TopicSalad.jsx | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/static/js/TopicLandingPage/TopicSalad.jsx b/static/js/TopicLandingPage/TopicSalad.jsx index 031a59000d..dc68ff42e7 100644 --- a/static/js/TopicLandingPage/TopicSalad.jsx +++ b/static/js/TopicLandingPage/TopicSalad.jsx @@ -1,5 +1,7 @@ import React from 'react'; +import {useEffect, useState} from "react"; import {WordSalad} from "./WordSalad"; +import Sefaria from "../sefaria/sefaria"; const exampleSlugs = [ "Potiphar’s wife", "Og", "vines", "diversity", "humanity", "ravens", @@ -17,14 +19,27 @@ const renderSaladItem = (item) => { return {item.text} } +const getRandomSaladItems = async () => { + const topics = await Sefaria.getTopicsByPool('general_en', 50); + const saladItems = topics.map(topic=>({slug: topic.slugs, text: topic.primaryTitle.en})) + return saladItems; +} + + +export const TopicSalad = () => { + + const [salad, setSalad] = useState([]); -export const TopicSalad = ({ numLines, salad, renderItem }) => { + useEffect(async () => { + const saladItems = await getRandomSaladItems(); + setSalad(saladItems); + }, []); - return ( + return (
+ salad={salad}/>
); }; \ No newline at end of file From db7a3d121be85724bc20678fa5eb18c178e75932 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 3 Dec 2024 01:10:28 +0200 Subject: [PATCH 063/265] feat(Topic Salad): add space after each rendered item in wordSalad in order to enable line breaks at spaces --- static/css/s2.css | 4 ++-- static/js/TopicLandingPage/TopicSalad.jsx | 27 ++++++++++++++++++----- static/js/TopicLandingPage/WordSalad.jsx | 7 +++++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 76ff365498..00602e8a41 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4947,7 +4947,6 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus } .topic-salad{ - /*font-family: Roboto;*/ width: 843px; font-size: 22px; font-weight: 400; @@ -4958,7 +4957,8 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus } .topic-salad-item{ margin: 11px; - font-family: 'Roboto'; + --english-font: var(--english-sans-serif-font-family); + --hebrew-font: var(--hebrew-sans-serif-font-family); } .readerNavMenu .sheet { diff --git a/static/js/TopicLandingPage/TopicSalad.jsx b/static/js/TopicLandingPage/TopicSalad.jsx index dc68ff42e7..f8a8aef83f 100644 --- a/static/js/TopicLandingPage/TopicSalad.jsx +++ b/static/js/TopicLandingPage/TopicSalad.jsx @@ -2,6 +2,7 @@ import React from 'react'; import {useEffect, useState} from "react"; import {WordSalad} from "./WordSalad"; import Sefaria from "../sefaria/sefaria"; +import {InterfaceText} from "../Misc"; const exampleSlugs = [ "Potiphar’s wife", "Og", "vines", "diversity", "humanity", "ravens", @@ -16,12 +17,22 @@ const exampleSlugs = [ const exampleItems = exampleSlugs.map(slug => ({text: slug})) const renderSaladItem = (item) => { - return {item.text} + console.log(item) + return( + // + // {/**/} + // {item.text} + // {/*{item.text}*/} + // + + + + ) } -const getRandomSaladItems = async () => { +const fetchRandomSaladItems = async () => { const topics = await Sefaria.getTopicsByPool('general_en', 50); - const saladItems = topics.map(topic=>({slug: topic.slugs, text: topic.primaryTitle.en})) + const saladItems = topics.map(topic=>({slug: topic.slug, text: topic.primaryTitle.en})) return saladItems; } @@ -30,9 +41,13 @@ export const TopicSalad = () => { const [salad, setSalad] = useState([]); - useEffect(async () => { - const saladItems = await getRandomSaladItems(); - setSalad(saladItems); + useEffect(() => { + const fetchSaladItems = async () => { + const saladItems = await fetchRandomSaladItems(); + setSalad(saladItems); + }; + + fetchSaladItems(); }, []); return ( diff --git a/static/js/TopicLandingPage/WordSalad.jsx b/static/js/TopicLandingPage/WordSalad.jsx index de22a4aafd..38a4738b69 100644 --- a/static/js/TopicLandingPage/WordSalad.jsx +++ b/static/js/TopicLandingPage/WordSalad.jsx @@ -9,10 +9,15 @@ export const WordSalad = ({ numLines, salad, renderItem }) => { text: saladItem.text.replace(/ /g, nonWhitespaceInvisibleChar), })); + const renderItemWithSpacesForBreaks = (item)=>{ + const spacedElement = {renderItem(item)} + return spacedElement + } + return (
- {salad.map((item, index) => renderItem(item))} + {salad.map((item, index) => renderItemWithSpacesForBreaks(item))}
); }; \ No newline at end of file From 3cf7558f70558567ad2350028ff369d058cc0d7a Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 3 Dec 2024 11:18:04 +0200 Subject: [PATCH 064/265] feat(Topic Search): fix dropdown css --- static/css/s2.css | 9 +++------ static/js/TopicLandingPage/TopicLandingSearch.jsx | 5 ++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 00602e8a41..494b62a50d 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4849,9 +4849,9 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus } .topic-landing-search-container{ - overflow: hidden; display: flex; align-items: center; + flex-direction: column; padding: 0; text-align: inherit; background: #EDEDEC; @@ -4863,6 +4863,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-landing-search-input-box-wrapper{ display: flex; + margin-top: 15px; } .topic-landing-search-input{ @@ -4907,17 +4908,13 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-landing-search-dropdown{ background: #FFFFFF; - position: absolute; - top: 218px; - /*width: auto;*/ - /*max-width: 130%;*/ width: 606px; box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); border-radius: 0px 0px 6px 4px; padding: 10px; max-height: calc(1.9em * 10 + 2em); /* 2.5em is an estimate of the height per suggestion, and 2em for padding */ - overflow-y: auto; z-index: 2; + margin-top: 14px; } .topic-landing-search-dropdown:empty { padding: 0; diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 0ddb9c9a23..54f86832e2 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -109,7 +109,10 @@ export const TopicLandingSearch = ({openTopic, numOfTopics}) => { renderItems={renderItems.bind(null, openTopic)} containerClassString="topic-landing-search-container" dropdownMenuClassString="topic-landing-search-dropdown" - renderInput={renderInput.bind(null, openTopic, numOfTopics)}/> + renderInput={renderInput.bind(null, openTopic, numOfTopics)} + //to debug styling: + //shouldDisplaySuggestions={()=>true} + />
); }; \ No newline at end of file From 29b45ec8d02566c2a7e09daf590516c0c38431ac Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 3 Dec 2024 12:14:07 +0200 Subject: [PATCH 065/265] feat(Topic Salad): implementation of the 'Rainbow of Truth' component --- static/css/s2.css | 44 +++++++++++++++++++ static/js/RainbowLine.jsx | 28 ++++++++++++ .../js/TopicLandingPage/TopicsLandingPage.jsx | 3 ++ 3 files changed, 75 insertions(+) create mode 100644 static/js/RainbowLine.jsx diff --git a/static/css/s2.css b/static/css/s2.css index 494b62a50d..ddb9ba1c97 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4926,6 +4926,50 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus padding-inline-start: 11px; } +.rainbow-line{ + display: flex; + height: 4.07px; +} +.rainbow-segment{ + width: 84.3px; +} +.tankah-teal{ + background: #004E5F; +} +.commentary-blue{ + background: #4B71B7; +} +.musar-purple{ + background: #7C416F; +} +.mishna-blue { + background: #5A99B7; +} +.talmud-gold{ + background: #CCB479; +} +.modrash-green{ + background: #5D956F; +} +.halakha-red{ + background: #802F3E; +} +.philosophy-purple{ + background: #7F85A9; +} +.tanaitic-greem{ + background: #00827F; +} +.chasidut-green{ + background: #97B386; +} +.topic-landing-upper-rainbow{ + margin-top: 88px; + margin-bottom: 43px; +} +.topic-landing-lower-rainbow{ + margin-top: 43px; +} @font-face { font-family: "ellipsis-font"; src: local("Courier"); diff --git a/static/js/RainbowLine.jsx b/static/js/RainbowLine.jsx new file mode 100644 index 0000000000..dcbd31011f --- /dev/null +++ b/static/js/RainbowLine.jsx @@ -0,0 +1,28 @@ +import React, {useState} from "react"; +import { useCombobox } from 'downshift'; + + + + +export const RainbowLine = ({rainbowClassname}) => { + const colors = [ + "tankah-teal", + "commentary-blue", + "musar-purple", + "mishna-blue", + "talmud-gold", + "modrash-green", + "halakha-red", + "philosophy-purple", + "tanaitic-greem", + "chasidut-green", + ]; + + return ( +
+ {colors.map((color, index) => ( + + ))} +
+ ); +}; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 1bef7189e2..04e87af0ec 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -3,6 +3,7 @@ import {TopicLandingSearch} from "./TopicLandingSearch"; import {NavSidebar} from "../NavSidebar"; import Footer from "../Footer"; import {TopicSalad} from "./TopicSalad"; +import {RainbowLine} from "../RainbowLine"; export const TopicsLandingPage = ({openTopic}) => { @@ -15,7 +16,9 @@ export const TopicsLandingPage = ({openTopic}) => {
+ +
From 07c3a56ac580f10ec2d8f5b5c97c7a53fed05322 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 3 Dec 2024 12:25:05 +0200 Subject: [PATCH 066/265] feat(Topic Salad): remove debug comments --- static/js/TopicLandingPage/TopicSalad.jsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/static/js/TopicLandingPage/TopicSalad.jsx b/static/js/TopicLandingPage/TopicSalad.jsx index f8a8aef83f..90432d26e5 100644 --- a/static/js/TopicLandingPage/TopicSalad.jsx +++ b/static/js/TopicLandingPage/TopicSalad.jsx @@ -17,13 +17,7 @@ const exampleSlugs = [ const exampleItems = exampleSlugs.map(slug => ({text: slug})) const renderSaladItem = (item) => { - console.log(item) return( - // - // {/**/} - // {item.text} - // {/*{item.text}*/} - // From d7da7e950dcb0cfacb29bc3102d512e6dc8cc722 Mon Sep 17 00:00:00 2001 From: saengel Date: Sun, 10 Nov 2024 13:28:37 +0200 Subject: [PATCH 067/265] chore(api): Correct param type to enable accurate results --- sefaria/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/views.py b/sefaria/views.py index d21eae8924..a33a385dbc 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -474,7 +474,7 @@ def bulktext_api(request, refs): g = lambda x: request.GET.get(x, None) min_char = int(g("minChar")) if g("minChar") else None max_char = int(g("maxChar")) if g("maxChar") else None - res = bundle_many_texts(refs, g("useTextFamily"), g("asSizedString"), min_char, max_char, g("transLangPref"), g("ven"), g("vhe")) + res = bundle_many_texts(refs, int(g("useTextFamily")), g("asSizedString"), min_char, max_char, g("transLangPref"), g("ven"), g("vhe")) resp = jsonResponse(res, cb) return resp From bc830f89e5ab4251b65b86f407f7d0e022ce3b05 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 3 Dec 2024 13:19:17 +0200 Subject: [PATCH 068/265] chore(Topic Salad): remove example items --- static/js/TopicLandingPage/TopicSalad.jsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/static/js/TopicLandingPage/TopicSalad.jsx b/static/js/TopicLandingPage/TopicSalad.jsx index 90432d26e5..64dcef9e0b 100644 --- a/static/js/TopicLandingPage/TopicSalad.jsx +++ b/static/js/TopicLandingPage/TopicSalad.jsx @@ -4,18 +4,6 @@ import {WordSalad} from "./WordSalad"; import Sefaria from "../sefaria/sefaria"; import {InterfaceText} from "../Misc"; -const exampleSlugs = [ - "Potiphar’s wife", "Og", "vines", "diversity", "humanity", "ravens", - "Terach", "Fast of Gedaliah", "harmful forces", "Purim", "energy", - "camels", "relationships", "Melchizedek", "prayer", "growth", "Iddo", - "maror", "Yitro", "menorah", "alacrity", "authority", "Yom Kippur", - "bal tashchit", "four questions", "Moses and Joseph’s coffin", "fertility", - "freedom", "the future to come", "Jacob’s dream", "Ezekiel", "social justice", - "compassion", - "hate" -] -const exampleItems = exampleSlugs.map(slug => ({text: slug})) - const renderSaladItem = (item) => { return( From 92bb0432e4697ee8788ca5ff1c31b8baf2979218 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 3 Dec 2024 14:06:41 +0200 Subject: [PATCH 069/265] chore(Topic Salad): cleaner code and comments --- static/css/s2.css | 10 +++---- static/js/RainbowLine.jsx | 2 +- static/js/TopicLandingPage/TopicSalad.jsx | 35 +++++++++++------------ static/js/TopicLandingPage/WordSalad.jsx | 3 +- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index ddb9ba1c97..4337c22759 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4883,14 +4883,12 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus display: flex; list-style-type: none; padding: 6px 12px; - /*font-family: 'EB Garamond';*/ font-family: 'Adobe Garamond Pro'; font-style: normal; font-weight: 400; font-size: 18px; line-height: 23px; cursor: pointer; - /*width: max-content;*/ flex-grow: 1; max-width: 100%; min-height: 10px; @@ -4957,7 +4955,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .philosophy-purple{ background: #7F85A9; } -.tanaitic-greem{ +.tanaitic-green{ background: #00827F; } .chasidut-green{ @@ -4970,8 +4968,10 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-landing-lower-rainbow{ margin-top: 43px; } +/*'Dummy' font, a hack to prevent the ellipsis char from being displayed at the end of a webkit line-clamped element*/ +/*This font addresses only the ellipsis char, rendering its size 0% */ @font-face { - font-family: "ellipsis-font"; + font-family: "hide-ellipsis-char-font"; src: local("Courier"); unicode-range: U+2026; size-adjust: 0%; @@ -4984,7 +4984,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus -webkit-box-orient: vertical; -webkit-line-clamp: var(--num-lines); text-overflow: clip; - font-family: ellipsis-font; + font-family: hide-ellipsis-char-font; } .topic-salad{ diff --git a/static/js/RainbowLine.jsx b/static/js/RainbowLine.jsx index dcbd31011f..aa95e04ab8 100644 --- a/static/js/RainbowLine.jsx +++ b/static/js/RainbowLine.jsx @@ -14,7 +14,7 @@ export const RainbowLine = ({rainbowClassname}) => { "modrash-green", "halakha-red", "philosophy-purple", - "tanaitic-greem", + "tanaitic-green", "chasidut-green", ]; diff --git a/static/js/TopicLandingPage/TopicSalad.jsx b/static/js/TopicLandingPage/TopicSalad.jsx index 64dcef9e0b..6c991a4b43 100644 --- a/static/js/TopicLandingPage/TopicSalad.jsx +++ b/static/js/TopicLandingPage/TopicSalad.jsx @@ -4,32 +4,31 @@ import {WordSalad} from "./WordSalad"; import Sefaria from "../sefaria/sefaria"; import {InterfaceText} from "../Misc"; -const renderSaladItem = (item) => { + +export const TopicSalad = () => { + + const [salad, setSalad] = useState([]); + + const renderSaladItem = (item) => { return( - ) -} + )} -const fetchRandomSaladItems = async () => { - const topics = await Sefaria.getTopicsByPool('general_en', 50); - const saladItems = topics.map(topic=>({slug: topic.slug, text: topic.primaryTitle.en})) - return saladItems; -} + const fetchRandomSaladItems = async () => { + const topics = await Sefaria.getTopicsByPool('general_en', 50); + const saladItems = topics.map(topic=>({slug: topic.slug, text: topic.primaryTitle.en})) + return saladItems; + } - -export const TopicSalad = () => { - - const [salad, setSalad] = useState([]); + const loadSalad = async () => { + const saladItems = await fetchRandomSaladItems(); + setSalad(saladItems); + }; useEffect(() => { - const fetchSaladItems = async () => { - const saladItems = await fetchRandomSaladItems(); - setSalad(saladItems); - }; - - fetchSaladItems(); + loadSalad(); }, []); return ( diff --git a/static/js/TopicLandingPage/WordSalad.jsx b/static/js/TopicLandingPage/WordSalad.jsx index 38a4738b69..f2b00f5aa9 100644 --- a/static/js/TopicLandingPage/WordSalad.jsx +++ b/static/js/TopicLandingPage/WordSalad.jsx @@ -3,13 +3,14 @@ import React from 'react'; export const WordSalad = ({ numLines, salad, renderItem }) => { const nonWhitespaceInvisibleChar = '\u00A0' - + // replace the normal space with the HTML space char, in order for css to not break line mid-item. salad = salad.map(saladItem => ({ ...saladItem, text: saladItem.text.replace(/ /g, nonWhitespaceInvisibleChar), })); const renderItemWithSpacesForBreaks = (item)=>{ + // needed in order for css to recognize space after each item to potentially break the line at this space. const spacedElement = {renderItem(item)} return spacedElement } From 3e0e73bd94badee8b646c78d61027226eb03f0d2 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Tue, 3 Dec 2024 15:13:30 +0200 Subject: [PATCH 070/265] Revert "chore(api): Correct param type to enable accurate results" This reverts commit 353e652e87a5101e122fd9329d394e2d16983dda. --- sefaria/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/views.py b/sefaria/views.py index a33a385dbc..d21eae8924 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -474,7 +474,7 @@ def bulktext_api(request, refs): g = lambda x: request.GET.get(x, None) min_char = int(g("minChar")) if g("minChar") else None max_char = int(g("maxChar")) if g("maxChar") else None - res = bundle_many_texts(refs, int(g("useTextFamily")), g("asSizedString"), min_char, max_char, g("transLangPref"), g("ven"), g("vhe")) + res = bundle_many_texts(refs, g("useTextFamily"), g("asSizedString"), min_char, max_char, g("transLangPref"), g("ven"), g("vhe")) resp = jsonResponse(res, cb) return resp From d46a04d25ed767833c85e355b193b16ddd6ab95c Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Wed, 4 Dec 2024 11:34:11 +0200 Subject: [PATCH 071/265] chore(Topic Salad): support he interface --- static/js/TopicLandingPage/TopicSalad.jsx | 15 ++++++++------- static/js/sefaria/sefaria.js | 3 +++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/static/js/TopicLandingPage/TopicSalad.jsx b/static/js/TopicLandingPage/TopicSalad.jsx index 6c991a4b43..0179b0f282 100644 --- a/static/js/TopicLandingPage/TopicSalad.jsx +++ b/static/js/TopicLandingPage/TopicSalad.jsx @@ -8,17 +8,18 @@ import {InterfaceText} from "../Misc"; export const TopicSalad = () => { const [salad, setSalad] = useState([]); + const lang = Sefaria.interfaceLang == 'hebrew' ? 'he' : 'en'; const renderSaladItem = (item) => { - return( - - - - )} + return( + + ) + } const fetchRandomSaladItems = async () => { - const topics = await Sefaria.getTopicsByPool('general_en', 50); - const saladItems = topics.map(topic=>({slug: topic.slug, text: topic.primaryTitle.en})) + const poolName = Sefaria.getLangSpecificTopicPoolName('general', lang); + const topics = await Sefaria.getTopicsByPool(poolName, 50); + const saladItems = topics.map(topic=>({slug: topic.slug, text: topic.primaryTitle[lang]})) return saladItems; } diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 494ab58c09..8f720206ae 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1964,6 +1964,9 @@ _media: {}, store: this._TopicsByPool }); }, + getLangSpecificTopicPoolName: function(poolName, lang){ + return `${poolName}_${lang}` + }, _related: {}, related: function(ref, callback) { // Single API to bundle public links, sheets, and notes by ref. From 69a76c66de45ace5dbc889494a1675dbc1707a6f Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Wed, 4 Dec 2024 12:35:28 +0200 Subject: [PATCH 072/265] chore(Topic Salad): add docs to Word Salad and changing location of file --- static/js/TopicLandingPage/TopicSalad.jsx | 2 +- static/js/TopicLandingPage/WordSalad.jsx | 24 -------------- static/js/WordSalad.jsx | 38 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 25 deletions(-) delete mode 100644 static/js/TopicLandingPage/WordSalad.jsx create mode 100644 static/js/WordSalad.jsx diff --git a/static/js/TopicLandingPage/TopicSalad.jsx b/static/js/TopicLandingPage/TopicSalad.jsx index 0179b0f282..9e83fdf315 100644 --- a/static/js/TopicLandingPage/TopicSalad.jsx +++ b/static/js/TopicLandingPage/TopicSalad.jsx @@ -1,6 +1,6 @@ import React from 'react'; import {useEffect, useState} from "react"; -import {WordSalad} from "./WordSalad"; +import {WordSalad} from "../WordSalad"; import Sefaria from "../sefaria/sefaria"; import {InterfaceText} from "../Misc"; diff --git a/static/js/TopicLandingPage/WordSalad.jsx b/static/js/TopicLandingPage/WordSalad.jsx deleted file mode 100644 index f2b00f5aa9..0000000000 --- a/static/js/TopicLandingPage/WordSalad.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; - - -export const WordSalad = ({ numLines, salad, renderItem }) => { - const nonWhitespaceInvisibleChar = '\u00A0' - // replace the normal space with the HTML space char, in order for css to not break line mid-item. - salad = salad.map(saladItem => ({ - ...saladItem, - text: saladItem.text.replace(/ /g, nonWhitespaceInvisibleChar), - })); - - const renderItemWithSpacesForBreaks = (item)=>{ - // needed in order for css to recognize space after each item to potentially break the line at this space. - const spacedElement = {renderItem(item)} - return spacedElement - } - - - return ( -
- {salad.map((item, index) => renderItemWithSpacesForBreaks(item))} -
- ); -}; \ No newline at end of file diff --git a/static/js/WordSalad.jsx b/static/js/WordSalad.jsx new file mode 100644 index 0000000000..8cf959f2e2 --- /dev/null +++ b/static/js/WordSalad.jsx @@ -0,0 +1,38 @@ +import React from 'react'; + + +export const WordSalad = ({ numLines, salad, renderItem }) => { +/** + * This component renders a collection of text items, styled to fit within a specified + * number of lines. Each item can be rendered using a custom `renderItem` function, and + * spaces within the items are replaced with non-breaking spaces to ensure consistent + * line breaking behavior in CSS. + * + * @param {number} props.numLines - The number of lines the container should display. + * This is used in the CSS to control layout. + * @param {Array} props.salad - An array of objects representing the text items to display. + * Each object must have a `text` property. + * @param {Function} props.renderItem - A function that receives an item from the `salad` array + * and returns a React element to render it. + */ + + const nonWhitespaceInvisibleChar = '\u00A0' + // replace the normal space with the HTML space char, in order for css to not break line mid-item. + salad = salad.map(saladItem => ({ + ...saladItem, + text: saladItem.text.replace(/ /g, nonWhitespaceInvisibleChar), + })); + + const renderItemWithSpacesForBreaks = (item)=>{ + // needed in order for css to recognize space after each item to potentially break the line at this space. + const spacedElement = {renderItem(item)} + return spacedElement + } + + + return ( +
+ {salad.map((item, index) => renderItemWithSpacesForBreaks(item))} +
+ ); +}; \ No newline at end of file From 97935381f57f0af27b0b66aa437c4ac3666e84b5 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Thu, 5 Dec 2024 14:23:50 +0200 Subject: [PATCH 073/265] refactor(Rainbow of Truth): css based implementation --- static/css/common.css | 23 +++++++++++++++++ static/css/static.css | 9 ------- static/js/RainbowLine.jsx | 25 ++----------------- .../henry-and-julia-koschitzky-apps.html | 2 +- 4 files changed, 26 insertions(+), 33 deletions(-) diff --git a/static/css/common.css b/static/css/common.css index c0c4d8dcbd..d5b68ac8e9 100644 --- a/static/css/common.css +++ b/static/css/common.css @@ -753,4 +753,27 @@ a:active { } .static label .int-he .sub{ margin-right: 4px; +} +.categoryColorLineRainbow { + top: 60px; + left: 0; + width: 100%; + height: 4px; + z-index: 101; + background: linear-gradient( + 90deg, + var(--tanakh-teal) 0% 10%, + var(--commentary-blue) 10% 20%, + var(--mussar-purple) 20% 30%, + var(--mishnah-blue) 30% 40%, + var(--talmud-gold) 40% 50%, + var(--midrash-green) 50% 60%, + var(--halakhah-red) 60% 70%, + var(--philosophy-purple) 70% 80%, + var(--taanitic-green) 80% 90%, + var(--chasidut-green) 90% 100% + ); +} +.categoryColorLineRainbow.fixedBar{ + position: fixed; } \ No newline at end of file diff --git a/static/css/static.css b/static/css/static.css index 4b1e6608c1..57dce0bcf7 100644 --- a/static/css/static.css +++ b/static/css/static.css @@ -1747,15 +1747,6 @@ body.interface-hebrew #visualGardenPage .filter-title .reset { #koschitzky.static { margin-top: 60px; } -.static .categoryColorLineRainbow { - position: fixed; - top: 60px; - left: 0; - width: 100%; - height: 4px; - z-index: 101; - background: linear-gradient(90deg, #00505E 0% 10%, #5698B4 10% 20%, #CCB37C 20% 30%, #5B9370 30% 40%, #823241 40% 50%, #5A4474 50% 60%, #AD4F66 60% 70%, #7285A6 70% 80%, #00807E 80% 90%, #4872B3 90% 100%) -} /******* mobile landing page ****/ #mobilePage{ diff --git a/static/js/RainbowLine.jsx b/static/js/RainbowLine.jsx index aa95e04ab8..292620d283 100644 --- a/static/js/RainbowLine.jsx +++ b/static/js/RainbowLine.jsx @@ -1,28 +1,7 @@ -import React, {useState} from "react"; -import { useCombobox } from 'downshift'; - - - +import React from "react"; export const RainbowLine = ({rainbowClassname}) => { - const colors = [ - "tankah-teal", - "commentary-blue", - "musar-purple", - "mishna-blue", - "talmud-gold", - "modrash-green", - "halakha-red", - "philosophy-purple", - "tanaitic-green", - "chasidut-green", - ]; - return ( -
- {colors.map((color, index) => ( - - ))} -
+
); }; \ No newline at end of file diff --git a/templates/static/henry-and-julia-koschitzky-apps.html b/templates/static/henry-and-julia-koschitzky-apps.html index 45f3f15d42..29a0bcb9a0 100644 --- a/templates/static/henry-and-julia-koschitzky-apps.html +++ b/templates/static/henry-and-julia-koschitzky-apps.html @@ -13,7 +13,7 @@ {% block content %}
-
+

Sefaria App for iOS and Android From 3bc825d03c02e810160577c0470bbe4286c29b90 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Thu, 5 Dec 2024 15:11:29 +0200 Subject: [PATCH 074/265] refactor(Word Salad): cleaner way to handle item wrapping --- static/css/s2.css | 3 +++ static/js/WordSalad.jsx | 21 +++++++-------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 4337c22759..1b193465b5 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5001,6 +5001,9 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus --english-font: var(--english-sans-serif-font-family); --hebrew-font: var(--hebrew-sans-serif-font-family); } +.no-wrapping-salad-item-container{ + white-space: nowrap; +} .readerNavMenu .sheet { display: flex; diff --git a/static/js/WordSalad.jsx b/static/js/WordSalad.jsx index 8cf959f2e2..3b09475586 100644 --- a/static/js/WordSalad.jsx +++ b/static/js/WordSalad.jsx @@ -3,36 +3,29 @@ import React from 'react'; export const WordSalad = ({ numLines, salad, renderItem }) => { /** - * This component renders a collection of text items, styled to fit within a specified + * This component renders a collection of items, styled to fit within a specified * number of lines. Each item can be rendered using a custom `renderItem` function, and * spaces within the items are replaced with non-breaking spaces to ensure consistent * line breaking behavior in CSS. * * @param {number} props.numLines - The number of lines the container should display. * This is used in the CSS to control layout. - * @param {Array} props.salad - An array of objects representing the text items to display. - * Each object must have a `text` property. + * @param {Array} props.salad - An array of objects representing the items to display. + * * @param {Function} props.renderItem - A function that receives an item from the `salad` array * and returns a React element to render it. */ - const nonWhitespaceInvisibleChar = '\u00A0' - // replace the normal space with the HTML space char, in order for css to not break line mid-item. - salad = salad.map(saladItem => ({ - ...saladItem, - text: saladItem.text.replace(/ /g, nonWhitespaceInvisibleChar), - })); - const renderItemWithSpacesForBreaks = (item)=>{ - // needed in order for css to recognize space after each item to potentially break the line at this space. - const spacedElement = {renderItem(item)} - return spacedElement + // inner span to prevent wrapping on spaces mid-item, outer span with trailing space to allow wrapping between items + const trailingSpacedElement = {renderItem(item)} + return trailingSpacedElement; } return (
- {salad.map((item, index) => renderItemWithSpacesForBreaks(item))} + {salad.map(renderItemWithSpacesForBreaks)}
); }; \ No newline at end of file From 9331be117d3b3b8015cd2fd9a13b3b200d4b5870 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Thu, 5 Dec 2024 15:27:44 +0200 Subject: [PATCH 075/265] refactor(Topic and Word Salad): cleaner code following PR comments --- static/css/s2.css | 3 +++ static/js/TopicLandingPage/TopicLandingSearch.jsx | 2 -- static/js/TopicLandingPage/TopicSalad.jsx | 13 +++++++++---- static/js/TopicLandingPage/TopicsLandingPage.jsx | 2 -- static/js/WordSalad.jsx | 2 -- static/js/sefaria/sefaria.js | 3 ++- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 1b193465b5..82e829baad 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4843,6 +4843,9 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus justify-content: center; align-items: center; } +.readerNavMenu .content .contentInner.topic-lading-page-content{ + width: auto; +} .interface-hebrew .topic-landing-page-wrapper{ direction: rtl; diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 54f86832e2..b3b7a61d1a 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -110,8 +110,6 @@ export const TopicLandingSearch = ({openTopic, numOfTopics}) => { containerClassString="topic-landing-search-container" dropdownMenuClassString="topic-landing-search-dropdown" renderInput={renderInput.bind(null, openTopic, numOfTopics)} - //to debug styling: - //shouldDisplaySuggestions={()=>true} />

); diff --git a/static/js/TopicLandingPage/TopicSalad.jsx b/static/js/TopicLandingPage/TopicSalad.jsx index 9e83fdf315..ee72e7bffc 100644 --- a/static/js/TopicLandingPage/TopicSalad.jsx +++ b/static/js/TopicLandingPage/TopicSalad.jsx @@ -3,23 +3,23 @@ import {useEffect, useState} from "react"; import {WordSalad} from "../WordSalad"; import Sefaria from "../sefaria/sefaria"; import {InterfaceText} from "../Misc"; +import {RainbowLine} from "../RainbowLine"; export const TopicSalad = () => { const [salad, setSalad] = useState([]); - const lang = Sefaria.interfaceLang == 'hebrew' ? 'he' : 'en'; const renderSaladItem = (item) => { return( - + ) } const fetchRandomSaladItems = async () => { - const poolName = Sefaria.getLangSpecificTopicPoolName('general', lang); + const poolName = Sefaria.getLangSpecificTopicPoolName('general'); const topics = await Sefaria.getTopicsByPool(poolName, 50); - const saladItems = topics.map(topic=>({slug: topic.slug, text: topic.primaryTitle[lang]})) + const saladItems = topics.map(topic=>({slug: topic.slug, text: topic.primaryTitle})); return saladItems; } @@ -33,10 +33,15 @@ export const TopicSalad = () => { }, []); return ( + <> +
+ + + ); }; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 04e87af0ec..f1d7bc2265 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -16,9 +16,7 @@ export const TopicsLandingPage = ({openTopic}) => {
- -
diff --git a/static/js/WordSalad.jsx b/static/js/WordSalad.jsx index 3b09475586..a3dabc9e8a 100644 --- a/static/js/WordSalad.jsx +++ b/static/js/WordSalad.jsx @@ -9,9 +9,7 @@ export const WordSalad = ({ numLines, salad, renderItem }) => { * line breaking behavior in CSS. * * @param {number} props.numLines - The number of lines the container should display. - * This is used in the CSS to control layout. * @param {Array} props.salad - An array of objects representing the items to display. - * * @param {Function} props.renderItem - A function that receives an item from the `salad` array * and returns a React element to render it. */ diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 8f720206ae..3cc2fe3881 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1964,7 +1964,8 @@ _media: {}, store: this._TopicsByPool }); }, - getLangSpecificTopicPoolName: function(poolName, lang){ + getLangSpecificTopicPoolName: function(poolName){ + const lang = this.interfaceLang == 'hebrew' ? 'he' : 'en'; return `${poolName}_${lang}` }, _related: {}, From c8bd0467610ae1027c3fbb5747b34d597e845810 Mon Sep 17 00:00:00 2001 From: yonadavGit Date: Thu, 5 Dec 2024 15:52:19 +0200 Subject: [PATCH 076/265] refactor(Rainbow of Truth): remove redundant css from previous implementation --- static/css/s2.css | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 82e829baad..800c427d9c 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4926,44 +4926,6 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus top: 6px; padding-inline-start: 11px; } - -.rainbow-line{ - display: flex; - height: 4.07px; -} -.rainbow-segment{ - width: 84.3px; -} -.tankah-teal{ - background: #004E5F; -} -.commentary-blue{ - background: #4B71B7; -} -.musar-purple{ - background: #7C416F; -} -.mishna-blue { - background: #5A99B7; -} -.talmud-gold{ - background: #CCB479; -} -.modrash-green{ - background: #5D956F; -} -.halakha-red{ - background: #802F3E; -} -.philosophy-purple{ - background: #7F85A9; -} -.tanaitic-green{ - background: #00827F; -} -.chasidut-green{ - background: #97B386; -} .topic-landing-upper-rainbow{ margin-top: 88px; margin-bottom: 43px; From 914c9bfe3b107d45fc33307598b4a472629fcb6c Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Sat, 7 Dec 2024 20:48:29 +0200 Subject: [PATCH 077/265] refactor(topics): only fetch topic of the day on first render --- static/js/TopicLandingPage/TopicOfTheDay.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/static/js/TopicLandingPage/TopicOfTheDay.jsx b/static/js/TopicLandingPage/TopicOfTheDay.jsx index 690d130e35..e37ce771f4 100644 --- a/static/js/TopicLandingPage/TopicOfTheDay.jsx +++ b/static/js/TopicLandingPage/TopicOfTheDay.jsx @@ -1,10 +1,14 @@ -import React, {useState} from 'react'; +import React, {useState, useEffect} from 'react'; import Sefaria from "../sefaria/sefaria"; import {ImageWithAltText, InterfaceText, SimpleLinkedBlock} from "../Misc"; + export const TopicOfTheDay = () => { let [topic, setTopic] = useState(null); - Sefaria.getTopicOfTheDay().then(result => setTopic(result.topic)); + useEffect(() => { + Sefaria.getTopicOfTheDay().then(result => setTopic(result.topic)); + }, []); + if (!topic) { return null; } return (
From acf585351412966c5dfbe3c245e4c061887beb62 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Sun, 8 Dec 2024 11:22:29 +0200 Subject: [PATCH 078/265] chore(Topic Salad): minor css changes --- static/css/s2.css | 6 +++--- static/js/TopicLandingPage/TopicsLandingPage.jsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 800c427d9c..bfd4663b15 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4837,14 +4837,14 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus justify-content: center; } -.topic-lading-page-content{ +.topic-landing-page-content{ display: flex; flex-direction: column; justify-content: center; align-items: center; } -.readerNavMenu .content .contentInner.topic-lading-page-content{ - width: auto; +.readerNavMenu .content .contentInner.topic-landing-page-content{ + width: 843px; } .interface-hebrew .topic-landing-page-wrapper{ diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index f1d7bc2265..f235e22cfc 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -14,7 +14,7 @@ export const TopicsLandingPage = ({openTopic}) => {
-
+
From 13acd7569209a5b09b61df9eb8096f009542b81a Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Sun, 8 Dec 2024 15:15:15 +0200 Subject: [PATCH 079/265] feat(Topic Landing Calendar): skeleton of new Calendar component --- static/css/s2.css | 9 ++++++++ .../TopicLandingPage/TopicLandingCalendar.jsx | 23 +++++++++++++++++++ .../js/TopicLandingPage/TopicsLandingPage.jsx | 7 ++++++ 3 files changed, 39 insertions(+) create mode 100644 static/js/TopicLandingPage/TopicLandingCalendar.jsx diff --git a/static/css/s2.css b/static/css/s2.css index 3e8b9f8c5f..1698f01d5b 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5005,6 +5005,15 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus white-space: nowrap; } +.topic-landing-calendar .calendar-header{ + color: var(--Dark-Grey, #666); + font-family: Roboto; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: normal; +} + .readerNavMenu .sheet { display: flex; justify-content: space-between; diff --git a/static/js/TopicLandingPage/TopicLandingCalendar.jsx b/static/js/TopicLandingPage/TopicLandingCalendar.jsx new file mode 100644 index 0000000000..bc65767b76 --- /dev/null +++ b/static/js/TopicLandingPage/TopicLandingCalendar.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import {useEffect, useState} from "react"; +import {WordSalad} from "../WordSalad"; +import Sefaria from "../sefaria/sefaria"; +import {InterfaceText} from "../Misc"; +import {Card} from "../common/Card"; + + +export const TopicLandingCalendar = ({ header, title, description, link, children }) => { + return ( +
+
+ {header} +
+ + {children &&
{children}
} +
+ ); +}; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index f235e22cfc..dc4cc1ee51 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -4,6 +4,7 @@ import {NavSidebar} from "../NavSidebar"; import Footer from "../Footer"; import {TopicSalad} from "./TopicSalad"; import {RainbowLine} from "../RainbowLine"; +import {TopicLandingCalendar} from "./TopicLandingCalendar"; export const TopicsLandingPage = ({openTopic}) => { @@ -17,6 +18,12 @@ export const TopicsLandingPage = ({openTopic}) => {
+ +
From e58d0bb0a321fa5e4a36e9d10d214a0b214d263a Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:21:42 +0200 Subject: [PATCH 080/265] feat(Topic Landing Calendar): style header of calendar component --- static/css/s2.css | 3 ++- static/js/TopicLandingPage/TopicLandingCalendar.jsx | 2 +- static/js/TopicLandingPage/TopicsLandingPage.jsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 1698f01d5b..9a5b9796c2 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5007,7 +5007,8 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-landing-calendar .calendar-header{ color: var(--Dark-Grey, #666); - font-family: Roboto; + --english-font: var(--english-sans-serif-font-family); + --hebrew-font: var(--hebrew-sans-serif-font-family); font-size: 16px; font-style: normal; font-weight: 600; diff --git a/static/js/TopicLandingPage/TopicLandingCalendar.jsx b/static/js/TopicLandingPage/TopicLandingCalendar.jsx index bc65767b76..71128c056c 100644 --- a/static/js/TopicLandingPage/TopicLandingCalendar.jsx +++ b/static/js/TopicLandingPage/TopicLandingCalendar.jsx @@ -10,7 +10,7 @@ export const TopicLandingCalendar = ({ header, title, description, link, childre return (
- {header} +
{ From 90ec6eb6fed6086c3cab4a8158a9e3da82602e88 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:05:30 +0200 Subject: [PATCH 081/265] feat(Topic Landing Temporal): skeleton of TopicLandingParasha --- .../TopicLandingPage/TopicLandingCalendar.jsx | 3 --- .../TopicLandingPage/TopicLandingParsha.jsx | 25 +++++++++++++++++++ .../js/TopicLandingPage/TopicsLandingPage.jsx | 12 +++++---- 3 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 static/js/TopicLandingPage/TopicLandingParsha.jsx diff --git a/static/js/TopicLandingPage/TopicLandingCalendar.jsx b/static/js/TopicLandingPage/TopicLandingCalendar.jsx index 71128c056c..8be1b4b166 100644 --- a/static/js/TopicLandingPage/TopicLandingCalendar.jsx +++ b/static/js/TopicLandingPage/TopicLandingCalendar.jsx @@ -1,7 +1,4 @@ import React from 'react'; -import {useEffect, useState} from "react"; -import {WordSalad} from "../WordSalad"; -import Sefaria from "../sefaria/sefaria"; import {InterfaceText} from "../Misc"; import {Card} from "../common/Card"; diff --git a/static/js/TopicLandingPage/TopicLandingParsha.jsx b/static/js/TopicLandingPage/TopicLandingParsha.jsx new file mode 100644 index 0000000000..c677e95309 --- /dev/null +++ b/static/js/TopicLandingPage/TopicLandingParsha.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import {InterfaceText} from "../Misc"; +import {Card} from "../common/Card"; +import {TopicLandingCalendar} from "./TopicLandingCalendar"; +import {useState, useEffect} from "react"; + + +export const TopicLandingParasha = ({ handleClick }) => { + const [parashah, setParashah] = useState({}); + + useEffect(() => { + Sefaria.getUpcomingDay('parasha').then(setParashah); + }, []); + + const parashahTitle = parashah.displayValue; + const parashahDesc = parashah.description; + console.log(parashahTitle); + + return ( + + ); +}; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index 915a836e9d..e2656b2f46 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -5,6 +5,7 @@ import Footer from "../Footer"; import {TopicSalad} from "./TopicSalad"; import {RainbowLine} from "../RainbowLine"; import {TopicLandingCalendar} from "./TopicLandingCalendar"; +import {TopicLandingParasha} from "./TopicLandingParsha"; export const TopicsLandingPage = ({openTopic}) => { @@ -18,11 +19,12 @@ export const TopicsLandingPage = ({openTopic}) => {
- + {/**/} +
From c7f03e123ee9d9b47931f8ed7dd70cf8d98c0210 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:35:47 +0200 Subject: [PATCH 082/265] feat(Topic Landing Temporal): children of TopicLandingParasha --- static/css/s2.css | 9 +++++ static/js/NavSidebar.jsx | 3 +- .../TopicLandingPage/TopicLandingParsha.jsx | 36 +++++++++++++++---- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 9a5b9796c2..aee3617c3c 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5015,6 +5015,15 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus line-height: normal; } +.topic-landing-calendar .learn-more-prompt{ + font-size: 14px; + line-height: 18px; + color: #666; + margin-inline-end: 20px; + --english-font: var(--english-sans-serif-font-family); + --hebrew-font: var(--hebrew-sans-serif-font-family); +} + .readerNavMenu .sheet { display: flex; justify-content: space-between; diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 38d0355549..11aac41b3b 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -938,5 +938,6 @@ const PortalNewsletter = ({title, description}) => { export { NavSidebar, SidebarModules, - RecentlyViewed + RecentlyViewed, + ParashahLink }; diff --git a/static/js/TopicLandingPage/TopicLandingParsha.jsx b/static/js/TopicLandingPage/TopicLandingParsha.jsx index c677e95309..e9486b769a 100644 --- a/static/js/TopicLandingPage/TopicLandingParsha.jsx +++ b/static/js/TopicLandingPage/TopicLandingParsha.jsx @@ -1,8 +1,9 @@ import React from 'react'; -import {InterfaceText} from "../Misc"; -import {Card} from "../common/Card"; import {TopicLandingCalendar} from "./TopicLandingCalendar"; import {useState, useEffect} from "react"; +import {ParashahLink} from "../NavSidebar"; +import {InterfaceText} from "../Misc"; +import Sefaria from "../sefaria/sefaria"; export const TopicLandingParasha = ({ handleClick }) => { @@ -14,12 +15,33 @@ export const TopicLandingParasha = ({ handleClick }) => { const parashahTitle = parashah.displayValue; const parashahDesc = parashah.description; - console.log(parashahTitle); + const parashahTopicLink = `topics/${parashah?.topic}`; + const parashahRefLink = `/${parashah?.url}`; + const learnMorePrompt = ( + + {`Learn More on Parashat ${parashahTitle?.en}>`} + + ); + const readPortionButton = ( + + + + ) return ( - + +
+ {learnMorePrompt} +
+ +
+ {readPortionButton} +
+
); }; \ No newline at end of file From bbe64962464a1c75bb7e1cf4745f9a690e25aaa7 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:54:45 +0200 Subject: [PATCH 083/265] feat(Topic Landing Temporal): seasonal topic api endpoint --- django_topics/models/seasonal_topic.py | 17 +++++++++++++++++ reader/views.py | 12 ++++++++++++ sefaria/urls.py | 1 + .../js/TopicLandingPage/TopicLandingParsha.jsx | 2 +- 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/django_topics/models/seasonal_topic.py b/django_topics/models/seasonal_topic.py index ff45333529..090319e96b 100644 --- a/django_topics/models/seasonal_topic.py +++ b/django_topics/models/seasonal_topic.py @@ -1,7 +1,23 @@ from django.db import models from django_topics.models import Topic from django.core.exceptions import ValidationError +from django.utils.timezone import now +from datetime import datetime +class SeasonalTopicManager(models.Manager): + def get_seasonal_topic(self, lang: str, date: datetime = None) -> 'SeasonalTopic': + """ + Return seasonal topic for given date or closest date that is less than or equal to given date + @param lang: language code, "en" or "he" + @param date: datetime object + @return: + """ + date = date or now().date() + return ( + self.filter(start_date__lte=date, lang=lang) + .order_by('-start_date') + .first() + ) class SeasonalTopic(models.Model): topic = models.ForeignKey( @@ -25,6 +41,7 @@ class SeasonalTopic(models.Model): display_date_prefix = models.CharField(max_length=255, blank=True, null=True) display_date_suffix = models.CharField(max_length=255, blank=True, null=True) lang = models.CharField(max_length=2, choices=[('en', 'English'), ('he', 'Hebrew')]) + objects = SeasonalTopicManager() class Meta: unique_together = ('topic', 'start_date') diff --git a/reader/views.py b/reader/views.py index 8ff2b35cba..2d6f05d136 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3281,6 +3281,18 @@ def topic_ref_bulk_api(request): all_links_touched.append(ref_topic_dict) return jsonResponse(all_links_touched) +@catch_error_as_json +def seasonal_topic_api(request): + from django_topics.models import SeasonalTopic + + lang = request.GET.get("lang") + cb = request.GET.get("callback", None) + stopic = SeasonalTopic.objects.get_seasonal_topic(lang) + if not stopic: + return jsonResponse({'error': f'No seasonal topic found for lang "{lang}"'}, status=404) + mongo_topic = Topic.init(stopic.topic.slug) + response = {'topic': mongo_topic.contents(), 'date': stopic.start_date.isoformat()} + return jsonResponse(response, callback=cb) @catch_error_as_json diff --git a/sefaria/urls.py b/sefaria/urls.py index 84039cc46c..76968294aa 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -265,6 +265,7 @@ url(r'^api/topics$', reader_views.topics_list_api), url(r'^api/topics/generate-prompts/(?P.+)$', reader_views.generate_topic_prompts_api), url(r'^api/topics-graph/(?P.+)$', reader_views.topic_graph_api), + url(r'^_api/topics/seasonal-topic/?$', reader_views.seasonal_topic_api), url(r'^api/topics/pools/(?P.+)$', reader_views.topic_pool_api), url(r'^api/ref-topic-links/bulk$', reader_views.topic_ref_bulk_api), url(r'^api/ref-topic-links/(?P.+)$', reader_views.topic_ref_api), diff --git a/static/js/TopicLandingPage/TopicLandingParsha.jsx b/static/js/TopicLandingPage/TopicLandingParsha.jsx index e9486b769a..b1b821e03a 100644 --- a/static/js/TopicLandingPage/TopicLandingParsha.jsx +++ b/static/js/TopicLandingPage/TopicLandingParsha.jsx @@ -6,7 +6,7 @@ import {InterfaceText} from "../Misc"; import Sefaria from "../sefaria/sefaria"; -export const TopicLandingParasha = ({ handleClick }) => { +export const TopicLandingParasha = () => { const [parashah, setParashah] = useState({}); useEffect(() => { From c4b36a393bc890936cc4fa925840b42df1224fcb Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:21:51 +0200 Subject: [PATCH 084/265] feat(Topic Landing Temporal): skeleton of TopicLandingSeasonal --- .../TopicLandingPage/TopicLandingSeasonal.jsx | 28 +++++++++++++++++++ .../js/TopicLandingPage/TopicsLandingPage.jsx | 8 ++---- static/js/sefaria/sefaria.js | 8 ++++++ 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 static/js/TopicLandingPage/TopicLandingSeasonal.jsx diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx new file mode 100644 index 0000000000..f0649a26e3 --- /dev/null +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import {TopicLandingCalendar} from "./TopicLandingCalendar"; +import {useState, useEffect} from "react"; +import {ParashahLink} from "../NavSidebar"; +import {InterfaceText} from "../Misc"; +import Sefaria from "../sefaria/sefaria"; + + +export const TopicLandingSeasonal = () => { + const [seasonal, setSeasonal] = useState({}); + + useEffect(() => { + Sefaria.getSeasonalTopic().then(setSeasonal); + }, []); + const title = seasonal.topic?.primaryTitle; + const description = seasonal.topic?.description + const link = `/topics/${seasonal.slug}` + + + return ( + + ); +}; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index e2656b2f46..c0f90f1106 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -6,6 +6,8 @@ import {TopicSalad} from "./TopicSalad"; import {RainbowLine} from "../RainbowLine"; import {TopicLandingCalendar} from "./TopicLandingCalendar"; import {TopicLandingParasha} from "./TopicLandingParsha"; +import Search from "../sefaria/search"; +import {TopicLandingSeasonal} from "./TopicLandingSeasonal"; export const TopicsLandingPage = ({openTopic}) => { @@ -19,12 +21,8 @@ export const TopicsLandingPage = ({openTopic}) => {
- {/**/} +
diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 4f38dbb611..30adc81ef5 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2780,6 +2780,14 @@ _media: {}, const key = this._getTopicCacheKey(slug, {annotated, with_html}); return this._topics[key]; }, + _seasonalTopic: {}, + getSeasonalTopic: function() { + return this._cachedApiPromise({ + url: `${Sefaria.apiHost}/_api/topics/seasonal-topic?lang=${Sefaria.interfaceLang.slice(0, 2)}`, + key: (new Date()).toLocaleDateString(), + store: this._seasonalTopic, + }); + }, _topicSlugsToTitles: null, slugsToTitles: function() { //initializes _topicSlugsToTitles for Topic Editor tool and adds necessary "Choose a Category" and "Main Menu" for From 34b70bff4933143f02eb7410a042dc1ddddf78f5 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:10:58 +0200 Subject: [PATCH 085/265] feat(Topic Landing Temporal): style temporal components --- static/css/s2.css | 29 ++++++++++++++++++- .../TopicLandingPage/TopicLandingParsha.jsx | 6 +++- .../TopicLandingPage/TopicLandingSeasonal.jsx | 6 ++-- .../js/TopicLandingPage/TopicsLandingPage.jsx | 3 +- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index aee3617c3c..4c13c37a3d 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5004,6 +5004,30 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .no-wrapping-salad-item-container{ white-space: nowrap; } +.topic-landing-temporal{ + display: flex; + margin-top: 30px; +} +.topic-landing-calendar{ + +} +.topic-landing-parasha{ + +} +.topic-landing-seasonal{ + +} +.topic-landing-parasha .read-portion-button{ + margin-top: 30px; +} +.topic-landing-temporal > .topic-landing-parasha { + border-right: 1px solid #ccc; + padding-inline-end: 67px; +} +.topic-landing-temporal > .topic-landing-seasonal { + padding-inline-start: 67px; +} + .topic-landing-calendar .calendar-header{ color: var(--Dark-Grey, #666); @@ -5015,7 +5039,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus line-height: normal; } -.topic-landing-calendar .learn-more-prompt{ +.topic-landing-temporal .learn-more-prompt{ font-size: 14px; line-height: 18px; color: #666; @@ -5023,6 +5047,9 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus --english-font: var(--english-sans-serif-font-family); --hebrew-font: var(--hebrew-sans-serif-font-family); } +.topic-landing-temporal .parashah-link{ + margin-top: 47px; +} .readerNavMenu .sheet { display: flex; diff --git a/static/js/TopicLandingPage/TopicLandingParsha.jsx b/static/js/TopicLandingPage/TopicLandingParsha.jsx index b1b821e03a..6ab11f3b3c 100644 --- a/static/js/TopicLandingPage/TopicLandingParsha.jsx +++ b/static/js/TopicLandingPage/TopicLandingParsha.jsx @@ -29,6 +29,7 @@ export const TopicLandingParasha = () => { ) return ( +
{
{learnMorePrompt}
+
-
+
+
{readPortionButton}
+
); }; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index f0649a26e3..d594678518 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -1,8 +1,6 @@ import React from 'react'; import {TopicLandingCalendar} from "./TopicLandingCalendar"; import {useState, useEffect} from "react"; -import {ParashahLink} from "../NavSidebar"; -import {InterfaceText} from "../Misc"; import Sefaria from "../sefaria/sefaria"; @@ -18,11 +16,13 @@ export const TopicLandingSeasonal = () => { return ( +
+
); }; \ No newline at end of file diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index c0f90f1106..f4fc232fea 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -21,9 +21,10 @@ export const TopicsLandingPage = ({openTopic}) => {
+
- +
From 28ab9cdd0c4d67e6cf5adee67db4b55a95926c94 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:41:38 +0200 Subject: [PATCH 086/265] feat(Topic Landing Temporal): added element to temporal components --- static/css/s2.css | 10 +++++++-- .../TopicLandingPage/TopicLandingParsha.jsx | 3 +++ .../TopicLandingPage/TopicLandingSeasonal.jsx | 22 ++++++++++++------- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 4c13c37a3d..a01072a75f 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5008,14 +5008,20 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus display: flex; margin-top: 30px; } -.topic-landing-calendar{ -} .topic-landing-parasha{ } .topic-landing-seasonal{ +} +.topic-landing-parasha .browse-all-parashot-prompt{ + color: var(--Commentary-Blue, #4B71B7); + margin-top: 14px +} +.topic-landing-parasha .browse-all-parashot-prompt span{ + font-family: Roboto; + font-size: 14px; } .topic-landing-parasha .read-portion-button{ margin-top: 30px; diff --git a/static/js/TopicLandingPage/TopicLandingParsha.jsx b/static/js/TopicLandingPage/TopicLandingParsha.jsx index 6ab11f3b3c..d299c40d8c 100644 --- a/static/js/TopicLandingPage/TopicLandingParsha.jsx +++ b/static/js/TopicLandingPage/TopicLandingParsha.jsx @@ -45,6 +45,9 @@ export const TopicLandingParasha = () => {
{readPortionButton}
+
+ +
); diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index d594678518..816f2048d9 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -12,17 +12,23 @@ export const TopicLandingSeasonal = () => { }, []); const title = seasonal.topic?.primaryTitle; const description = seasonal.topic?.description - const link = `/topics/${seasonal.slug}` - + const link = `/topics/${seasonal.slug}`; + const learnMorePrompt = ( + + {`Learn More on ${title?.en}>`} + ) return (
- + +
+ {learnMorePrompt} +
); }; \ No newline at end of file From 5655ba6e3b81b9f93b26518c21aabdb3e4eb5a86 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:37:30 +0200 Subject: [PATCH 087/265] feat(Topic Landing Temporal): get display date and message from server and render them --- django_topics/models/seasonal_topic.py | 6 ++++ reader/views.py | 11 +++++++- static/css/s2.css | 22 ++++++++++++++- .../TopicLandingPage/TopicLandingSeasonal.jsx | 28 +++++++++++++++---- 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/django_topics/models/seasonal_topic.py b/django_topics/models/seasonal_topic.py index 090319e96b..3457ae8b62 100644 --- a/django_topics/models/seasonal_topic.py +++ b/django_topics/models/seasonal_topic.py @@ -78,6 +78,12 @@ def clean(self): def __str__(self): return f"{self.topic.slug} ({self.start_date})" + def get_display_start_date(self, diaspora=True): + return self.display_start_date_diaspora if diaspora else self.display_start_date_israel + + def get_display_end_date(self, diaspora=True): + return self.display_end_date_diaspora if diaspora else self.display_end_date_israel + class SeasonalTopicEnglish(SeasonalTopic): class Meta: diff --git a/reader/views.py b/reader/views.py index 2d6f05d136..8329231d14 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3287,11 +3287,20 @@ def seasonal_topic_api(request): lang = request.GET.get("lang") cb = request.GET.get("callback", None) + diaspora = request.GET.get("diaspora", False) + stopic = SeasonalTopic.objects.get_seasonal_topic(lang) if not stopic: return jsonResponse({'error': f'No seasonal topic found for lang "{lang}"'}, status=404) mongo_topic = Topic.init(stopic.topic.slug) - response = {'topic': mongo_topic.contents(), 'date': stopic.start_date.isoformat()} + mongo_secondary_topic = Topic.init(stopic.secondary_topic.slug) + response = {'topic': mongo_topic.contents(), + 'secondary_topic': mongo_secondary_topic.contents(), + 'display_start_date': stopic.get_display_start_date(diaspora).isoformat(), + 'display_end_date': stopic.get_display_end_date(diaspora).isoformat(), + 'display_date_prefix': stopic.display_date_prefix, + 'display_date_suffix': stopic.display_date_suffix, + } return jsonResponse(response, callback=cb) diff --git a/static/css/s2.css b/static/css/s2.css index a01072a75f..b74bf451eb 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5056,7 +5056,27 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-landing-temporal .parashah-link{ margin-top: 47px; } - +.topic-landing-temporal .display-date-message{ + color: var(--Darkest-Grey, #333); + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: 18px; + margin-top: 55px; +} +.topic-landing-temporal .display-date-message span { + font-family: Roboto; +} +.topic-landing-temporal .display-date{ + font-size: 14px; + font-style: normal; + font-weight: 400; + font-family: Roboto; + margin-top: 19px; +} +.topic-landing-temporal .display-date span{ + font-family: 'Roboto'; +} .readerNavMenu .sheet { display: flex; justify-content: space-between; diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index 816f2048d9..69495c2ea7 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -2,23 +2,35 @@ import React from 'react'; import {TopicLandingCalendar} from "./TopicLandingCalendar"; import {useState, useEffect} from "react"; import Sefaria from "../sefaria/sefaria"; +import {InterfaceText} from "../Misc"; export const TopicLandingSeasonal = () => { - const [seasonal, setSeasonal] = useState({}); + const [seasonal, setSeasonal] = useState(null); useEffect(() => { Sefaria.getSeasonalTopic().then(setSeasonal); }, []); - const title = seasonal.topic?.primaryTitle; - const description = seasonal.topic?.description - const link = `/topics/${seasonal.slug}`; + const title = seasonal?.topic?.primaryTitle; + const description = seasonal?.topic?.description; + const link = `/topics/${seasonal?.topic?.slug}`; const learnMorePrompt = ( {`Learn More on ${title?.en}>`} ) + const displayStartDate = seasonal ? new Date(seasonal.display_start_date): null; + const displayEndDate = seasonal ? new Date(seasonal.display_end_date): null; + const displayDatePrefix = seasonal ? seasonal.display_date_prefix: null; + const displayDateSuffix = seasonal ? seasonal.display_date_suffix: null; + const displayDateMessage = `${displayDatePrefix ?? ''} ${title?.en} ${displayDateSuffix ?? ''}`; - return ( + const fmt = new Intl.DateTimeFormat("en", { + month: "long", + day: "numeric", + }); + const formattedDate = seasonal? fmt.formatRange(displayStartDate, displayEndDate) : null; + + return ( seasonal &&
{
{learnMorePrompt}
+
+ +
+
+ +
); }; \ No newline at end of file From e579f0b3c8b8fe3822e0ab64d2c283ae8b261bc8 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Wed, 11 Dec 2024 18:29:40 +0200 Subject: [PATCH 088/265] chore(Topic Landing Temporal): fix merge conflicts --- static/js/Header.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index cd4411610a..5a4d8f53f4 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -8,7 +8,6 @@ import Sefaria from './sefaria/sefaria'; import { SearchButton, GlobalWarningMessage, - ProfilePic, InterfaceLanguageMenu, InterfaceText, LanguageToggleButton, From 86675cb4f0e9e5de3e04a25360eb5d4b192f8da1 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:25:49 +0200 Subject: [PATCH 089/265] feat(Topic Landing Temporal): fix turn display date message into a link to topic --- static/js/TopicLandingPage/TopicLandingSeasonal.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index 69495c2ea7..617d2445f3 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -23,6 +23,7 @@ export const TopicLandingSeasonal = () => { const displayDatePrefix = seasonal ? seasonal.display_date_prefix: null; const displayDateSuffix = seasonal ? seasonal.display_date_suffix: null; const displayDateMessage = `${displayDatePrefix ?? ''} ${title?.en} ${displayDateSuffix ?? ''}`; + const secondaryTopicSlug = seasonal ? seasonal.secondary_topic.slug: null; const fmt = new Intl.DateTimeFormat("en", { month: "long", @@ -42,7 +43,7 @@ export const TopicLandingSeasonal = () => { {learnMorePrompt}
- +
From 8bff332cf7e2fdc64b82e3ac5a8e1fc00256f508 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:56:36 +0200 Subject: [PATCH 090/265] refactor(Topic Landing Temporal): export date retrieval into a custom hook --- .../TopicLandingPage/TopicLandingSeasonal.jsx | 84 +++++++++++++------ 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index 617d2445f3..3ae35a7a22 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -4,34 +4,62 @@ import {useState, useEffect} from "react"; import Sefaria from "../sefaria/sefaria"; import {InterfaceText} from "../Misc"; +const useSeasonalTopic = () => { + const [seasonal, setSeasonal] = useState(null); + + useEffect(() => { + Sefaria.getSeasonalTopic().then(setSeasonal); + }, []); + + if (!seasonal) return { isLoading: true }; + + const title = seasonal.topic?.primaryTitle; + const description = seasonal.topic?.description; + const link = `/topics/${seasonal.topic?.slug}`; + + const displayStartDate = new Date(seasonal.display_start_date); + const displayEndDate = new Date(seasonal.display_end_date); + const displayDatePrefix = seasonal.display_date_prefix || ''; + const displayDateSuffix = seasonal.display_date_suffix || ''; + const displayDateMessage = `${displayDatePrefix} ${title?.en} ${displayDateSuffix}`; + const secondaryTopicSlug = seasonal.secondary_topic?.slug || null; + + + return { + title, + description, + link, + displayStartDate, + displayEndDate, + displayDateMessage, + secondaryTopicSlug, + isLoading: false, + }; +}; export const TopicLandingSeasonal = () => { - const [seasonal, setSeasonal] = useState(null); - - useEffect(() => { - Sefaria.getSeasonalTopic().then(setSeasonal); - }, []); - const title = seasonal?.topic?.primaryTitle; - const description = seasonal?.topic?.description; - const link = `/topics/${seasonal?.topic?.slug}`; - const learnMorePrompt = ( - - {`Learn More on ${title?.en}>`} - ) - const displayStartDate = seasonal ? new Date(seasonal.display_start_date): null; - const displayEndDate = seasonal ? new Date(seasonal.display_end_date): null; - const displayDatePrefix = seasonal ? seasonal.display_date_prefix: null; - const displayDateSuffix = seasonal ? seasonal.display_date_suffix: null; - const displayDateMessage = `${displayDatePrefix ?? ''} ${title?.en} ${displayDateSuffix ?? ''}`; - const secondaryTopicSlug = seasonal ? seasonal.secondary_topic.slug: null; - - const fmt = new Intl.DateTimeFormat("en", { - month: "long", - day: "numeric", - }); - const formattedDate = seasonal? fmt.formatRange(displayStartDate, displayEndDate) : null; - - return ( seasonal && + + const { + title, + description, + link, + displayDateMessage, + secondaryTopicSlug, + displayStartDate, + displayEndDate, + isLoading, + } = useSeasonalTopic(); + if (isLoading) return null; + const fmt = new Intl.DateTimeFormat("en", { + month: "long", + day: "numeric", + }); + + const formattedDate = fmt.formatRange(displayStartDate, displayEndDate); + const learnMorePrompt = `Learn More on ${title?.en}>`; + + + return (
{ link={link} />
- {learnMorePrompt} + + {learnMorePrompt} +
From eae74dfbd682d7b4476378467e14e6a5f132b9cf Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:40:47 +0200 Subject: [PATCH 091/265] feat(Topic Landing Temporal): Hebrew localization --- reader/views.py | 8 ++--- static/css/s2.css | 4 ++- .../TopicLandingPage/TopicLandingCalendar.jsx | 2 +- .../TopicLandingPage/TopicLandingParsha.jsx | 25 +++++++--------- .../TopicLandingPage/TopicLandingSeasonal.jsx | 30 ++++++++++++------- static/js/sefaria/strings.js | 6 ++++ 6 files changed, 45 insertions(+), 30 deletions(-) diff --git a/reader/views.py b/reader/views.py index 8329231d14..e5512406a1 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3293,11 +3293,11 @@ def seasonal_topic_api(request): if not stopic: return jsonResponse({'error': f'No seasonal topic found for lang "{lang}"'}, status=404) mongo_topic = Topic.init(stopic.topic.slug) - mongo_secondary_topic = Topic.init(stopic.secondary_topic.slug) + mongo_secondary_topic = Topic.init(stopic.secondary_topic.slug) if stopic.secondary_topic else None response = {'topic': mongo_topic.contents(), - 'secondary_topic': mongo_secondary_topic.contents(), - 'display_start_date': stopic.get_display_start_date(diaspora).isoformat(), - 'display_end_date': stopic.get_display_end_date(diaspora).isoformat(), + 'secondary_topic': mongo_secondary_topic.contents() if mongo_secondary_topic else None, + 'display_start_date': stopic.get_display_start_date(diaspora).isoformat() if mongo_secondary_topic else None, + 'display_end_date': stopic.get_display_end_date(diaspora).isoformat() if mongo_secondary_topic else None, 'display_date_prefix': stopic.display_date_prefix, 'display_date_suffix': stopic.display_date_suffix, } diff --git a/static/css/s2.css b/static/css/s2.css index b74bf451eb..47b0a47d2d 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5027,11 +5027,13 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus margin-top: 30px; } .topic-landing-temporal > .topic-landing-parasha { - border-right: 1px solid #ccc; + border-inline-end: 1px solid #ccc; padding-inline-end: 67px; + flex: 1; } .topic-landing-temporal > .topic-landing-seasonal { padding-inline-start: 67px; + flex: 1; } diff --git a/static/js/TopicLandingPage/TopicLandingCalendar.jsx b/static/js/TopicLandingPage/TopicLandingCalendar.jsx index 8be1b4b166..a09283eb3d 100644 --- a/static/js/TopicLandingPage/TopicLandingCalendar.jsx +++ b/static/js/TopicLandingPage/TopicLandingCalendar.jsx @@ -7,7 +7,7 @@ export const TopicLandingCalendar = ({ header, title, description, link, childre return (
- + {header}
{ const parashahDesc = parashah.description; const parashahTopicLink = `topics/${parashah?.topic}`; const parashahRefLink = `/${parashah?.url}`; - const learnMorePrompt = ( - - {`Learn More on Parashat ${parashahTitle?.en}>`} - - ); - const readPortionButton = ( - - - - ) + const learnMorePrompt = {en: `Learn More on Parashat ${parashahTitle?.en}>`, + he:`${Sefaria._("Learn More on Parashat")} ${parashahTitle?.he}>`} + return (
This Week’s Torah Portion} title={parashahTitle} description={parashahDesc} link={parashahTopicLink} >
- {learnMorePrompt} + + +
- {readPortionButton} + + Read the Portion +
diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index 3ae35a7a22..2c420b6f3e 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -21,7 +21,9 @@ const useSeasonalTopic = () => { const displayEndDate = new Date(seasonal.display_end_date); const displayDatePrefix = seasonal.display_date_prefix || ''; const displayDateSuffix = seasonal.display_date_suffix || ''; - const displayDateMessage = `${displayDatePrefix} ${title?.en} ${displayDateSuffix}`; + const secondaryTopicTitle = seasonal.secondary_topic?.primaryTitle || null; + const displayDateMessageEn = `${displayDatePrefix ?? ''} ${secondaryTopicTitle?.en ?? ''} ${displayDateSuffix ?? ''}`; + const displayDateMessageHe = `${displayDatePrefix ?? ''} ${secondaryTopicTitle?.he ?? ''} ${displayDateSuffix ?? ''}`; const secondaryTopicSlug = seasonal.secondary_topic?.slug || null; @@ -31,7 +33,8 @@ const useSeasonalTopic = () => { link, displayStartDate, displayEndDate, - displayDateMessage, + displayDateMessageEn, + displayDateMessageHe, secondaryTopicSlug, isLoading: false, }; @@ -43,40 +46,47 @@ export const TopicLandingSeasonal = () => { title, description, link, - displayDateMessage, + displayDateMessageEn, + displayDateMessageHe, secondaryTopicSlug, displayStartDate, displayEndDate, isLoading, } = useSeasonalTopic(); if (isLoading) return null; - const fmt = new Intl.DateTimeFormat("en", { + const enDateFormat = new Intl.DateTimeFormat("en", { + month: "long", + day: "numeric", + }); + const heDateFormat = new Intl.DateTimeFormat("he", { month: "long", day: "numeric", }); - const formattedDate = fmt.formatRange(displayStartDate, displayEndDate); - const learnMorePrompt = `Learn More on ${title?.en}>`; + const formattedDateEn = secondaryTopicSlug && enDateFormat.formatRange(displayStartDate, displayEndDate); + const formattedDateHe = secondaryTopicSlug && heDateFormat.formatRange(displayStartDate, displayEndDate); + const learnMorePrompt = {en: `Learn More on ${title?.en}>`, + he:`${Sefaria._("Learn More on")} ${title?.he}>`} return (
Upcoming Holiday} title={title} description={description} link={link} />
- +
- +
); diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index 9b607b81f5..a3ebef3379 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -548,6 +548,12 @@ const Strings = { "Jewish Encyclopedia": "האנציקלופדיה היהודית", "National Library of Israel": "הספרייה הלאומית", "Works on Sefaria": "חיבורים וכתבים בספריא", + "Learn More on Parashat": "לקריאה נוספת על פרשת", + "This Week’s Torah Portion": "פרשת השבוע", + "Learn More on": "לקריאה נוספת על", + "Read the Portion": "לקריאת הפרשה", + "Browse all Parshayot": "לצפייה בכל הפרשות", + "Upcoming Holiday": "החג הבא", //Module Names "Download Text": "הורדת טקסט", From c5378fced1ca75dc767bca9db3c1ede8168a8c1c Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:14:22 +0200 Subject: [PATCH 092/265] feat(Topic Landing Temporal): replace > with caret --- static/js/TopicLandingPage/TopicLandingParsha.jsx | 4 ++-- static/js/TopicLandingPage/TopicLandingSeasonal.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingParsha.jsx b/static/js/TopicLandingPage/TopicLandingParsha.jsx index 84e406ffe9..fd8020a665 100644 --- a/static/js/TopicLandingPage/TopicLandingParsha.jsx +++ b/static/js/TopicLandingPage/TopicLandingParsha.jsx @@ -17,8 +17,8 @@ export const TopicLandingParasha = () => { const parashahDesc = parashah.description; const parashahTopicLink = `topics/${parashah?.topic}`; const parashahRefLink = `/${parashah?.url}`; - const learnMorePrompt = {en: `Learn More on Parashat ${parashahTitle?.en}>`, - he:`${Sefaria._("Learn More on Parashat")} ${parashahTitle?.he}>`} + const learnMorePrompt = {en: `Learn More on Parashat ${parashahTitle?.en}›`, + he:`${Sefaria._("Learn More on Parashat")} ${parashahTitle?.he}›`} return ( diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index 2c420b6f3e..13c77d59a6 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -65,8 +65,8 @@ export const TopicLandingSeasonal = () => { const formattedDateEn = secondaryTopicSlug && enDateFormat.formatRange(displayStartDate, displayEndDate); const formattedDateHe = secondaryTopicSlug && heDateFormat.formatRange(displayStartDate, displayEndDate); - const learnMorePrompt = {en: `Learn More on ${title?.en}>`, - he:`${Sefaria._("Learn More on")} ${title?.he}>`} + const learnMorePrompt = {en: `Learn More on ${title?.en}›`, + he:`${Sefaria._("Learn More on")} ${title?.he}›`} return ( From 8a229fd5e472049048d46cf948d11626ed0dc7c7 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:22:59 +0200 Subject: [PATCH 093/265] feat(Topic Landing Temporal): space before caret --- static/js/TopicLandingPage/TopicLandingParsha.jsx | 4 ++-- static/js/TopicLandingPage/TopicLandingSeasonal.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingParsha.jsx b/static/js/TopicLandingPage/TopicLandingParsha.jsx index fd8020a665..3aeed901ff 100644 --- a/static/js/TopicLandingPage/TopicLandingParsha.jsx +++ b/static/js/TopicLandingPage/TopicLandingParsha.jsx @@ -17,8 +17,8 @@ export const TopicLandingParasha = () => { const parashahDesc = parashah.description; const parashahTopicLink = `topics/${parashah?.topic}`; const parashahRefLink = `/${parashah?.url}`; - const learnMorePrompt = {en: `Learn More on Parashat ${parashahTitle?.en}›`, - he:`${Sefaria._("Learn More on Parashat")} ${parashahTitle?.he}›`} + const learnMorePrompt = {en: `Learn More on Parashat ${parashahTitle?.en} ›`, + he:`${Sefaria._("Learn More on Parashat")} ${parashahTitle?.he} ›`} return ( diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index 13c77d59a6..31918cefa2 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -65,8 +65,8 @@ export const TopicLandingSeasonal = () => { const formattedDateEn = secondaryTopicSlug && enDateFormat.formatRange(displayStartDate, displayEndDate); const formattedDateHe = secondaryTopicSlug && heDateFormat.formatRange(displayStartDate, displayEndDate); - const learnMorePrompt = {en: `Learn More on ${title?.en}›`, - he:`${Sefaria._("Learn More on")} ${title?.he}›`} + const learnMorePrompt = {en: `Learn More on ${title?.en} ›`, + he:`${Sefaria._("Learn More on")} ${title?.he} ›`} return ( From fd014c2d36ba57868398aa6e8b0c9dc2e7d93e3f Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:56:00 +0200 Subject: [PATCH 094/265] feat(Topic Landing Temporal): only topic title is a-tagged --- static/css/s2.css | 3 ++ .../TopicLandingPage/TopicLandingSeasonal.jsx | 29 ++++++++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 47b0a47d2d..baa01d2548 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5069,6 +5069,9 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-landing-temporal .display-date-message span { font-family: Roboto; } +.topic-landing-temporal .display-date-message a { + font-family: Roboto; +} .topic-landing-temporal .display-date{ font-size: 14px; font-style: normal; diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index 31918cefa2..7d403f500a 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -4,6 +4,16 @@ import {useState, useEffect} from "react"; import Sefaria from "../sefaria/sefaria"; import {InterfaceText} from "../Misc"; +const createDisplayDateMessage =(displayDatePrefix, link, secondaryTopicTitleString, displayDateSuffix)=> { + return ( + <> + {displayDatePrefix ?? ''}{' '} + {secondaryTopicTitleString ? {secondaryTopicTitleString} : ''}{' '} + {displayDateSuffix ?? ''} + + ); +} + const useSeasonalTopic = () => { const [seasonal, setSeasonal] = useState(null); @@ -22,8 +32,6 @@ const useSeasonalTopic = () => { const displayDatePrefix = seasonal.display_date_prefix || ''; const displayDateSuffix = seasonal.display_date_suffix || ''; const secondaryTopicTitle = seasonal.secondary_topic?.primaryTitle || null; - const displayDateMessageEn = `${displayDatePrefix ?? ''} ${secondaryTopicTitle?.en ?? ''} ${displayDateSuffix ?? ''}`; - const displayDateMessageHe = `${displayDatePrefix ?? ''} ${secondaryTopicTitle?.he ?? ''} ${displayDateSuffix ?? ''}`; const secondaryTopicSlug = seasonal.secondary_topic?.slug || null; @@ -33,8 +41,9 @@ const useSeasonalTopic = () => { link, displayStartDate, displayEndDate, - displayDateMessageEn, - displayDateMessageHe, + displayDateSuffix, + displayDatePrefix, + secondaryTopicTitle, secondaryTopicSlug, isLoading: false, }; @@ -46,19 +55,19 @@ export const TopicLandingSeasonal = () => { title, description, link, - displayDateMessageEn, - displayDateMessageHe, secondaryTopicSlug, displayStartDate, displayEndDate, isLoading, } = useSeasonalTopic(); if (isLoading) return null; - const enDateFormat = new Intl.DateTimeFormat("en", { + const displayDateMessageEn = createDisplayDateMessage(displayDatePrefix, link, secondaryTopicTitle?.en, displayDateSuffix); + const displayDateMessageHe = createDisplayDateMessage(displayDatePrefix, link, secondaryTopicTitle?.he, displayDateSuffix); + const enDateFormat = new Intl.DateTimeFormat("en", { month: "long", day: "numeric", - }); - const heDateFormat = new Intl.DateTimeFormat("he", { + }); + const heDateFormat = new Intl.DateTimeFormat("he", { month: "long", day: "numeric", }); @@ -83,7 +92,7 @@ export const TopicLandingSeasonal = () => {
- +
From 46ea3106288d0d685fcb6392f3d430a69dd44fc6 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:58:56 +0200 Subject: [PATCH 095/265] fix(Topic Landing Temporal): only return all necessary variables in hook --- static/js/TopicLandingPage/TopicLandingSeasonal.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index 7d403f500a..d2ba238a49 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -55,9 +55,12 @@ export const TopicLandingSeasonal = () => { title, description, link, - secondaryTopicSlug, displayStartDate, displayEndDate, + displayDateSuffix, + displayDatePrefix, + secondaryTopicTitle, + secondaryTopicSlug, isLoading, } = useSeasonalTopic(); if (isLoading) return null; From e77010b2941f7cb922c50cf54880fdba0177828b Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Sun, 15 Dec 2024 10:10:55 +0200 Subject: [PATCH 096/265] refactor(topics): rename all mentions of TopicOfTheDay to FeaturedTopic --- django_topics/admin.py | 12 +-- .../migrations/0007_auto_20241127_0034.py | 53 ------------- .../migrations/0007_auto_20241215_0408.py | 76 +++++++++++++++++++ django_topics/models/__init__.py | 2 +- ...{topic_of_the_day.py => featured_topic.py} | 28 +++---- .../models/tests/featured_topic_test.py | 47 ++++++++++++ .../models/tests/topic_of_the_day_test.py | 47 ------------ reader/views.py | 14 ++-- sefaria/urls.py | 2 +- static/css/s2.css | 20 ++--- .../{TopicOfTheDay.jsx => FeaturedTopic.jsx} | 12 +-- .../js/TopicLandingPage/TopicsLandingPage.jsx | 4 +- static/js/sefaria/sefaria.js | 8 +- 13 files changed, 174 insertions(+), 151 deletions(-) delete mode 100644 django_topics/migrations/0007_auto_20241127_0034.py create mode 100644 django_topics/migrations/0007_auto_20241215_0408.py rename django_topics/models/{topic_of_the_day.py => featured_topic.py} (57%) create mode 100644 django_topics/models/tests/featured_topic_test.py delete mode 100644 django_topics/models/tests/topic_of_the_day_test.py rename static/js/TopicLandingPage/{TopicOfTheDay.jsx => FeaturedTopic.jsx} (75%) diff --git a/django_topics/admin.py b/django_topics/admin.py index f9ce67aadc..73dabe9828 100644 --- a/django_topics/admin.py +++ b/django_topics/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin, messages from django.utils.html import format_html -from django_topics.models import Topic, TopicPool, TopicOfTheDayEnglish, TopicOfTheDayHebrew, SeasonalTopicEnglish, SeasonalTopicHebrew +from django_topics.models import Topic, TopicPool, FeaturedTopicEnglish, FeaturedTopicHebrew, SeasonalTopicEnglish, SeasonalTopicHebrew from django_topics.models.pool import PoolType @@ -102,7 +102,7 @@ def sefaria_link(self, obj): sefaria_link.short_description = "Sefaria Link" -class TopicOfTheDayAdmin(admin.ModelAdmin): +class FeaturedTopicAdmin(admin.ModelAdmin): exclude = ("lang",) # not for manual editing list_display = ('start_date', 'topic') list_filter = ('start_date',) @@ -123,16 +123,16 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs): return super().formfield_for_foreignkey(db_field, request, **kwargs) -@admin.register(TopicOfTheDayEnglish) -class TopicOfTheDayAdminEnglish(TopicOfTheDayAdmin): +@admin.register(FeaturedTopicEnglish) +class FeaturedTopicAdminEnglish(FeaturedTopicAdmin): def get_queryset(self, request): qs = super().get_queryset(request) return qs.filter(lang="en") -@admin.register(TopicOfTheDayHebrew) -class TopicOfTheDayAdminHebrew(TopicOfTheDayAdmin): +@admin.register(FeaturedTopicHebrew) +class FeaturedTopicAdminHebrew(FeaturedTopicAdmin): def get_queryset(self, request): qs = super().get_queryset(request) diff --git a/django_topics/migrations/0007_auto_20241127_0034.py b/django_topics/migrations/0007_auto_20241127_0034.py deleted file mode 100644 index f375c1622f..0000000000 --- a/django_topics/migrations/0007_auto_20241127_0034.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.29 on 2024-11-27 04:34 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('django_topics', '0006_seasonaltopicenglish_seasonaltopichebrew'), - ] - - operations = [ - migrations.CreateModel( - name='TopicOfTheDayEnglish', - fields=[ - ], - options={ - 'verbose_name': 'Landing Page - Topic of the Day (EN)', - 'verbose_name_plural': 'Landing Page - Topic of the Day (EN)', - 'proxy': True, - 'indexes': [], - }, - bases=('django_topics.topicoftheday',), - ), - migrations.CreateModel( - name='TopicOfTheDayHebrew', - fields=[ - ], - options={ - 'verbose_name': 'Landing Page - Topic of the Day (HE)', - 'verbose_name_plural': 'Landing Page - Topic of the Day (HE)', - 'proxy': True, - 'indexes': [], - }, - bases=('django_topics.topicoftheday',), - ), - migrations.AlterModelOptions( - name='seasonaltopicenglish', - options={'verbose_name': 'Landing Page - Calendar (EN)', 'verbose_name_plural': 'Landing Page - Calendar (EN)'}, - ), - migrations.AlterModelOptions( - name='seasonaltopichebrew', - options={'verbose_name': 'Landing Page - Calendar (HE)', 'verbose_name_plural': 'Landing Page - Calendar (HE)'}, - ), - migrations.AddField( - model_name='topicoftheday', - name='lang', - field=models.CharField(choices=[('en', 'English'), ('he', 'Hebrew')], default='en', max_length=2), - preserve_default=False, - ), - ] diff --git a/django_topics/migrations/0007_auto_20241215_0408.py b/django_topics/migrations/0007_auto_20241215_0408.py new file mode 100644 index 0000000000..7644cfefbc --- /dev/null +++ b/django_topics/migrations/0007_auto_20241215_0408.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-12-15 08:08 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_topics', '0006_seasonaltopicenglish_seasonaltopichebrew'), + ] + + operations = [ + migrations.CreateModel( + name='FeaturedTopic', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start_date', models.DateField()), + ('lang', models.CharField(choices=[('en', 'English'), ('he', 'Hebrew')], max_length=2)), + ('topic', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='featured_topic', to='django_topics.Topic')), + ], + options={ + 'verbose_name': 'Landing Page - Featured Topic', + 'verbose_name_plural': 'Landing Page - Featured Topic', + }, + ), + migrations.AlterUniqueTogether( + name='topicoftheday', + unique_together=set([]), + ), + migrations.RemoveField( + model_name='topicoftheday', + name='topic', + ), + migrations.AlterModelOptions( + name='seasonaltopicenglish', + options={'verbose_name': 'Landing Page - Calendar (EN)', 'verbose_name_plural': 'Landing Page - Calendar (EN)'}, + ), + migrations.AlterModelOptions( + name='seasonaltopichebrew', + options={'verbose_name': 'Landing Page - Calendar (HE)', 'verbose_name_plural': 'Landing Page - Calendar (HE)'}, + ), + migrations.DeleteModel( + name='TopicOfTheDay', + ), + migrations.CreateModel( + name='FeaturedTopicEnglish', + fields=[ + ], + options={ + 'verbose_name': 'Landing Page - Featured Topic (EN)', + 'verbose_name_plural': 'Landing Page - Featured Topic (EN)', + 'proxy': True, + 'indexes': [], + }, + bases=('django_topics.featuredtopic',), + ), + migrations.CreateModel( + name='FeaturedTopicHebrew', + fields=[ + ], + options={ + 'verbose_name': 'Landing Page - Featured Topic (HE)', + 'verbose_name_plural': 'Landing Page - Featured Topic (HE)', + 'proxy': True, + 'indexes': [], + }, + bases=('django_topics.featuredtopic',), + ), + migrations.AlterUniqueTogether( + name='featuredtopic', + unique_together=set([('topic', 'start_date')]), + ), + ] diff --git a/django_topics/models/__init__.py b/django_topics/models/__init__.py index e05c9bccf9..682554f9bf 100644 --- a/django_topics/models/__init__.py +++ b/django_topics/models/__init__.py @@ -1,4 +1,4 @@ from .topic import Topic from .pool import TopicPool, PoolType -from .topic_of_the_day import TopicOfTheDay, TopicOfTheDayEnglish, TopicOfTheDayHebrew +from .featured_topic import FeaturedTopic, FeaturedTopicEnglish, FeaturedTopicHebrew from .seasonal_topic import SeasonalTopic, SeasonalTopicEnglish, SeasonalTopicHebrew diff --git a/django_topics/models/topic_of_the_day.py b/django_topics/models/featured_topic.py similarity index 57% rename from django_topics/models/topic_of_the_day.py rename to django_topics/models/featured_topic.py index 6670df0912..2cd7921df7 100644 --- a/django_topics/models/topic_of_the_day.py +++ b/django_topics/models/featured_topic.py @@ -4,11 +4,11 @@ from django_topics.models import Topic -class TopicOfTheDayManager(models.Manager): +class FeaturedTopicManager(models.Manager): - def get_topic_of_the_day(self, lang: str, date: datetime = None) -> 'TopicOfTheDay': + def get_featured_topic(self, lang: str, date: datetime = None) -> 'FeaturedTopic': """ - Return topic of day for given date or closest date that is less than or equal to given date + Return featured topic for given date or closest date that is less than or equal to given date @param lang: language code, "en" or "he" @param date: datetime object @return: @@ -21,41 +21,41 @@ def get_topic_of_the_day(self, lang: str, date: datetime = None) -> 'TopicOfTheD ) -class TopicOfTheDay(models.Model): +class FeaturedTopic(models.Model): topic = models.ForeignKey( Topic, on_delete=models.CASCADE, - related_name='topic_of_the_day' + related_name='featured_topic' ) start_date = models.DateField() lang = models.CharField(max_length=2, choices=[('en', 'English'), ('he', 'Hebrew')]) - objects = TopicOfTheDayManager() + objects = FeaturedTopicManager() class Meta: unique_together = ('topic', 'start_date') - verbose_name = "Landing Page - Topic of the Day" - verbose_name_plural = "Landing Page - Topic of the Day" + verbose_name = "Landing Page - Featured Topic" + verbose_name_plural = "Landing Page - Featured Topic" def __str__(self): return f"{self.topic.slug} ({self.start_date})" -class TopicOfTheDayEnglish(TopicOfTheDay): +class FeaturedTopicEnglish(FeaturedTopic): class Meta: proxy = True - verbose_name = "Landing Page - Topic of the Day (EN)" - verbose_name_plural = "Landing Page - Topic of the Day (EN)" + verbose_name = "Landing Page - Featured Topic (EN)" + verbose_name_plural = "Landing Page - Featured Topic (EN)" def save(self, *args, **kwargs): self.lang = "en" super().save(*args, **kwargs) -class TopicOfTheDayHebrew(TopicOfTheDay): +class FeaturedTopicHebrew(FeaturedTopic): class Meta: proxy = True - verbose_name = "Landing Page - Topic of the Day (HE)" - verbose_name_plural = "Landing Page - Topic of the Day (HE)" + verbose_name = "Landing Page - Featured Topic (HE)" + verbose_name_plural = "Landing Page - Featured Topic (HE)" def save(self, *args, **kwargs): self.lang = "he" diff --git a/django_topics/models/tests/featured_topic_test.py b/django_topics/models/tests/featured_topic_test.py new file mode 100644 index 0000000000..617f63bc3a --- /dev/null +++ b/django_topics/models/tests/featured_topic_test.py @@ -0,0 +1,47 @@ +import pytest +from datetime import date +from django_topics.models import FeaturedTopic, Topic + + +@pytest.fixture +def topic(db): + """Fixture to create a Topic instance.""" + return Topic.objects.create(slug="test-topic") + + +@pytest.fixture +def featured_topics(db, topic): + """Fixture to create FeaturedTopic instances.""" + topics = [ + FeaturedTopic.objects.create(topic=topic, start_date=date(2024, 11, 26), lang="en"), + FeaturedTopic.objects.create(topic=topic, start_date=date(2024, 11, 25), lang="en"), + FeaturedTopic.objects.create(topic=topic, start_date=date(2024, 11, 24), lang="en"), + ] + return topics + + +@pytest.mark.django_db +def test_get_featured_topic_with_exact_date_db(featured_topics): + """Test for exact match.""" + result = FeaturedTopic.objects.get_featured_topic(lang="en", date=date(2024, 11, 26)) + + assert result.start_date == date(2024, 11, 26) + assert result.lang == "en" + + +@pytest.mark.django_db +def test_get_featured_topic_with_closest_date_db(featured_topics): + """Test for the closest date less than or equal to the given date.""" + result = FeaturedTopic.objects.get_featured_topic(lang="en", date=date(2024, 11, 27)) + + assert result.start_date == date(2024, 11, 26) + assert result.lang == "en" + + +@pytest.mark.django_db +def test_get_featured_topic_with_no_matching_date_db(db, topic): + """Test when there is no matching date.""" + FeaturedTopic.objects.create(topic=topic, start_date=date(2024, 11, 20), lang="en") + result = FeaturedTopic.objects.get_featured_topic(lang="en", date=date(2024, 11, 19)) + + assert result is None diff --git a/django_topics/models/tests/topic_of_the_day_test.py b/django_topics/models/tests/topic_of_the_day_test.py deleted file mode 100644 index 2faa853cdd..0000000000 --- a/django_topics/models/tests/topic_of_the_day_test.py +++ /dev/null @@ -1,47 +0,0 @@ -import pytest -from datetime import date -from django_topics.models import TopicOfTheDay, Topic - - -@pytest.fixture -def topic(db): - """Fixture to create a Topic instance.""" - return Topic.objects.create(slug="test-topic") - - -@pytest.fixture -def topics_of_the_day(db, topic): - """Fixture to create TopicOfTheDay instances.""" - topics = [ - TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 26), lang="en"), - TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 25), lang="en"), - TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 24), lang="en"), - ] - return topics - - -@pytest.mark.django_db -def test_get_topic_of_the_day_with_exact_date_db(topics_of_the_day): - """Test for exact match.""" - result = TopicOfTheDay.objects.get_topic_of_the_day(lang="en", date=date(2024, 11, 26)) - - assert result.start_date == date(2024, 11, 26) - assert result.lang == "en" - - -@pytest.mark.django_db -def test_get_topic_of_the_day_with_closest_date_db(topics_of_the_day): - """Test for the closest date less than or equal to the given date.""" - result = TopicOfTheDay.objects.get_topic_of_the_day(lang="en", date=date(2024, 11, 27)) - - assert result.start_date == date(2024, 11, 26) - assert result.lang == "en" - - -@pytest.mark.django_db -def test_get_topic_of_the_day_with_no_matching_date_db(db, topic): - """Test when there is no matching date.""" - TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 20), lang="en") - result = TopicOfTheDay.objects.get_topic_of_the_day(lang="en", date=date(2024, 11, 19)) - - assert result is None diff --git a/reader/views.py b/reader/views.py index 5c74ad116f..750ecc1c4f 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3209,16 +3209,16 @@ def topic_pool_api(request, pool_name): @catch_error_as_json -def topic_of_the_day_api(request): - from django_topics.models import TopicOfTheDay +def featured_topic_api(request): + from django_topics.models import FeaturedTopic lang = request.GET.get("lang") cb = request.GET.get("callback", None) - tod = TopicOfTheDay.objects.get_topic_of_the_day(lang) - if not tod: - return jsonResponse({'error': f'No topic of the day found for lang "{lang}"'}, status=404) - mongo_topic = Topic.init(tod.topic.slug) - response = {'topic': mongo_topic.contents(), 'date': tod.start_date.isoformat()} + featured_topic = FeaturedTopic.objects.get_featured_topic(lang) + if not featured_topic: + return jsonResponse({'error': f'No featured topic found for lang "{lang}"'}, status=404) + mongo_topic = Topic.init(featured_topic.topic.slug) + response = {'topic': mongo_topic.contents(), 'date': featured_topic.start_date.isoformat()} return jsonResponse(response, callback=cb) diff --git a/sefaria/urls.py b/sefaria/urls.py index 893c5fe607..b71d7bfe79 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -264,7 +264,7 @@ url(r'^api/topics/generate-prompts/(?P.+)$', reader_views.generate_topic_prompts_api), url(r'^api/topics-graph/(?P.+)$', reader_views.topic_graph_api), url(r'^api/topics/pools/(?P.+)$', reader_views.topic_pool_api), - url(r'^_api/topics/topic-of-the-day/?$', reader_views.topic_of_the_day_api), + url(r'^_api/topics/featured-topic/?$', reader_views.featured_topic_api), url(r'^api/ref-topic-links/bulk$', reader_views.topic_ref_bulk_api), url(r'^api/ref-topic-links/(?P.+)$', reader_views.topic_ref_api), url(r'^api/v2/topics/(?P.+)$', reader_views.topics_api, {'v2': True}), diff --git a/static/css/s2.css b/static/css/s2.css index 1bd94035ce..0e34755cf6 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4926,40 +4926,40 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus top: 6px; padding-inline-start: 11px; } -.topicOfTheDay { +.featuredTopic { display: flex; flex-direction: column; margin-top: 68px; } -.topicOfTheDayContent { +.featuredTopicContent { display: flex; flex-direction: row; } -.topicOfTheDayContent img { +.featuredTopicContent img { width: 239px; } -.topicOfTheDayContent h3 { +.featuredTopicContent h3 { font-size: var(--serif-h3-font-size); text-transform: none; } -.topicOfTheDayText .int-en, -.topicOfTheDayText .int-he { +.featuredTopicText .int-en, +.featuredTopicText .int-he { display: flex; flex-direction: column; color: var(--dark-grey); margin-inline-start: 30px; } -.topicOfTheDayText h3 { +.featuedTopicText h3 { text-transform: none; margin: 0 0 15px 0; } -.topicOfTheDayGoToLink { +.featuedTopicGoToLink { margin-top: 25px; } -.topicOfTheDayText .topicDescription { +.featuredTopicText .topicDescription { font-size: var(--sans-serif-small-font-size); } -.topicOfTheDay h1 { +.featuredTopic h1 { font-size: var(--sans-serif-h2-font-size); border-bottom: 1px solid #ccc; width: fit-content; diff --git a/static/js/TopicLandingPage/TopicOfTheDay.jsx b/static/js/TopicLandingPage/FeaturedTopic.jsx similarity index 75% rename from static/js/TopicLandingPage/TopicOfTheDay.jsx rename to static/js/TopicLandingPage/FeaturedTopic.jsx index e37ce771f4..bc8dca23e0 100644 --- a/static/js/TopicLandingPage/TopicOfTheDay.jsx +++ b/static/js/TopicLandingPage/FeaturedTopic.jsx @@ -3,23 +3,23 @@ import Sefaria from "../sefaria/sefaria"; import {ImageWithAltText, InterfaceText, SimpleLinkedBlock} from "../Misc"; -export const TopicOfTheDay = () => { +export const FeaturedTopic = () => { let [topic, setTopic] = useState(null); useEffect(() => { - Sefaria.getTopicOfTheDay().then(result => setTopic(result.topic)); + Sefaria.getFeaturedTopic().then(result => setTopic(result.topic)); }, []); if (!topic) { return null; } return ( -
+

Featured Topic

-
+
-
+

-
+
")} />
diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index c0f9951723..d07a653b9f 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -2,7 +2,7 @@ import React from 'react'; import {TopicLandingSearch} from "./TopicLandingSearch"; import {NavSidebar} from "../NavSidebar"; import Footer from "../Footer"; -import {TopicOfTheDay} from "./TopicOfTheDay"; +import {FeaturedTopic} from "./FeaturedTopic"; import {TopicSalad} from "./TopicSalad"; import {RainbowLine} from "../RainbowLine"; @@ -18,7 +18,7 @@ export const TopicsLandingPage = ({openTopic}) => {
- +
diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 69b4822e79..01c7d24bd3 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2768,12 +2768,12 @@ _media: {}, const key = this._getTopicCacheKey(slug, {annotated, with_html}); return this._topics[key]; }, - _topicOfTheDay: {}, - getTopicOfTheDay: function() { + _featuredTopic: {}, + getFeaturedTopic: function() { return this._cachedApiPromise({ - url: `${Sefaria.apiHost}/_api/topics/topic-of-the-day?lang=${Sefaria.interfaceLang.slice(0, 2)}`, + url: `${Sefaria.apiHost}/_api/topics/featured-topic?lang=${Sefaria.interfaceLang.slice(0, 2)}`, key: (new Date()).toLocaleDateString(), - store: this._topicOfTheDay, + store: this._featuredTopic, }); }, _topicSlugsToTitles: null, From 12e2f5a552f2da43a798f48b87ac509833a85b9d Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Sun, 15 Dec 2024 10:13:39 +0200 Subject: [PATCH 097/265] chore(Topic Landing Temporal): change parasha link font so sans serif --- static/css/s2.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/static/css/s2.css b/static/css/s2.css index baa01d2548..1cd9049cb4 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5058,6 +5058,10 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-landing-temporal .parashah-link{ margin-top: 47px; } +.topic-landing-parasha .parasha-link .navSidebarLink.ref span{ + --english-font: var(--english-sans-serif-font-family); + --hebrew-font: var(--hebrew-sans-serif-font-family); +} .topic-landing-temporal .display-date-message{ color: var(--Darkest-Grey, #333); font-size: 14px; @@ -5082,6 +5086,9 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-landing-temporal .display-date span{ font-family: 'Roboto'; } +.topic-landing-parasha .navSidebarLink span{ + font-family: Roboto, sans-serif; +} .readerNavMenu .sheet { display: flex; justify-content: space-between; From b5e5f9d0525915e66f6333f9abef015648046a5d Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Sun, 15 Dec 2024 10:33:37 +0200 Subject: [PATCH 098/265] chore(Topic Landing Temporal): fix style, add explore-calendar-prompt --- static/css/s2.css | 11 ++++++++++- ...TopicLandingParsha.jsx => TopicLandingParasha.jsx} | 0 static/js/TopicLandingPage/TopicLandingSeasonal.jsx | 7 +++++-- static/js/TopicLandingPage/TopicsLandingPage.jsx | 2 +- static/js/sefaria/strings.js | 2 +- 5 files changed, 17 insertions(+), 5 deletions(-) rename static/js/TopicLandingPage/{TopicLandingParsha.jsx => TopicLandingParasha.jsx} (100%) diff --git a/static/css/s2.css b/static/css/s2.css index 1cd9049cb4..199afe1a18 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5043,10 +5043,19 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus --hebrew-font: var(--hebrew-sans-serif-font-family); font-size: 16px; font-style: normal; - font-weight: 600; + font-weight: 500; line-height: normal; } +.topic-landing-seasonal .explore-calendar-prompt{ + color: var(--Commentary-Blue, #4B71B7); + margin-top: 14px; +} +.topic-landing-seasonal .explore-calendar-prompt span{ + font-family: Roboto; + font-size: 14px; +} + .topic-landing-temporal .learn-more-prompt{ font-size: 14px; line-height: 18px; diff --git a/static/js/TopicLandingPage/TopicLandingParsha.jsx b/static/js/TopicLandingPage/TopicLandingParasha.jsx similarity index 100% rename from static/js/TopicLandingPage/TopicLandingParsha.jsx rename to static/js/TopicLandingPage/TopicLandingParasha.jsx diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index d2ba238a49..13f94f3ef6 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -84,7 +84,7 @@ export const TopicLandingSeasonal = () => { return (
Upcoming Holiday} + header={Upcoming Holiday on the Jewish Calendar} title={title} description={description} link={link} @@ -98,7 +98,10 @@ export const TopicLandingSeasonal = () => {
- + +
+
); diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index f4fc232fea..4d960fed94 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -5,7 +5,7 @@ import Footer from "../Footer"; import {TopicSalad} from "./TopicSalad"; import {RainbowLine} from "../RainbowLine"; import {TopicLandingCalendar} from "./TopicLandingCalendar"; -import {TopicLandingParasha} from "./TopicLandingParsha"; +import {TopicLandingParasha} from "./TopicLandingParasha"; import Search from "../sefaria/search"; import {TopicLandingSeasonal} from "./TopicLandingSeasonal"; diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index a3ebef3379..14ff7dd225 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -553,7 +553,7 @@ const Strings = { "Learn More on": "לקריאה נוספת על", "Read the Portion": "לקריאת הפרשה", "Browse all Parshayot": "לצפייה בכל הפרשות", - "Upcoming Holiday": "החג הבא", + "Upcoming Holiday on the Jewish Calendar": "החג הבא בלוח השנה", //Module Names "Download Text": "הורדת טקסט", From 90078bef43225caa89755c6ffce36537a29eaa59 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Sun, 15 Dec 2024 12:42:04 +0200 Subject: [PATCH 099/265] chore(Topic Landing Temporal): remove 'parashat' from learn more string --- static/js/TopicLandingPage/TopicLandingParasha.jsx | 4 ++-- static/js/sefaria/strings.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingParasha.jsx b/static/js/TopicLandingPage/TopicLandingParasha.jsx index 3aeed901ff..5e09ac6125 100644 --- a/static/js/TopicLandingPage/TopicLandingParasha.jsx +++ b/static/js/TopicLandingPage/TopicLandingParasha.jsx @@ -17,8 +17,8 @@ export const TopicLandingParasha = () => { const parashahDesc = parashah.description; const parashahTopicLink = `topics/${parashah?.topic}`; const parashahRefLink = `/${parashah?.url}`; - const learnMorePrompt = {en: `Learn More on Parashat ${parashahTitle?.en} ›`, - he:`${Sefaria._("Learn More on Parashat")} ${parashahTitle?.he} ›`} + const learnMorePrompt = {en: `Learn More about ${parashahTitle?.en} ›`, + he:`${Sefaria._("Learn More about")} ${parashahTitle?.he} ›`} return ( diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index 14ff7dd225..4e517ae247 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -551,6 +551,7 @@ const Strings = { "Learn More on Parashat": "לקריאה נוספת על פרשת", "This Week’s Torah Portion": "פרשת השבוע", "Learn More on": "לקריאה נוספת על", + "Learn More about": "לקריאה נוספת על", "Read the Portion": "לקריאת הפרשה", "Browse all Parshayot": "לצפייה בכל הפרשות", "Upcoming Holiday on the Jewish Calendar": "החג הבא בלוח השנה", From dd60de1b7b1338926c707b92a7cae44f914cbfd4 Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Sun, 15 Dec 2024 13:50:49 +0200 Subject: [PATCH 100/265] fix(topics): fix forked migration --- ...20241215_0408.py => 0008_auto_20241215_0745.py} | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) rename django_topics/migrations/{0007_auto_20241215_0408.py => 0008_auto_20241215_0745.py} (80%) diff --git a/django_topics/migrations/0007_auto_20241215_0408.py b/django_topics/migrations/0008_auto_20241215_0745.py similarity index 80% rename from django_topics/migrations/0007_auto_20241215_0408.py rename to django_topics/migrations/0008_auto_20241215_0745.py index 7644cfefbc..edc0a25ede 100644 --- a/django_topics/migrations/0007_auto_20241215_0408.py +++ b/django_topics/migrations/0008_auto_20241215_0745.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.29 on 2024-12-15 08:08 +# Generated by Django 1.11.29 on 2024-12-15 11:45 from __future__ import unicode_literals from django.db import migrations, models @@ -9,7 +9,7 @@ class Migration(migrations.Migration): dependencies = [ - ('django_topics', '0006_seasonaltopicenglish_seasonaltopichebrew'), + ('django_topics', '0007_auto_20241127_0034'), ] operations = [ @@ -34,13 +34,11 @@ class Migration(migrations.Migration): model_name='topicoftheday', name='topic', ), - migrations.AlterModelOptions( - name='seasonaltopicenglish', - options={'verbose_name': 'Landing Page - Calendar (EN)', 'verbose_name_plural': 'Landing Page - Calendar (EN)'}, + migrations.DeleteModel( + name='TopicOfTheDayEnglish', ), - migrations.AlterModelOptions( - name='seasonaltopichebrew', - options={'verbose_name': 'Landing Page - Calendar (HE)', 'verbose_name_plural': 'Landing Page - Calendar (HE)'}, + migrations.DeleteModel( + name='TopicOfTheDayHebrew', ), migrations.DeleteModel( name='TopicOfTheDay', From b691c1272a337a634203d909746a5b42b7d43dfa Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Sun, 15 Dec 2024 13:55:54 +0200 Subject: [PATCH 101/265] Revert "chore(api): Correct param type to enable accurate results" This reverts commit 353e652e87a5101e122fd9329d394e2d16983dda. --- sefaria/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/views.py b/sefaria/views.py index a33a385dbc..d21eae8924 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -474,7 +474,7 @@ def bulktext_api(request, refs): g = lambda x: request.GET.get(x, None) min_char = int(g("minChar")) if g("minChar") else None max_char = int(g("maxChar")) if g("maxChar") else None - res = bundle_many_texts(refs, int(g("useTextFamily")), g("asSizedString"), min_char, max_char, g("transLangPref"), g("ven"), g("vhe")) + res = bundle_many_texts(refs, g("useTextFamily"), g("asSizedString"), min_char, max_char, g("transLangPref"), g("ven"), g("vhe")) resp = jsonResponse(res, cb) return resp From 6386f4f7c42458d68acdb17808f7f5985c48f6f8 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Sun, 15 Dec 2024 14:25:05 +0200 Subject: [PATCH 102/265] fix(Topic Landing Temporal): fix explore links --- static/js/TopicLandingPage/TopicLandingParasha.jsx | 2 +- static/js/TopicLandingPage/TopicLandingSeasonal.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingParasha.jsx b/static/js/TopicLandingPage/TopicLandingParasha.jsx index 5e09ac6125..dbf2d9f79d 100644 --- a/static/js/TopicLandingPage/TopicLandingParasha.jsx +++ b/static/js/TopicLandingPage/TopicLandingParasha.jsx @@ -43,7 +43,7 @@ export const TopicLandingParasha = () => {
diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index 13f94f3ef6..e671ba81e5 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -101,7 +101,7 @@ export const TopicLandingSeasonal = () => {
); From 26ecf684f6b699cce34e23f2254ca7097401b394 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:16:50 +0200 Subject: [PATCH 103/265] feat(name api): add param to enable exact matches --- reader/views.py | 9 +++++---- sefaria/model/autospell.py | 9 +++++---- static/js/TopicLandingPage/TopicLandingSearch.jsx | 4 ++-- static/js/sefaria/sefaria.js | 3 ++- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/reader/views.py b/reader/views.py index 0b41c10820..62feba4e2b 100644 --- a/reader/views.py +++ b/reader/views.py @@ -2591,7 +2591,7 @@ def _internal_do_post(request, uid): return jsonResponse({"error": "Unsupported HTTP method."}) -def get_name_completions(name, limit, topic_override=False, type=None, topic_pool=None): +def get_name_completions(name, limit, topic_override=False, type=None, topic_pool=None, exact_continuations=False): lang = "he" if has_hebrew(name) else "en" completer = library.full_auto_completer(lang) object_data = None @@ -2617,7 +2617,7 @@ def get_name_completions(name, limit, topic_override=False, type=None, topic_poo completion_objects = [o for n in completions for o in lexicon_ac.get_data(n)] else: - completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool) + completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations) object_data = completer.get_object(name) except DictionaryEntryNotFoundError as e: @@ -2627,7 +2627,7 @@ def get_name_completions(name, limit, topic_override=False, type=None, topic_poo completions = list(OrderedDict.fromkeys(t)) # filter out dupes completion_objects = [o for n in completions for o in lexicon_ac.get_data(n)] except InputError: # Not a Ref - completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool) + completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations) object_data = completer.get_object(name) return { @@ -2650,7 +2650,8 @@ def name_api(request, name): LIMIT = int(request.GET.get("limit", 10)) type = request.GET.get("type", None) topic_pool = request.GET.get("topic_pool", None) - completions_dict = get_name_completions(name, LIMIT, topic_override, type=type, topic_pool=topic_pool) + exact_continuations = request.GET.get("exact_continuations", False) + completions_dict = get_name_completions(name, LIMIT, topic_override, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations) ref = completions_dict["ref"] topic = completions_dict["topic"] d = { diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index 84fb8980ac..340fd3782b 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -202,7 +202,7 @@ def get_data(self, instring): except KeyError: return None - def complete(self, instring, limit=0, redirected=False, type=None, topic_pool=None): + def complete(self, instring, limit=0, redirected=False, type=None, topic_pool=None, exact_continuations=False): """ Wrapper for Completions object - prioritizes and aggregates completion results. In the case where there are no results, tries to swap keyboards and get completion results from the other language. @@ -216,7 +216,7 @@ def complete(self, instring, limit=0, redirected=False, type=None, topic_pool=No if len(instring) >= self.max_completion_length: return [], [] cm = Completions(self, self.lang, instring, limit, - do_autocorrect=len(instring) < self.max_autocorrect_length, type=type, topic_pool=topic_pool) + do_autocorrect=len(instring) < self.max_autocorrect_length, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations) cm.process() if cm.has_results(): return cm.get_completion_strings(), cm.get_completion_objects() @@ -259,7 +259,7 @@ class Completions(object): "Term": "Term", "User": "User"} - def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = True, type=None, topic_pool=None): + def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = True, type=None, topic_pool=None, exact_continuations=False): """ An object that contains a single search, delegates to different methods of completions, and aggregates results. :param auto_completer: @@ -286,6 +286,7 @@ def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = Tru self._type_limit = 3 self.type = type self.topic_pool = topic_pool + self.exact_continuations = exact_continuations def has_results(self): return len(self._completion_objects) > 0 @@ -388,7 +389,7 @@ def _collect_candidates(self): self._raw_completion_strings += cs self._completion_objects += co - if self._is_past_limit(): + if self._is_past_limit() or self.exact_continuations: return # A minor variations of this string of characters deeper in the string diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index b3b7a61d1a..98019ed161 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -36,8 +36,8 @@ const getSuggestions = async (input) => { const isInputHebrew = Sefaria.hebrew.isHebrew(word); const lang = isInputHebrew? 'he' : 'en'; - const rawCompletions = await Sefaria.getName(word,20, "Topic", "library") - const completionObjects = _parseSuggestions(rawCompletions["completion_objects"], lang) + const rawCompletions = await Sefaria.getName(word,20, "Topic", "library", true); + const completionObjects = _parseSuggestions(rawCompletions["completion_objects"], lang); return completionObjects.map((suggestion) => ({ text: suggestion.title, categoryText: suggestion.categoryPathTitle, diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 3cc2fe3881..3551b017d3 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1206,13 +1206,14 @@ Sefaria = extend(Sefaria, { _lookups: {}, // getName w/ refOnly true should work as a replacement for parseRef - it uses a callback rather than return value. Besides that - same data. - getName: function(name, limit = undefined, type=undefined, topicPool=undefined) { + getName: function(name, limit = undefined, type=undefined, topicPool=undefined, exactContinuations=undefined) { const trimmed_name = name.trim(); let params = {}; // if (refOnly) { params["ref_only"] = 1; } if (limit != undefined) { params["limit"] = limit; } if (type != undefined) { params["type"] = type; } if (topicPool != undefined) { params["topic_pool"] = topicPool; } + if (exactContinuations != undefined) { params["exact_continuations"] = exactContinuations; } let queryString = Object.keys(params).map(key => key + '=' + params[key]).join('&'); queryString = (queryString ? "?" + queryString : ""); return this._cachedApiPromise({ From c48c3c32ad7f133a61cbc0722dcccaa9a3971986 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:54:56 +0200 Subject: [PATCH 104/265] fix(topic landing search): clip overflowing suggestions and put rainbow behind search box --- static/css/s2.css | 7 +++++-- static/js/TopicLandingPage/TopicLandingSearch.jsx | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index bfd4663b15..8d3bc1bbd6 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4916,10 +4916,11 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus max-height: calc(1.9em * 10 + 2em); /* 2.5em is an estimate of the height per suggestion, and 2em for padding */ z-index: 2; margin-top: 14px; + overflow-y: clip; } .topic-landing-search-dropdown:empty { - padding: 0; - visibility: hidden; + padding: 0; + visibility: hidden; } .topic-landing-search-container .readerNavMenuSearchButton{ @@ -4929,9 +4930,11 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .topic-landing-upper-rainbow{ margin-top: 88px; margin-bottom: 43px; + z-index: 0; } .topic-landing-lower-rainbow{ margin-top: 43px; + z-index: 0; } /*'Dummy' font, a hack to prevent the ellipsis char from being displayed at the end of a webkit line-clamped element*/ /*This font addresses only the ellipsis char, rendering its size 0% */ diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 98019ed161..5b371d55ec 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -110,6 +110,7 @@ export const TopicLandingSearch = ({openTopic, numOfTopics}) => { containerClassString="topic-landing-search-container" dropdownMenuClassString="topic-landing-search-dropdown" renderInput={renderInput.bind(null, openTopic, numOfTopics)} + shouldDisplaySuggestions={()=>true} />
); From b85d9801e0503c3996a5e1bb76b05552613b4081 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:07:30 +0200 Subject: [PATCH 105/265] feat(name api): prevent letter swap when exact_continuations flag is true --- sefaria/model/autospell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index 340fd3782b..e0855a7688 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -222,7 +222,7 @@ def complete(self, instring, limit=0, redirected=False, type=None, topic_pool=No return cm.get_completion_strings(), cm.get_completion_objects() # No results. Try letter swap - if not redirected and self.other_lang_ac: + if not redirected and self.other_lang_ac and not exact_continuations: swapped_string = hebrew.swap_keyboards_for_string(instring) return self.other_lang_ac.complete(swapped_string, limit, redirected=True) From 52817f9b97674896dd4e04a29253902c32c7540c Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:08:58 +0200 Subject: [PATCH 106/265] Revert "chore(api): Correct param type to enable accurate results" This reverts commit 353e652e87a5101e122fd9329d394e2d16983dda. --- sefaria/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/views.py b/sefaria/views.py index a33a385dbc..d21eae8924 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -474,7 +474,7 @@ def bulktext_api(request, refs): g = lambda x: request.GET.get(x, None) min_char = int(g("minChar")) if g("minChar") else None max_char = int(g("maxChar")) if g("maxChar") else None - res = bundle_many_texts(refs, int(g("useTextFamily")), g("asSizedString"), min_char, max_char, g("transLangPref"), g("ven"), g("vhe")) + res = bundle_many_texts(refs, g("useTextFamily"), g("asSizedString"), min_char, max_char, g("transLangPref"), g("ven"), g("vhe")) resp = jsonResponse(res, cb) return resp From ec212e93d01504b5dcab9633d5a29ee46cc2893a Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Mon, 16 Dec 2024 12:32:29 +0200 Subject: [PATCH 107/265] chore: change strings --- static/js/UserProfile.jsx | 9 ++++++--- static/js/sefaria/strings.js | 2 -- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/static/js/UserProfile.jsx b/static/js/UserProfile.jsx index 5ea5e4e400..d32f438a34 100644 --- a/static/js/UserProfile.jsx +++ b/static/js/UserProfile.jsx @@ -448,9 +448,12 @@ UserProfile.propTypes = { const EditorToggleHeader = ({usesneweditor}) => { const [feedbackHeaderState, setFeedbackHeaderState] = useState("hidden") - - const text = {usesneweditor ? "You are currently testing the new Sefaria editor." : "You are currently using the old Sefaria editor."}; - const buttonText = {usesneweditor ? "Go back to old version" : "Try the new version"}; + const old_editor_msg = "Sefaria's original source sheet editor will no longer be supported after \"Dec. 1, 2024\". Start\n" + + "using the new editor now or click to learn more about this important change."; + const new_editor_msg = "You are currently using the new source sheet editor. The old editor will no longer be\n" + + "available after \"Dec 1, 2024\". Click to learn more about this important change."; + const text = {usesneweditor ? new_editor_msg : old_editor_msg}; + const buttonText = {usesneweditor ? "Go back to old version" : "Switch to New Editor"}; const sendFeedback = () => { diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index e99b29c75f..3e757ac2fb 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -524,8 +524,6 @@ const Strings = { "Tell us about it...": "ספר/י לנו על כך...", "Submit Feedback": "לשליחת המשוב", "Thank you!": "תודה רבה!", - "You are currently testing the new Sefaria editor.": "ברגע זה הינך עושה שימוש בעורך החדש של ספריא", - "You are currently using the old Sefaria editor.": "כעת הינך עושה שימוש בגרסה הישנה של עורך ספריא", "Try the new version": "להתנסוּת בגרסה החדשה", "Your feedback is greatly appreciated. You can now edit your sheets again using the old source sheet editor. If you have any questions or additional feedback you can reach us at": "אנחנו מעריכים מאוד את המשוב שלך. כעת באפשרותך לחזור לערוך את הדפים שלך באמצעות עורך המסמכים הישן. לשאלות או למשוב נוסף אפשר לפנות אלינו בדוא\"ל:", From 6c1df74c2367479c811b61b2ec99355670bef98b Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:54:46 +0200 Subject: [PATCH 108/265] feat(helper topic): get num of library topic --- sefaria/helper/topic.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index c54bdbf25f..610e3dccba 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -10,7 +10,7 @@ from sefaria.model.topic import TopicLinkHelper from sefaria.system.database import db from sefaria.system.cache import django_cache - +from django_topics.models import Topic as DjangoTopic import structlog from sefaria import tracker from sefaria.helper.descriptions import create_era_link @@ -239,6 +239,11 @@ def get_all_topics(limit=1000, displayableOnly=True): query = {"shouldDisplay": {"$ne": False}, "numSources": {"$gt": 0}} if displayableOnly else {} return TopicSet(query, limit=limit, sort=[('numSources', -1)]).array() +@django_cache(timeout=24 * 60 * 60) +def get_num_library_topics(): + all_topics = DjangoTopic.objects.get_topic_slugs_by_pool('library') + return len(all_topics) + def get_topic_by_parasha(parasha:str) -> Topic: """ From 1890d12400ce5ec26f0f575738e8c149c846e8f9 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:03:54 +0200 Subject: [PATCH 109/265] feat(Topic Landing Search): pass num of library topics as base prop to search component --- reader/views.py | 7 ++++--- static/js/TopicLandingPage/TopicsLandingPage.jsx | 4 +++- static/js/sefaria/sefaria.js | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/reader/views.py b/reader/views.py index 62feba4e2b..03bb700f4b 100644 --- a/reader/views.py +++ b/reader/views.py @@ -62,9 +62,9 @@ from sefaria.helper.crm.crm_mediator import CrmMediator from sefaria.search import get_search_categories from sefaria.helper.topic import get_topic, get_all_topics, get_topics_for_ref, get_topics_for_book, \ - get_bulk_topics, recommend_topics, get_top_topic, get_random_topic, \ - get_random_topic_source, edit_topic_source, \ - update_order_of_topic_sources, delete_ref_topic_link, update_authors_place_and_time + get_bulk_topics, recommend_topics, get_top_topic, get_random_topic, \ + get_random_topic_source, edit_topic_source, \ + update_order_of_topic_sources, delete_ref_topic_link, update_authors_place_and_time, get_num_library_topics from sefaria.helper.community_page import get_community_page_items from sefaria.helper.file import get_resized_file from sefaria.image_generator import make_img_http_response @@ -228,6 +228,7 @@ def base_props(request): "fontSize": request.COOKIES.get("fontSize", 62.5), }, "trendingTopics": trending_topics(days=7, ntags=5), + "numLibraryTopics": get_num_library_topics(), "_siteSettings": SITE_SETTINGS, "_debug": DEBUG }) diff --git a/static/js/TopicLandingPage/TopicsLandingPage.jsx b/static/js/TopicLandingPage/TopicsLandingPage.jsx index f235e22cfc..967c3b055d 100644 --- a/static/js/TopicLandingPage/TopicsLandingPage.jsx +++ b/static/js/TopicLandingPage/TopicsLandingPage.jsx @@ -4,18 +4,20 @@ import {NavSidebar} from "../NavSidebar"; import Footer from "../Footer"; import {TopicSalad} from "./TopicSalad"; import {RainbowLine} from "../RainbowLine"; +import Sefaria from "../sefaria/sefaria"; export const TopicsLandingPage = ({openTopic}) => { const sidebarModules = [ {type: 'TrendingTopics'}, ]; + const numLibrayTopics = Sefaria.numLibraryTopics; return (
- +
diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 3551b017d3..64dae2731a 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -3342,6 +3342,7 @@ Sefaria.unpackBaseProps = function(props){ "community", "followRecommendations", "trendingTopics", + "numLibraryTopics", "_siteSettings", "_debug" ]; From 22782c055eab3a2f7b834482d2761d56b9ce556f Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:33:48 +0200 Subject: [PATCH 110/265] feat(Topic Landing Search and name api): another flag to enable ordering of matches by length of matched string --- reader/views.py | 9 +++++---- sefaria/model/autospell.py | 9 ++++++--- static/js/TopicLandingPage/TopicLandingSearch.jsx | 4 ++-- static/js/sefaria/sefaria.js | 3 ++- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/reader/views.py b/reader/views.py index 03bb700f4b..ab7a08d1ec 100644 --- a/reader/views.py +++ b/reader/views.py @@ -2592,7 +2592,7 @@ def _internal_do_post(request, uid): return jsonResponse({"error": "Unsupported HTTP method."}) -def get_name_completions(name, limit, topic_override=False, type=None, topic_pool=None, exact_continuations=False): +def get_name_completions(name, limit, topic_override=False, type=None, topic_pool=None, exact_continuations=False, order_by_matched_length=False): lang = "he" if has_hebrew(name) else "en" completer = library.full_auto_completer(lang) object_data = None @@ -2618,7 +2618,7 @@ def get_name_completions(name, limit, topic_override=False, type=None, topic_poo completion_objects = [o for n in completions for o in lexicon_ac.get_data(n)] else: - completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations) + completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations, order_by_matched_length=order_by_matched_length) object_data = completer.get_object(name) except DictionaryEntryNotFoundError as e: @@ -2628,7 +2628,7 @@ def get_name_completions(name, limit, topic_override=False, type=None, topic_poo completions = list(OrderedDict.fromkeys(t)) # filter out dupes completion_objects = [o for n in completions for o in lexicon_ac.get_data(n)] except InputError: # Not a Ref - completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations) + completions, completion_objects = completer.complete(name, limit, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations, order_by_matched_length=order_by_matched_length) object_data = completer.get_object(name) return { @@ -2652,7 +2652,8 @@ def name_api(request, name): type = request.GET.get("type", None) topic_pool = request.GET.get("topic_pool", None) exact_continuations = request.GET.get("exact_continuations", False) - completions_dict = get_name_completions(name, LIMIT, topic_override, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations) + order_by_matched_length = request.GET.get("order_by_matched_length", False) + completions_dict = get_name_completions(name, LIMIT, topic_override, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations, order_by_matched_length=order_by_matched_length) ref = completions_dict["ref"] topic = completions_dict["topic"] d = { diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index e0855a7688..eed2ddb240 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -202,7 +202,7 @@ def get_data(self, instring): except KeyError: return None - def complete(self, instring, limit=0, redirected=False, type=None, topic_pool=None, exact_continuations=False): + def complete(self, instring, limit=0, redirected=False, type=None, topic_pool=None, exact_continuations=False, order_by_matched_length=False): """ Wrapper for Completions object - prioritizes and aggregates completion results. In the case where there are no results, tries to swap keyboards and get completion results from the other language. @@ -216,7 +216,7 @@ def complete(self, instring, limit=0, redirected=False, type=None, topic_pool=No if len(instring) >= self.max_completion_length: return [], [] cm = Completions(self, self.lang, instring, limit, - do_autocorrect=len(instring) < self.max_autocorrect_length, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations) + do_autocorrect=len(instring) < self.max_autocorrect_length, type=type, topic_pool=topic_pool, exact_continuations=exact_continuations, order_by_matched_length=order_by_matched_length) cm.process() if cm.has_results(): return cm.get_completion_strings(), cm.get_completion_objects() @@ -259,7 +259,7 @@ class Completions(object): "Term": "Term", "User": "User"} - def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = True, type=None, topic_pool=None, exact_continuations=False): + def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = True, type=None, topic_pool=None, exact_continuations=False, order_by_matched_length=False): """ An object that contains a single search, delegates to different methods of completions, and aggregates results. :param auto_completer: @@ -287,6 +287,7 @@ def __init__(self, auto_completer, lang, instring, limit=0, do_autocorrect = Tru self.type = type self.topic_pool = topic_pool self.exact_continuations = exact_continuations + self.order_by_matched_length = order_by_matched_length def has_results(self): return len(self._completion_objects) > 0 @@ -368,6 +369,8 @@ def _collect_candidates(self): if len(joined): # joined.sort(key=lambda w: w[1]["order"]) joined.sort(key=self._candidate_order) + if self.order_by_matched_length: + joined.sort(key=lambda i: len(i[0])) self._raw_completion_strings, self._completion_objects = [list(_) for _ in zip(*joined)] else: self._raw_completion_strings, self._completion_objects = [], [] diff --git a/static/js/TopicLandingPage/TopicLandingSearch.jsx b/static/js/TopicLandingPage/TopicLandingSearch.jsx index 5b371d55ec..b09509f929 100644 --- a/static/js/TopicLandingPage/TopicLandingSearch.jsx +++ b/static/js/TopicLandingPage/TopicLandingSearch.jsx @@ -36,7 +36,7 @@ const getSuggestions = async (input) => { const isInputHebrew = Sefaria.hebrew.isHebrew(word); const lang = isInputHebrew? 'he' : 'en'; - const rawCompletions = await Sefaria.getName(word,20, "Topic", "library", true); + const rawCompletions = await Sefaria.getName(word,20, "Topic", "library", true, true); const completionObjects = _parseSuggestions(rawCompletions["completion_objects"], lang); return completionObjects.map((suggestion) => ({ text: suggestion.title, @@ -110,7 +110,7 @@ export const TopicLandingSearch = ({openTopic, numOfTopics}) => { containerClassString="topic-landing-search-container" dropdownMenuClassString="topic-landing-search-dropdown" renderInput={renderInput.bind(null, openTopic, numOfTopics)} - shouldDisplaySuggestions={()=>true} + // shouldDisplaySuggestions={()=>true} />
); diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 64dae2731a..b39e7c847e 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1206,7 +1206,7 @@ Sefaria = extend(Sefaria, { _lookups: {}, // getName w/ refOnly true should work as a replacement for parseRef - it uses a callback rather than return value. Besides that - same data. - getName: function(name, limit = undefined, type=undefined, topicPool=undefined, exactContinuations=undefined) { + getName: function(name, limit = undefined, type=undefined, topicPool=undefined, exactContinuations=undefined, orderByMatchedLength=undefined) { const trimmed_name = name.trim(); let params = {}; // if (refOnly) { params["ref_only"] = 1; } @@ -1214,6 +1214,7 @@ Sefaria = extend(Sefaria, { if (type != undefined) { params["type"] = type; } if (topicPool != undefined) { params["topic_pool"] = topicPool; } if (exactContinuations != undefined) { params["exact_continuations"] = exactContinuations; } + if (orderByMatchedLength != undefined) { params["order_by_matched_length"] = orderByMatchedLength; } let queryString = Object.keys(params).map(key => key + '=' + params[key]).join('&'); queryString = (queryString ? "?" + queryString : ""); return this._cachedApiPromise({ From d9f25b30e1f24b943dd6f70a7ac0c0968e9f7e2b Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:48:07 +0200 Subject: [PATCH 111/265] chore(Topic Landing Temporal): remove redundant variables --- .../TopicLandingPage/TopicLandingParasha.jsx | 55 +++++++++---------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingParasha.jsx b/static/js/TopicLandingPage/TopicLandingParasha.jsx index dbf2d9f79d..844f1c3351 100644 --- a/static/js/TopicLandingPage/TopicLandingParasha.jsx +++ b/static/js/TopicLandingPage/TopicLandingParasha.jsx @@ -13,39 +13,34 @@ export const TopicLandingParasha = () => { Sefaria.getUpcomingDay('parasha').then(setParashah); }, []); - const parashahTitle = parashah.displayValue; - const parashahDesc = parashah.description; - const parashahTopicLink = `topics/${parashah?.topic}`; - const parashahRefLink = `/${parashah?.url}`; - const learnMorePrompt = {en: `Learn More about ${parashahTitle?.en} ›`, - he:`${Sefaria._("Learn More about")} ${parashahTitle?.he} ›`} - return (
- This Week’s Torah Portion} - title={parashahTitle} - description={parashahDesc} - link={parashahTopicLink} - > -
- - - -
-
- -
- - -
+ This Week’s Torah Portion} + title={parashah.displayValue} + description={parashah.description} + link={`topics/${parashah?.topic}`} + > +
+ + + +
+
+ +
+ + +
); }; \ No newline at end of file From a4190925fbe4b747b720677fdadfae6af2b83cf0 Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:44:42 +0200 Subject: [PATCH 112/265] fix(Topic Landing): remove deprecated api endpoint from urls.py --- sefaria/urls.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sefaria/urls.py b/sefaria/urls.py index e03302f937..f8f5ef98f7 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -102,7 +102,6 @@ url(r'^topics/?$', reader_views.topics_page), url(r'^topics/b/(?P.+)$', reader_views.topic_page_b), url(r'^topics/(?P.+)$', reader_views.topic_page), - url(r'^api/topic/completion/(?P.+)', reader_views.topic_completion_api), url(r'^_api/topics/images/secondary/(?P.+)$', reader_views.topic_upload_photo, {"secondary": True}), url(r'^_api/topics/images/(?P.+)$', reader_views.topic_upload_photo) From c4c55255139366de8d6d1446b38d09f183f4be9a Mon Sep 17 00:00:00 2001 From: yonadavGit <92536571+yonadavGit@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:02:42 +0200 Subject: [PATCH 113/265] chore(Topic Landing): remove redundant variables from hook --- .../TopicLandingPage/TopicLandingSeasonal.jsx | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx index e671ba81e5..79c5c2623c 100644 --- a/static/js/TopicLandingPage/TopicLandingSeasonal.jsx +++ b/static/js/TopicLandingPage/TopicLandingSeasonal.jsx @@ -23,28 +23,16 @@ const useSeasonalTopic = () => { if (!seasonal) return { isLoading: true }; - const title = seasonal.topic?.primaryTitle; - const description = seasonal.topic?.description; - const link = `/topics/${seasonal.topic?.slug}`; - - const displayStartDate = new Date(seasonal.display_start_date); - const displayEndDate = new Date(seasonal.display_end_date); - const displayDatePrefix = seasonal.display_date_prefix || ''; - const displayDateSuffix = seasonal.display_date_suffix || ''; - const secondaryTopicTitle = seasonal.secondary_topic?.primaryTitle || null; - const secondaryTopicSlug = seasonal.secondary_topic?.slug || null; - - return { - title, - description, - link, - displayStartDate, - displayEndDate, - displayDateSuffix, - displayDatePrefix, - secondaryTopicTitle, - secondaryTopicSlug, + title: seasonal.topic?.primaryTitle, + description: seasonal.topic?.description, + link: `/topics/${seasonal.topic?.slug}`, + displayStartDate: new Date(seasonal.display_start_date), + displayEndDate: new Date(seasonal.display_end_date), + displayDatePrefix: seasonal.display_date_prefix || '', + displayDateSuffix: seasonal.display_date_suffix || '', + secondaryTopicTitle: seasonal.secondary_topic?.primaryTitle || null, + secondaryTopicSlug: seasonal.secondary_topic?.slug || null, isLoading: false, }; }; From 94784793e388b9030fe11fe1db814f208d3b3f8a Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Tue, 17 Dec 2024 13:04:05 +0200 Subject: [PATCH 114/265] chore(topics): leave TopicOfTheDay model name as is to avoid issues with shared cauldron postgres --- .../migrations/0008_auto_20241215_0745.py | 74 ------------------- .../migrations/0008_auto_20241217_0702.py | 49 ++++++++++++ django_topics/models/__init__.py | 2 +- django_topics/models/featured_topic.py | 10 +-- .../models/tests/featured_topic_test.py | 18 ++--- django_topics/models/topic_of_the_day.py | 0 reader/views.py | 4 +- 7 files changed, 66 insertions(+), 91 deletions(-) delete mode 100644 django_topics/migrations/0008_auto_20241215_0745.py create mode 100644 django_topics/migrations/0008_auto_20241217_0702.py delete mode 100644 django_topics/models/topic_of_the_day.py diff --git a/django_topics/migrations/0008_auto_20241215_0745.py b/django_topics/migrations/0008_auto_20241215_0745.py deleted file mode 100644 index edc0a25ede..0000000000 --- a/django_topics/migrations/0008_auto_20241215_0745.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.29 on 2024-12-15 11:45 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('django_topics', '0007_auto_20241127_0034'), - ] - - operations = [ - migrations.CreateModel( - name='FeaturedTopic', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('start_date', models.DateField()), - ('lang', models.CharField(choices=[('en', 'English'), ('he', 'Hebrew')], max_length=2)), - ('topic', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='featured_topic', to='django_topics.Topic')), - ], - options={ - 'verbose_name': 'Landing Page - Featured Topic', - 'verbose_name_plural': 'Landing Page - Featured Topic', - }, - ), - migrations.AlterUniqueTogether( - name='topicoftheday', - unique_together=set([]), - ), - migrations.RemoveField( - model_name='topicoftheday', - name='topic', - ), - migrations.DeleteModel( - name='TopicOfTheDayEnglish', - ), - migrations.DeleteModel( - name='TopicOfTheDayHebrew', - ), - migrations.DeleteModel( - name='TopicOfTheDay', - ), - migrations.CreateModel( - name='FeaturedTopicEnglish', - fields=[ - ], - options={ - 'verbose_name': 'Landing Page - Featured Topic (EN)', - 'verbose_name_plural': 'Landing Page - Featured Topic (EN)', - 'proxy': True, - 'indexes': [], - }, - bases=('django_topics.featuredtopic',), - ), - migrations.CreateModel( - name='FeaturedTopicHebrew', - fields=[ - ], - options={ - 'verbose_name': 'Landing Page - Featured Topic (HE)', - 'verbose_name_plural': 'Landing Page - Featured Topic (HE)', - 'proxy': True, - 'indexes': [], - }, - bases=('django_topics.featuredtopic',), - ), - migrations.AlterUniqueTogether( - name='featuredtopic', - unique_together=set([('topic', 'start_date')]), - ), - ] diff --git a/django_topics/migrations/0008_auto_20241217_0702.py b/django_topics/migrations/0008_auto_20241217_0702.py new file mode 100644 index 0000000000..117ec91ab6 --- /dev/null +++ b/django_topics/migrations/0008_auto_20241217_0702.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-12-17 11:02 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_topics', '0007_auto_20241127_0034'), + ] + + operations = [ + migrations.DeleteModel( + name='TopicOfTheDayEnglish', + ), + migrations.DeleteModel( + name='TopicOfTheDayHebrew', + ), + migrations.CreateModel( + name='FeaturedTopicEnglish', + fields=[ + ], + options={ + 'verbose_name': 'Landing Page - Featured Topic (EN)', + 'verbose_name_plural': 'Landing Page - Featured Topic (EN)', + 'proxy': True, + 'indexes': [], + }, + bases=('django_topics.topicoftheday',), + ), + migrations.CreateModel( + name='FeaturedTopicHebrew', + fields=[ + ], + options={ + 'verbose_name': 'Landing Page - Featured Topic (HE)', + 'verbose_name_plural': 'Landing Page - Featured Topic (HE)', + 'proxy': True, + 'indexes': [], + }, + bases=('django_topics.topicoftheday',), + ), + migrations.AlterModelOptions( + name='topicoftheday', + options={'verbose_name': 'Landing Page - Featured Topic', 'verbose_name_plural': 'Landing Page - Featured Topic'}, + ), + ] diff --git a/django_topics/models/__init__.py b/django_topics/models/__init__.py index 682554f9bf..25b56d5cec 100644 --- a/django_topics/models/__init__.py +++ b/django_topics/models/__init__.py @@ -1,4 +1,4 @@ from .topic import Topic from .pool import TopicPool, PoolType -from .featured_topic import FeaturedTopic, FeaturedTopicEnglish, FeaturedTopicHebrew +from .featured_topic import TopicOfTheDay, FeaturedTopicEnglish, FeaturedTopicHebrew from .seasonal_topic import SeasonalTopic, SeasonalTopicEnglish, SeasonalTopicHebrew diff --git a/django_topics/models/featured_topic.py b/django_topics/models/featured_topic.py index 2cd7921df7..d106306c43 100644 --- a/django_topics/models/featured_topic.py +++ b/django_topics/models/featured_topic.py @@ -6,7 +6,7 @@ class FeaturedTopicManager(models.Manager): - def get_featured_topic(self, lang: str, date: datetime = None) -> 'FeaturedTopic': + def get_featured_topic(self, lang: str, date: datetime = None) -> 'TopicOfTheDay': """ Return featured topic for given date or closest date that is less than or equal to given date @param lang: language code, "en" or "he" @@ -21,11 +21,11 @@ def get_featured_topic(self, lang: str, date: datetime = None) -> 'FeaturedTopic ) -class FeaturedTopic(models.Model): +class TopicOfTheDay(models.Model): topic = models.ForeignKey( Topic, on_delete=models.CASCADE, - related_name='featured_topic' + related_name='topic_of_the_day' ) start_date = models.DateField() lang = models.CharField(max_length=2, choices=[('en', 'English'), ('he', 'Hebrew')]) @@ -40,7 +40,7 @@ def __str__(self): return f"{self.topic.slug} ({self.start_date})" -class FeaturedTopicEnglish(FeaturedTopic): +class FeaturedTopicEnglish(TopicOfTheDay): class Meta: proxy = True verbose_name = "Landing Page - Featured Topic (EN)" @@ -51,7 +51,7 @@ def save(self, *args, **kwargs): super().save(*args, **kwargs) -class FeaturedTopicHebrew(FeaturedTopic): +class FeaturedTopicHebrew(TopicOfTheDay): class Meta: proxy = True verbose_name = "Landing Page - Featured Topic (HE)" diff --git a/django_topics/models/tests/featured_topic_test.py b/django_topics/models/tests/featured_topic_test.py index 617f63bc3a..cf3736ee7a 100644 --- a/django_topics/models/tests/featured_topic_test.py +++ b/django_topics/models/tests/featured_topic_test.py @@ -1,6 +1,6 @@ import pytest from datetime import date -from django_topics.models import FeaturedTopic, Topic +from django_topics.models import TopicOfTheDay, Topic @pytest.fixture @@ -11,11 +11,11 @@ def topic(db): @pytest.fixture def featured_topics(db, topic): - """Fixture to create FeaturedTopic instances.""" + """Fixture to create TopicOfTheDay instances.""" topics = [ - FeaturedTopic.objects.create(topic=topic, start_date=date(2024, 11, 26), lang="en"), - FeaturedTopic.objects.create(topic=topic, start_date=date(2024, 11, 25), lang="en"), - FeaturedTopic.objects.create(topic=topic, start_date=date(2024, 11, 24), lang="en"), + TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 26), lang="en"), + TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 25), lang="en"), + TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 24), lang="en"), ] return topics @@ -23,7 +23,7 @@ def featured_topics(db, topic): @pytest.mark.django_db def test_get_featured_topic_with_exact_date_db(featured_topics): """Test for exact match.""" - result = FeaturedTopic.objects.get_featured_topic(lang="en", date=date(2024, 11, 26)) + result = TopicOfTheDay.objects.get_featured_topic(lang="en", date=date(2024, 11, 26)) assert result.start_date == date(2024, 11, 26) assert result.lang == "en" @@ -32,7 +32,7 @@ def test_get_featured_topic_with_exact_date_db(featured_topics): @pytest.mark.django_db def test_get_featured_topic_with_closest_date_db(featured_topics): """Test for the closest date less than or equal to the given date.""" - result = FeaturedTopic.objects.get_featured_topic(lang="en", date=date(2024, 11, 27)) + result = TopicOfTheDay.objects.get_featured_topic(lang="en", date=date(2024, 11, 27)) assert result.start_date == date(2024, 11, 26) assert result.lang == "en" @@ -41,7 +41,7 @@ def test_get_featured_topic_with_closest_date_db(featured_topics): @pytest.mark.django_db def test_get_featured_topic_with_no_matching_date_db(db, topic): """Test when there is no matching date.""" - FeaturedTopic.objects.create(topic=topic, start_date=date(2024, 11, 20), lang="en") - result = FeaturedTopic.objects.get_featured_topic(lang="en", date=date(2024, 11, 19)) + TopicOfTheDay.objects.create(topic=topic, start_date=date(2024, 11, 20), lang="en") + result = TopicOfTheDay.objects.get_featured_topic(lang="en", date=date(2024, 11, 19)) assert result is None diff --git a/django_topics/models/topic_of_the_day.py b/django_topics/models/topic_of_the_day.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/reader/views.py b/reader/views.py index da1f4d0ac3..2d83921de4 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3210,11 +3210,11 @@ def topic_pool_api(request, pool_name): @catch_error_as_json def featured_topic_api(request): - from django_topics.models import FeaturedTopic + from django_topics.models import TopicOfTheDay lang = request.GET.get("lang") cb = request.GET.get("callback", None) - featured_topic = FeaturedTopic.objects.get_featured_topic(lang) + featured_topic = TopicOfTheDay.objects.get_featured_topic(lang) if not featured_topic: return jsonResponse({'error': f'No featured topic found for lang "{lang}"'}, status=404) mongo_topic = Topic.init(featured_topic.topic.slug) From f18e75b45ae9a78463c8a8a08bd55cd1108c4bbe Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Tue, 17 Dec 2024 13:32:54 +0200 Subject: [PATCH 115/265] chore(topics): upgrade pytest to match pytest-django --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ab56da61c7..c6a75b62b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -52,7 +52,7 @@ pillow==8.0.1; sys_platform == 'linux' psycopg2==2.8.6 #for dev: psycopg2-binary==2.8.6 py2-py3-django-email-as-username==1.7.1 pymongo==3.12.* -pytest==6.1.1 +pytest~=7.4.4 python-bidi pytz pyyaml==6.0.1 From 74357c2e1418be0d907d759b3e9ed06613988b9b Mon Sep 17 00:00:00 2001 From: nsantacruz Date: Tue, 17 Dec 2024 14:48:36 +0200 Subject: [PATCH 116/265] fix(topics): use secondary image --- static/js/TopicLandingPage/FeaturedTopic.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/TopicLandingPage/FeaturedTopic.jsx b/static/js/TopicLandingPage/FeaturedTopic.jsx index bc8dca23e0..e3e145a6d2 100644 --- a/static/js/TopicLandingPage/FeaturedTopic.jsx +++ b/static/js/TopicLandingPage/FeaturedTopic.jsx @@ -14,7 +14,7 @@ export const FeaturedTopic = () => {

Featured Topic

- +

From 637d124169fde180f785b9e9afbe1faf70049995 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Tue, 17 Dec 2024 14:53:12 +0200 Subject: [PATCH 117/265] chore: warning banner in sheets with learn more link --- static/css/s2.css | 22 +++++++++++++++++++++- static/css/sheets.css | 5 +---- static/js/UserProfile.jsx | 1 + templates/sheets.html | 6 ++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index f8b7d3f9b2..1854d6ccc7 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -11198,6 +11198,17 @@ cursor: pointer; .profile-page .social-icon:hover img { opacity: 0.6; } +#staticContentWrapper .editorToggleHeader { + width: 100%; + background-color: #18345D; + color: white; + height: 60px; + font-size: 16px; + display: flex; + justify-content: center; + align-items: center; + margin: revert; +} .editorToggleHeader { width: 100%; background-color: #18345D; @@ -11207,7 +11218,7 @@ cursor: pointer; margin-top: -60px; margin-left: -12px; margin-bottom: 80px; - padding-right: 12px; + padding: 0 100px; display: flex; justify-content: center; align-items: center; @@ -11218,12 +11229,21 @@ cursor: pointer; direction: rtl; } .editorToggleHeader .button { + white-space: nowrap; padding: 5px 8px; margin-inline-start: 15px; margin-top: 5px; height: 30px; letter-spacing: 0; } +.editorToggleHeader .learnMore { + text-decoration: underline; + color: white; + margin-right: 200px; + margin-left: 20px; + width: 15%; + padding: revert; +} .feedbackOverlay { position: fixed; width: 100%; diff --git a/static/css/sheets.css b/static/css/sheets.css index 91a8211f76..e971a33726 100755 --- a/static/css/sheets.css +++ b/static/css/sheets.css @@ -1628,12 +1628,9 @@ body.hasBannerMessage .sheetsEditNavTop { padding: 16px 20px; margin-bottom: 0; } -.sheetsEditNavTop .button { +.sheetsEditNavTop #save { background-color: #212e50; border-color: #212e50; - padding: 12px 24px; -} -.sheetsEditNavTop .button { padding: 5px 12px; } #customTextLanguageToggle { diff --git a/static/js/UserProfile.jsx b/static/js/UserProfile.jsx index d32f438a34..cfa76d38bd 100644 --- a/static/js/UserProfile.jsx +++ b/static/js/UserProfile.jsx @@ -550,6 +550,7 @@ const EditorToggleHeader = ({usesneweditor}) => { <> {feedbackHeaderState != "hidden" ?
{overlayContent}
: null} diff --git a/templates/sheets.html b/templates/sheets.html index 19cc55ad13..bf404deb33 100755 --- a/templates/sheets.html +++ b/templates/sheets.html @@ -21,9 +21,15 @@ +
+
+ Sefaria's original source sheet editor will no longer be supported after "Dec. 1, 2024". Start using the new editor now or click to learn more about this important change. + Switch to New Editor + Learn More +