mirror of
https://github.com/MAKS-IT-COM/maksit-postclient.git
synced 2026-09-30 03:18:09 +02:00
(feature): index keyword and meaning progress across all mailboxes
This commit is contained in:
parent
7198014a21
commit
adee0696ba
@ -6,6 +6,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/) and [Semantic
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.3.5] - 2026-09-20
|
||||||
|
|
||||||
|
Index status covers every mailbox, not one folder or one account.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Status bar keyword and meaning index lines show one done / total across all mailboxes, not the folder or mailbox currently being scanned.
|
||||||
|
|
||||||
## [0.3.4] - 2026-09-20
|
## [0.3.4] - 2026-09-20
|
||||||
|
|
||||||
Status bar index lines no longer flash between batches.
|
Status bar index lines no longer flash between batches.
|
||||||
|
|||||||
@ -30,6 +30,11 @@
|
|||||||
<display_length compare="ge">768</display_length>
|
<display_length compare="ge">768</display_length>
|
||||||
</requires>
|
</requires>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="0.3.5" date="2026-09-20">
|
||||||
|
<description>
|
||||||
|
<p>Keyword and meaning index status shows one done / total across all mailboxes, not the current folder or a single account.</p>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="0.3.4" date="2026-09-20">
|
<release version="0.3.4" date="2026-09-20">
|
||||||
<description>
|
<description>
|
||||||
<p>Status bar indexing and meaning-index lines stay visible between batches instead of flashing.</p>
|
<p>Status bar indexing and meaning-index lines stay visible between batches instead of flashing.</p>
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Version>0.3.4</Version>
|
<Version>0.3.5</Version>
|
||||||
<Product>Postclient</Product>
|
<Product>Postclient</Product>
|
||||||
<AssemblyTitle>Postclient</AssemblyTitle>
|
<AssemblyTitle>Postclient</AssemblyTitle>
|
||||||
<Authors>Maksym Sadovnychyy</Authors>
|
<Authors>Maksym Sadovnychyy</Authors>
|
||||||
|
|||||||
@ -197,13 +197,21 @@ public sealed class SemanticSearchService : ISemanticSearchService {
|
|||||||
await DelayAsync(TimeSpan.FromMilliseconds(800), token).ConfigureAwait(false);
|
await DelayAsync(TimeSpan.FromMilliseconds(800), token).ConfigureAwait(false);
|
||||||
if (_archive.PendingEmbeddings(EmbeddingModelSpec.Id, 1, ready).Count > 0)
|
if (_archive.PendingEmbeddings(EmbeddingModelSpec.Id, 1, ready).Count > 0)
|
||||||
continue;
|
continue;
|
||||||
|
var leftAll = _archive.EmbeddingPendingCount(EmbeddingModelSpec.Id);
|
||||||
|
var doneAll = _archive.EmbeddingCount(EmbeddingModelSpec.Id);
|
||||||
|
if (leftAll > 0) {
|
||||||
|
SetStatus(MailIndexProgress.MeaningLine(doneAll, doneAll + leftAll), ready: true);
|
||||||
|
await WaitAsync(token).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SetStatus("", ready: true);
|
SetStatus("", ready: true);
|
||||||
await WaitAsync(token).ConfigureAwait(false);
|
await WaitAsync(token).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var left = _archive.EmbeddingPendingCount(EmbeddingModelSpec.Id, ready);
|
var left = _archive.EmbeddingPendingCount(EmbeddingModelSpec.Id);
|
||||||
var done = _archive.EmbeddingCount(EmbeddingModelSpec.Id, ready);
|
var done = _archive.EmbeddingCount(EmbeddingModelSpec.Id);
|
||||||
SetStatus(MailIndexProgress.MeaningLine(done, done + left), ready: true);
|
SetStatus(MailIndexProgress.MeaningLine(done, done + left), ready: true);
|
||||||
foreach (var item in pending) {
|
foreach (var item in pending) {
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
|
|||||||
@ -105,6 +105,14 @@ public sealed class MailArchiveCatalog : IDisposable {
|
|||||||
public int MessageCount(string mailboxId) =>
|
public int MessageCount(string mailboxId) =>
|
||||||
OffUi(() => TryGet(mailboxId)?.MessageCount(mailboxId) ?? 0);
|
OffUi(() => TryGet(mailboxId)?.MessageCount(mailboxId) ?? 0);
|
||||||
|
|
||||||
|
public int TotalMessageCount() =>
|
||||||
|
OffUi(() => {
|
||||||
|
var count = 0;
|
||||||
|
foreach (var pair in _stores)
|
||||||
|
count += pair.Value.MessageCount(pair.Key);
|
||||||
|
return count;
|
||||||
|
});
|
||||||
|
|
||||||
public int RebuildKeywordIndex(string mailboxId) =>
|
public int RebuildKeywordIndex(string mailboxId) =>
|
||||||
OffUi(() => TryGet(mailboxId)?.RebuildKeywordIndex() ?? 0);
|
OffUi(() => TryGet(mailboxId)?.RebuildKeywordIndex() ?? 0);
|
||||||
|
|
||||||
|
|||||||
@ -38,7 +38,6 @@ public partial class MainViewModel : ObservableObject, IAsyncDisposable {
|
|||||||
private readonly HashSet<string> _catalogFolders = new(StringComparer.OrdinalIgnoreCase);
|
private readonly HashSet<string> _catalogFolders = new(StringComparer.OrdinalIgnoreCase);
|
||||||
private const int IndexCatalogChunk = 2_000;
|
private const int IndexCatalogChunk = 2_000;
|
||||||
private const int IndexUiChunk = 250;
|
private const int IndexUiChunk = 250;
|
||||||
private int _indexBodyTotal;
|
|
||||||
private readonly StatusLineHold _indexHold;
|
private readonly StatusLineHold _indexHold;
|
||||||
private readonly StatusLineHold _semanticHold;
|
private readonly StatusLineHold _semanticHold;
|
||||||
private bool _uiReady;
|
private bool _uiReady;
|
||||||
@ -3756,10 +3755,10 @@ public partial class MainViewModel : ObservableObject, IAsyncDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureIndexing(string mailboxId) {
|
private void EnsureIndexing(string mailboxId) {
|
||||||
if (string.IsNullOrWhiteSpace(mailboxId))
|
if (!string.IsNullOrWhiteSpace(mailboxId))
|
||||||
return;
|
_indexMailboxes.Add(mailboxId);
|
||||||
_indexMailboxes.Add(mailboxId);
|
TrackConnectedMailboxes();
|
||||||
if (_backfill is not null)
|
if (_backfill is not null || _indexMailboxes.Count == 0)
|
||||||
return;
|
return;
|
||||||
StartIndexing();
|
StartIndexing();
|
||||||
}
|
}
|
||||||
@ -3799,7 +3798,10 @@ public partial class MainViewModel : ObservableObject, IAsyncDisposable {
|
|||||||
try {
|
try {
|
||||||
while (!token.IsCancellationRequested) {
|
while (!token.IsCancellationRequested) {
|
||||||
await WaitIfIndexingPausedAsync(token).ConfigureAwait(false);
|
await WaitIfIndexingPausedAsync(token).ConfigureAwait(false);
|
||||||
var snapshot = await Dispatcher.UIThread.InvokeAsync(CaptureIndexSnapshot);
|
var (snapshot, readyIds) = await Dispatcher.UIThread.InvokeAsync(() => {
|
||||||
|
TrackConnectedMailboxes();
|
||||||
|
return (CaptureIndexSnapshot(), ReadyMailboxIds());
|
||||||
|
});
|
||||||
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
||||||
if (snapshot.Count == 0)
|
if (snapshot.Count == 0)
|
||||||
break;
|
break;
|
||||||
@ -3824,11 +3826,6 @@ public partial class MainViewModel : ObservableObject, IAsyncDisposable {
|
|||||||
pendingFolders = true;
|
pendingFolders = true;
|
||||||
cataloged = true;
|
cataloged = true;
|
||||||
await WaitIfIndexingPausedAsync(token).ConfigureAwait(false);
|
await WaitIfIndexingPausedAsync(token).ConfigureAwait(false);
|
||||||
var folderDone = box.CatalogedFolders.Count;
|
|
||||||
await SetIndexLineAsync(
|
|
||||||
MailIndexProgress.Line(folderDone, Math.Max(box.Folders.Count, 1), folder.Name),
|
|
||||||
token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
var known = _archive.Uids(box.Id, folder.FullName);
|
var known = _archive.Uids(box.Id, folder.FullName);
|
||||||
var listed = await box.Session
|
var listed = await box.Session
|
||||||
.ListMessagesAsync(folder.FullName, known, token, IndexCatalogChunk)
|
.ListMessagesAsync(folder.FullName, known, token, IndexCatalogChunk)
|
||||||
@ -3837,6 +3834,8 @@ public partial class MainViewModel : ObservableObject, IAsyncDisposable {
|
|||||||
continue;
|
continue;
|
||||||
var sync = listed.Value ?? new MailFolderSync();
|
var sync = listed.Value ?? new MailFolderSync();
|
||||||
ApplyFolderSync(box.Id, folder.FullName, sync);
|
ApplyFolderSync(box.Id, folder.FullName, sync);
|
||||||
|
await SetIndexLineAsync(GlobalIndexLine(), token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
var counts = _archive.FolderCounts(box.Id, folder.FullName);
|
var counts = _archive.FolderCounts(box.Id, folder.FullName);
|
||||||
await UiAsync(() => RefreshIndexedFolderCounts(box.Id, folder.FullName, counts))
|
await UiAsync(() => RefreshIndexedFolderCounts(box.Id, folder.FullName, counts))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
@ -3855,10 +3854,6 @@ public partial class MainViewModel : ObservableObject, IAsyncDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await WaitIfIndexingPausedAsync(token).ConfigureAwait(false);
|
await WaitIfIndexingPausedAsync(token).ConfigureAwait(false);
|
||||||
var readyIds = snapshot
|
|
||||||
.Where(s => IsInitialSyncComplete(s.Id))
|
|
||||||
.Select(s => s.Id)
|
|
||||||
.ToList();
|
|
||||||
var pending = readyIds.Count == 0 ? [] : _archive.MissingBodies(readyIds, 16);
|
var pending = readyIds.Count == 0 ? [] : _archive.MissingBodies(readyIds, 16);
|
||||||
if (pending.Count == 0) {
|
if (pending.Count == 0) {
|
||||||
if (!cataloged)
|
if (!cataloged)
|
||||||
@ -3866,11 +3861,7 @@ public partial class MainViewModel : ObservableObject, IAsyncDisposable {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var left = _archive.MissingBodyCount(readyIds);
|
await SetIndexLineAsync(GlobalIndexLine(), token)
|
||||||
if (_indexBodyTotal < left)
|
|
||||||
_indexBodyTotal = left;
|
|
||||||
var done = Math.Max(0, _indexBodyTotal - left);
|
|
||||||
await SetIndexLineAsync(MailIndexProgress.Line(done, _indexBodyTotal), token)
|
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
var fetched = 0;
|
var fetched = 0;
|
||||||
foreach (var item in pending) {
|
foreach (var item in pending) {
|
||||||
@ -3916,7 +3907,6 @@ public partial class MainViewModel : ObservableObject, IAsyncDisposable {
|
|||||||
var again = _indexMailboxes.Any(id => SessionFor(MailboxById(id)) is { IsConnected: true });
|
var again = _indexMailboxes.Any(id => SessionFor(MailboxById(id)) is { IsConnected: true });
|
||||||
if (!again) {
|
if (!again) {
|
||||||
_indexHold.Set("");
|
_indexHold.Set("");
|
||||||
_indexBodyTotal = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3962,6 +3952,29 @@ public partial class MainViewModel : ObservableObject, IAsyncDisposable {
|
|||||||
_indexHold.Set(line);
|
_indexHold.Set(line);
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
|
private void TrackConnectedMailboxes() {
|
||||||
|
foreach (var box in Mailboxes) {
|
||||||
|
if (SessionFor(box) is { IsConnected: true })
|
||||||
|
_indexMailboxes.Add(box.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> ReadyMailboxIds() {
|
||||||
|
var ids = new List<string>();
|
||||||
|
foreach (var box in Mailboxes) {
|
||||||
|
if (IsInitialSyncComplete(box.Id))
|
||||||
|
ids.Add(box.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GlobalIndexLine() {
|
||||||
|
var total = _archive.TotalMessageCount();
|
||||||
|
var missing = _archive.MissingBodyCount();
|
||||||
|
return MailIndexProgress.Line(Math.Max(0, total - missing), total);
|
||||||
|
}
|
||||||
|
|
||||||
private MailboxAccount? MailboxById(string mailboxId) =>
|
private MailboxAccount? MailboxById(string mailboxId) =>
|
||||||
Mailboxes.FirstOrDefault(m => m.Id.Equals(mailboxId, StringComparison.OrdinalIgnoreCase))
|
Mailboxes.FirstOrDefault(m => m.Id.Equals(mailboxId, StringComparison.OrdinalIgnoreCase))
|
||||||
?? _files.Current.FindMailbox(mailboxId);
|
?? _files.Current.FindMailbox(mailboxId);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user