diff --git a/public/index.html b/public/index.html index f88e14edc..a5143f554 100644 --- a/public/index.html +++ b/public/index.html @@ -2292,7 +2292,8 @@
-
+
+
diff --git a/public/script.js b/public/script.js index 15a6f41fd..964927eb4 100644 --- a/public/script.js +++ b/public/script.js @@ -116,6 +116,8 @@ import { countOccurrences, isOdd, isElementInViewport, + sortMoments, + timestampToMoment, } from "./scripts/utils.js"; import { extension_settings, loadExtensionSettings, runGenerationInterceptors } from "./scripts/extensions.js"; @@ -1503,7 +1505,7 @@ class StreamingProcessor { return; } - $(`#chat .mes[mesid="${messageId}"] .mes_stop`).css({ 'display': '' }); + $(`#chat .mes[mesid="${messageId}"] .mes_stop`).css({ 'display': 'block' }); $(`#chat .mes[mesid="${messageId}"] .mes_buttons`).css({ 'display': 'none' }); } @@ -1513,7 +1515,7 @@ class StreamingProcessor { } $(`#chat .mes[mesid="${messageId}"] .mes_stop`).css({ 'display': 'none' }); - $(`#chat .mes[mesid="${messageId}"] .mes_buttons`).css({ 'display': '' }); + $(`#chat .mes[mesid="${messageId}"] .mes_buttons`).css({ 'display': 'block' }); } onStartStreaming(text) { @@ -4040,6 +4042,9 @@ export async function displayPastChats() { const displayName = selected_group ? group?.name : characters[this_chid].name; const avatarImg = selected_group ? group?.avatar_url : getThumbnailUrl('avatar', characters[this_chid]['avatar']); + // Sort by last message date descending + data.sort((a, b) => sortMoments(timestampToMoment(a.last_mes), timestampToMoment(b.last_mes))); + $("#load_select_chat_div").css("display", "none"); $("#ChatHistoryCharName").text(displayName); for (const key in data) { @@ -4053,6 +4058,7 @@ export async function displayPastChats() { const chat_items = data[key]["chat_items"]; const file_size = data[key]["file_size"]; const fileName = data[key]['file_name']; + const timestamp = timestampToMoment(data[key]['last_mes']).format('LL LT'); const template = $('#past_chat_template .select_chat_block_wrapper').clone(); template.find('.select_chat_block').attr('file_name', fileName); template.find('.avatar img').attr('src', avatarImg); @@ -4061,6 +4067,7 @@ export async function displayPastChats() { template.find('.chat_messages_num').text(" (" + chat_items + " messages)"); template.find('.select_chat_block_mes').text(mes); template.find('.PastChat_cross').attr('file_name', fileName); + template.find('.chat_messages_date').text(timestamp); if (selected_group) { template.find('.avatar img').replaceWith(getGroupAvatar(group)); diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index 90f189f1f..3876c7965 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -1206,9 +1206,11 @@ export async function getGroupPastChats(groupId) { let this_chat_file_size = (JSON.stringify(messages).length / 1024).toFixed(2) + "kb"; let chat_items = messages.length; const lastMessage = messages.length ? messages[messages.length - 1].mes : '[The chat is empty]'; + const lastMessageDate = messages.length ? (messages[messages.length - 1].send_date || Date.now()) : Date.now(); chats.push({ 'file_name': chatId, 'mes': lastMessage, + 'last_mes': lastMessageDate, 'file_size': this_chat_file_size, 'chat_items': chat_items, }); @@ -1303,7 +1305,7 @@ export async function importGroupChat(formData) { if (data.res) { const chatId = data.res; const group = groups.find(x => x.id == selected_group); - + if (group) { group.chats.push(chatId); await editGroup(selected_group, true, true); @@ -1369,4 +1371,4 @@ jQuery(() => { const value = $(this).prop("checked"); is_group_automode_enabled = value; }); -}); \ No newline at end of file +}); diff --git a/public/scripts/utils.js b/public/scripts/utils.js index 61f0d1321..d9b921ccc 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -259,6 +259,31 @@ export function isOdd(number) { return number % 2 !== 0; } +export function timestampToMoment(timestamp) { + // Unix time (legacy TAI) + if (typeof timestamp === 'number') { + return moment(timestamp); + } + + // ST "humanized" format pattern + const pattern = /(\d{4})-(\d{1,2})-(\d{1,2}) @(\d{1,2})h (\d{1,2})m (\d{1,2})s (\d{1,3})ms/; + const replacement = (match, year, month, day, hour, minute, second, millisecond) => { + return `${year.padStart(4, "0")}-${month.padStart(2, "0")}-${day.padStart(2, "0")}T${hour.padStart(2, "0")}:${minute.padStart(2, "0")}:${second.padStart(2, "0")}.${millisecond.padStart(3, "0")}Z`; + }; + const isoTimestamp = timestamp.replace(pattern, replacement); + return moment(isoTimestamp); +} + +export function sortMoments(a, b) { + if (a.isBefore(b)) { + return 1; + } else if (a.isAfter(b)) { + return -1; + } else { + return 0; + } +} + /** Split string to parts no more than length in size */ export function splitRecursive(input, length, delimitiers = ['\n\n', '\n', ' ', '']) { const delim = delimitiers[0] ?? ''; diff --git a/server.js b/server.js index 2a40f7e60..96b3147a0 100644 --- a/server.js +++ b/server.js @@ -1454,7 +1454,6 @@ app.post("/getallchatsofcharacter", jsonParser, function (request, response) { } // filter for JSON files - console.log('looking for JSONL files'); const jsonFiles = files.filter(file => path.extname(file) === '.jsonl'); // sort the files by name @@ -1496,6 +1495,7 @@ app.post("/getallchatsofcharacter", jsonParser, function (request, response) { chatData[i]['file_size'] = fileSizeInKB; chatData[i]['chat_items'] = itemCounter - 1; chatData[i]['mes'] = jsonData['mes'] || '[The chat is empty]'; + chatData[i]['last_mes'] = jsonData['send_date'] || Date.now(); } } if (ii === 0) {