Prevent saveWorldInfo calls while rendering the list
| @@ -85,6 +85,7 @@ const saveSettingsDebounced = debounce(() => { | |||
| 85 | }, debounce_timeout.relaxed); | 85 | }, debounce_timeout.relaxed); |
| 86 | const sortFn = (a, b) => b.order - a.order; | 86 | const sortFn = (a, b) => b.order - a.order; |
| 87 | let updateEditor = (navigation, flashOnNav = true) => { console.debug('Triggered WI navigation', navigation, flashOnNav); }; | 87 | let updateEditor = (navigation, flashOnNav = true) => { console.debug('Triggered WI navigation', navigation, flashOnNav); }; |
| 88 | let isSaveWorldInfoDisabled = false; | ||
| 88 | 89 | ||
| 89 | // Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data. | 90 | // Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data. |
| 90 | export const worldInfoFilter = new FilterHelper(() => updateEditor()); | 91 | export const worldInfoFilter = new FilterHelper(() => updateEditor()); |
| @@ -2010,12 +2011,9 @@ async function displayWorldEntries(name, data, navigation = navigation_option.no | |||
| 2010 | 2011 | ||
| 2011 | const worldEntriesList = $('#world_popup_entries_list'); | 2012 | const worldEntriesList = $('#world_popup_entries_list'); |
| 2012 | 2013 | ||
| 2013 | // We save costly performance by removing all events before emptying. Because we know there are no relevant event handlers reacting on removing elements | ||
| 2014 | // This prevents jQuery from actually going through all registered events on the controls for each entry when removing it | ||
| 2015 | //worldEntriesList.find('*').off(); | ||
| 2016 | worldEntriesList.css({ 'opacity': 0, 'transition': 'opacity 250ms ease-in-out' }); | 2014 | worldEntriesList.css({ 'opacity': 0, 'transition': 'opacity 250ms ease-in-out' }); |
| 2017 | await delay(250); | 2015 | await delay(250); |
| 2018 | clearEntryList(); // Use enhanced cleanup | 2016 | clearEntryList(); |
| 2019 | worldEntriesList.show(); | 2017 | worldEntriesList.show(); |
| 2020 | 2018 | ||
| 2021 | if (!data || !('entries' in data)) { | 2019 | if (!data || !('entries' in data)) { |
| @@ -2109,23 +2107,39 @@ async function displayWorldEntries(name, data, navigation = navigation_option.no | |||
| 2109 | formatNavigator: PAGINATION_TEMPLATE, | 2107 | formatNavigator: PAGINATION_TEMPLATE, |
| 2110 | showNavigator: true, | 2108 | showNavigator: true, |
| 2111 | callback: async function (/** @type {object[]} */ page) { | 2109 | callback: async function (/** @type {object[]} */ page) { |
| 2112 | // We save costly performance by removing all events before emptying. Because we know there are no relevant event handlers reacting on removing elements | 2110 | try { |
| 2113 | // This prevents jQuery from actually going through all registered events on the controls for each entry when removing it | 2111 | // Prevent saveWorldInfo from firing timeouts while rendering the list |
| 2114 | //worldEntriesList.find('*').off(); | 2112 | isSaveWorldInfoDisabled = true; |
| 2115 | clearEntryList(); | 2113 | clearEntryList(); |
| 2116 | //worldEntriesList.empty(); | ||
| 2117 | 2114 | ||
| 2118 | const keywordHeaders = await renderTemplateAsync('worldInfoKeywordHeaders'); | 2115 | const keywordHeaders = await renderTemplateAsync('worldInfoKeywordHeaders'); |
| 2119 | const blocksPromises = page.map(async (entry) => await getWorldEntry(name, data, entry)).filter(x => x); | 2116 | const blocks = []; |
| 2120 | const blocks = await Promise.all(blocksPromises); | 2117 | |
| 2118 | for (const entry of page) { | ||
| 2119 | try { | ||
| 2120 | const block = await getWorldEntry(name, data, entry); | ||
| 2121 | if (block) { | ||
| 2122 | blocks.push(block); | ||
| 2123 | } | ||
| 2124 | } catch (error) { | ||
| 2125 | console.error(`Error while processing entry ${entry.uid}:`, error); | ||
| 2126 | } | ||
| 2127 | } | ||
| 2128 | |||
| 2121 | const isCustomOrder = $('#world_info_sort_order').find(':selected').data('rule') === 'custom'; | 2129 | const isCustomOrder = $('#world_info_sort_order').find(':selected').data('rule') === 'custom'; |
| 2122 | if (!isCustomOrder) { | 2130 | if (!isCustomOrder) { |
| 2123 | blocks.forEach(block => { | 2131 | blocks.forEach(block => { |
| 2124 | block.find('.drag-handle').remove(); | 2132 | block.find('.drag-handle').remove(); |
| 2125 | }); | 2133 | }); |
| 2126 | } | 2134 | } |
| 2135 | |||
| 2127 | worldEntriesList.append(keywordHeaders); | 2136 | worldEntriesList.append(keywordHeaders); |
| 2128 | worldEntriesList.append(blocks); | 2137 | worldEntriesList.append(blocks); |
| 2138 | } catch (error) { | ||
| 2139 | console.error('Error while rendering WI entries:', error); | ||
| 2140 | } finally { | ||
| 2141 | isSaveWorldInfoDisabled = false; | ||
| 2142 | } | ||
| 2129 | }, | 2143 | }, |
| 2130 | afterSizeSelectorChange: function (e) { | 2144 | afterSizeSelectorChange: function (e) { |
| 2131 | accountStorage.setItem(storageKey, e.target.value); | 2145 | accountStorage.setItem(storageKey, e.target.value); |
| @@ -3739,6 +3753,11 @@ async function _save(name, data) { | |||
| 3739 | * @return {Promise<void>} A promise that resolves when the world info is saved | 3753 | * @return {Promise<void>} A promise that resolves when the world info is saved |
| 3740 | */ | 3754 | */ |
| 3741 | export async function saveWorldInfo(name, data, immediately = false) { | 3755 | export async function saveWorldInfo(name, data, immediately = false) { |
| 3756 | // Saving is temporarily disabled | ||
| 3757 | if (isSaveWorldInfoDisabled) { | ||
| 3758 | return; | ||
| 3759 | } | ||
| 3760 | |||
| 3742 | if (!name || !data) { | 3761 | if (!name || !data) { |
| 3743 | return; | 3762 | return; |
| 3744 | } | 3763 | } |