Make conversation queues mutually exclusive
This commit is contained in:
1 parent
3bb1dda6bb
commit
2447563c60
20 files changed
+386
-220
No files matched your search
@@ -21,12 +21,12 @@ import {
|
||||
CONVERSATION_SORTS,
|
||||
DEFAULT_CONVERSATION_FILTERS,
|
||||
accountOptions,
|
||||
conversationQueue,
|
||||
filterCounts,
|
||||
isUnread,
|
||||
manualFlowLabel,
|
||||
needsReply,
|
||||
partitionConversations,
|
||||
triageStatus,
|
||||
type ConversationFilter,
|
||||
type ConversationFilters,
|
||||
type ConversationSort,
|
||||
@@ -87,16 +87,19 @@ function sellerScanSummary(result: SellerScanResponse): string {
|
||||
return `${parts.join(' · ')}.`;
|
||||
}
|
||||
|
||||
/** Independent state chips. A conversation can be several of these at once. */
|
||||
/** One workflow chip plus independent presentation/ownership badges. */
|
||||
function conversationChips(conversation: ActiveChat, now: number): ConversationChip[] {
|
||||
const chips: ConversationChip[] = [];
|
||||
if (conversation.recipientAccountStatus === 'deleted') {
|
||||
chips.push({ label: 'Account deleted' });
|
||||
return chips;
|
||||
switch (conversationQueue(conversation)) {
|
||||
case 'needs-reply': chips.push({ label: 'Needs attention' }); break;
|
||||
case 'awaiting-reply': chips.push({ label: 'Waiting' }); break;
|
||||
case 'sellers': chips.push({ label: 'Seller' }); break;
|
||||
case 'snoozed': chips.push({ label: 'Snoozed' }); break;
|
||||
case 'done': chips.push({ label: 'Done' }); break;
|
||||
case 'account-deleted': chips.push({ label: 'Account deleted' }); break;
|
||||
case 'archived': chips.push({ label: 'Archived' }); break;
|
||||
}
|
||||
if (isUnread(conversation)) chips.push({ label: 'Unread' });
|
||||
if (needsReply(conversation)) chips.push({ label: 'Needs reply' });
|
||||
else if (conversation.isFromUs && triageStatus(conversation) === 'open') chips.push({ label: 'Waiting on them' });
|
||||
const manualLabel = manualFlowLabel(conversation);
|
||||
if (manualLabel) {
|
||||
chips.push({
|
||||
@@ -107,10 +110,8 @@ function conversationChips(conversation: ActiveChat, now: number): ConversationC
|
||||
: manualLabel,
|
||||
});
|
||||
}
|
||||
const status = triageStatus(conversation);
|
||||
if (status === 'snoozed') chips.push({ label: 'Snoozed' });
|
||||
if (status === 'done') chips.push({ label: 'Done' });
|
||||
if (needsReply(conversation) && conversationAge(conversation.lastMessageTime, now) !== 'fresh') {
|
||||
if (conversationQueue(conversation) === 'needs-reply'
|
||||
&& conversationAge(conversation.lastMessageTime, now) !== 'fresh') {
|
||||
chips.push({ label: 'Overdue' });
|
||||
}
|
||||
return chips;
|
||||
@@ -133,7 +134,6 @@ function ConversationRow({
|
||||
redditUsernameLabel(conversation.username),
|
||||
...chips.map((chip) => chip.hint ?? chip.label),
|
||||
relativeTime,
|
||||
...(conversation.sellerDetected ? ['Seller'] : []),
|
||||
...(conversation.hasReceivedImages ? ['Image received'] : []),
|
||||
...(conversation.readOnly ? ['Removed account, read-only'] : []),
|
||||
];
|
||||
@@ -186,7 +186,6 @@ function ConversationRow({
|
||||
Read-only
|
||||
</span>
|
||||
) : null}
|
||||
{conversation.sellerDetected ? <span className="conversation-seller-marker">Seller</span> : null}
|
||||
{conversation.hasReceivedImages ? <span className="conversation-image-marker">Image received</span> : null}
|
||||
<span className="conversation-list-profile"><RedditUsernameLink username={conversation.username} /></span>
|
||||
</span>
|
||||
@@ -235,7 +234,7 @@ export function ConversationsPage({
|
||||
|
||||
const allConversations = conversationsQuery.data ?? [];
|
||||
|
||||
const { visible: liveConversations, hiddenSellerCount } = useMemo(
|
||||
const { visible: liveConversations } = useMemo(
|
||||
() => partitionConversations(allConversations, activeFilters),
|
||||
[allConversations, activeFilters],
|
||||
);
|
||||
@@ -514,11 +513,27 @@ export function ConversationsPage({
|
||||
) : null}
|
||||
<label className="conversation-seller-toggle">
|
||||
<input
|
||||
checked={activeFilters.showSellers}
|
||||
onChange={(event) => updateFilters({ ...activeFilters, showSellers: event.target.checked })}
|
||||
checked={activeFilters.unreadOnly}
|
||||
onChange={(event) => updateFilters({ ...activeFilters, unreadOnly: event.target.checked })}
|
||||
type="checkbox"
|
||||
/>
|
||||
Show sellers
|
||||
Unread only
|
||||
</label>
|
||||
<label className="conversation-seller-toggle">
|
||||
<input
|
||||
checked={activeFilters.manualOnly}
|
||||
onChange={(event) => updateFilters({ ...activeFilters, manualOnly: event.target.checked })}
|
||||
type="checkbox"
|
||||
/>
|
||||
Manual only
|
||||
</label>
|
||||
<label className="conversation-seller-toggle">
|
||||
<input
|
||||
checked={activeFilters.imagesOnly}
|
||||
onChange={(event) => updateFilters({ ...activeFilters, imagesOnly: event.target.checked })}
|
||||
type="checkbox"
|
||||
/>
|
||||
Has images
|
||||
</label>
|
||||
</div>
|
||||
{visibleConversations.length > 0 ? (
|
||||
@@ -577,9 +592,6 @@ export function ConversationsPage({
|
||||
<div className="conversation-list-state">
|
||||
<strong>No matching conversations.</strong>
|
||||
<span>Try a different search or filter.</span>
|
||||
{hiddenSellerCount > 0 ? (
|
||||
<span>{hiddenSellerCount} hidden by the seller filter.</span>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
@@ -605,9 +617,6 @@ export function ConversationsPage({
|
||||
})}
|
||||
</ul>
|
||||
<p className="conversation-list-footer">
|
||||
{hiddenSellerCount > 0
|
||||
? `${hiddenSellerCount} hidden by the seller filter.`
|
||||
: null}
|
||||
<span className="conversation-shortcut-hint">
|
||||
j/k move · Enter opens · u toggles unread · e marks done · / searches
|
||||
</span>
|
||||
|
||||
Reference in new issue
Block a user