@codingfactory/socialkit-vue 0.7.0 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -241,6 +241,7 @@ export function createDiscussionStoreDefinition(config: DiscussionStoreConfig) {
241
241
  const threads = ref<Thread[]>([])
242
242
  const currentThread = ref<Thread | null>(null)
243
243
  const replies = ref<Reply[]>([])
244
+ const currentReplySort = ref<'best' | 'top' | 'new' | 'controversial'>('best')
244
245
 
245
246
  const spacesLoadingState = createLoadingState()
246
247
  const threadsLoadingState = createLoadingState()
@@ -322,6 +323,78 @@ export function createDiscussionStoreDefinition(config: DiscussionStoreConfig) {
322
323
  }
323
324
  }
324
325
 
326
+ function hasQuotedReplyBody(quotedReply: Reply['quoted_reply']): boolean {
327
+ return typeof quotedReply?.body === 'string' && quotedReply.body.trim().length > 0
328
+ }
329
+
330
+ function hasQuotedReplyAuthorName(quotedReply: Reply['quoted_reply']): boolean {
331
+ return typeof quotedReply?.author?.name === 'string' && quotedReply.author.name.trim().length > 0
332
+ }
333
+
334
+ function buildQuotedReplyFromReply(sourceReply: Reply | null | undefined): Reply['quoted_reply'] {
335
+ if (!sourceReply?.id || !sourceReply.author_id || typeof sourceReply.body !== 'string' || sourceReply.body.trim().length === 0) {
336
+ return null
337
+ }
338
+
339
+ return {
340
+ id: sourceReply.id,
341
+ author_id: sourceReply.author_id,
342
+ author: sourceReply.author
343
+ ? {
344
+ id: sourceReply.author.id,
345
+ name: sourceReply.author.name,
346
+ ...(sourceReply.author.handle ? { handle: sourceReply.author.handle } : {}),
347
+ }
348
+ : null,
349
+ body: sourceReply.body,
350
+ }
351
+ }
352
+
353
+ function resolveQuotedReply(reply: Reply, existingReply?: Reply | null): Reply['quoted_reply'] {
354
+ const quotedReplyId = typeof reply.quoted_reply_id === 'string'
355
+ ? reply.quoted_reply_id.trim()
356
+ : ''
357
+
358
+ if (quotedReplyId.length === 0) {
359
+ return null
360
+ }
361
+
362
+ const incomingQuotedReply = reply.quoted_reply ?? null
363
+ if (hasQuotedReplyBody(incomingQuotedReply) && hasQuotedReplyAuthorName(incomingQuotedReply)) {
364
+ return incomingQuotedReply
365
+ }
366
+
367
+ const referencedReply = replies.value.find((candidate) => candidate.id === quotedReplyId)
368
+ const hydratedQuotedReply = buildQuotedReplyFromReply(referencedReply)
369
+ if (hydratedQuotedReply) {
370
+ return hydratedQuotedReply
371
+ }
372
+
373
+ const existingQuotedReply = existingReply?.quoted_reply ?? null
374
+ if (hasQuotedReplyBody(existingQuotedReply) && hasQuotedReplyAuthorName(existingQuotedReply)) {
375
+ return existingQuotedReply
376
+ }
377
+
378
+ return hasQuotedReplyBody(incomingQuotedReply) ? incomingQuotedReply : null
379
+ }
380
+
381
+ function normalizeReplyPayload(reply: Reply, existingReply?: Reply | null): Reply {
382
+ const normalized: Reply = { ...reply }
383
+ const hasQuotedReplyId = typeof normalized.quoted_reply_id === 'string'
384
+ && normalized.quoted_reply_id.trim().length > 0
385
+
386
+ if (!hasQuotedReplyId) {
387
+ normalized.quoted_reply_id = null
388
+ normalized.quoted_reply = null
389
+ normalized.quote_data = null
390
+ return normalized
391
+ }
392
+
393
+ normalized.quoted_reply = resolveQuotedReply(normalized, existingReply)
394
+
395
+ return normalized
396
+ }
397
+
325
398
  function mergeUniqueById<TItem extends { id: string }>(
326
399
  existingItems: TItem[],
327
400
  incomingItems: TItem[]
@@ -950,6 +1023,7 @@ export function createDiscussionStoreDefinition(config: DiscussionStoreConfig) {
950
1023
  }
951
1024
 
952
1025
  const filteredReplyBatch = filterOpeningReplies(loadedReplyBatch)
1026
+ currentReplySort.value = sortBy
953
1027
 
954
1028
  if (cursor) {
955
1029
  replies.value = mergeUniqueById(replies.value, filteredReplyBatch)
@@ -998,6 +1072,70 @@ export function createDiscussionStoreDefinition(config: DiscussionStoreConfig) {
998
1072
  }
999
1073
  }
1000
1074
 
1075
+ function insertTopLevelReply(reply: Reply): void {
1076
+ if (currentReplySort.value === 'new') {
1077
+ replies.value.unshift(reply)
1078
+ return
1079
+ }
1080
+
1081
+ replies.value.push(reply)
1082
+ }
1083
+
1084
+ function insertReplyIntoActiveThread(reply: Reply, parentReplyId?: string | null): boolean {
1085
+ const existingReplyIndex = replies.value.findIndex((candidate) => candidate.id === reply.id)
1086
+
1087
+ if (existingReplyIndex !== -1) {
1088
+ const existingReply = replies.value[existingReplyIndex]
1089
+ if (existingReply) {
1090
+ replies.value[existingReplyIndex] = {
1091
+ ...existingReply,
1092
+ ...reply,
1093
+ }
1094
+ }
1095
+
1096
+ return false
1097
+ }
1098
+
1099
+ if (parentReplyId) {
1100
+ const parentIndex = replies.value.findIndex((candidate) => candidate.id === parentReplyId)
1101
+ if (parentIndex !== -1) {
1102
+ const parent = replies.value[parentIndex]
1103
+ const parentDepth = parent?.depth ?? parent?.display_depth ?? 0
1104
+
1105
+ reply.depth = parentDepth + 1
1106
+ reply.display_depth = parentDepth + 1
1107
+ reply.parent_reply_id = parentReplyId
1108
+
1109
+ let insertIndex = parentIndex + 1
1110
+ while (insertIndex < replies.value.length) {
1111
+ const nextReply = replies.value[insertIndex]
1112
+ const nextDepth = nextReply?.depth ?? nextReply?.display_depth ?? 0
1113
+ if (nextDepth <= parentDepth) {
1114
+ break
1115
+ }
1116
+ insertIndex += 1
1117
+ }
1118
+
1119
+ replies.value.splice(insertIndex, 0, reply)
1120
+
1121
+ if (parent) {
1122
+ parent.reply_count = (parent.reply_count ?? 0) + 1
1123
+ parent.children_count = (parent.children_count ?? 0) + 1
1124
+ }
1125
+
1126
+ return true
1127
+ }
1128
+
1129
+ replies.value.push(reply)
1130
+ return true
1131
+ }
1132
+
1133
+ reply.depth = 0
1134
+ reply.display_depth = 0
1135
+ insertTopLevelReply(reply)
1136
+ return true
1137
+ }
1138
+
1001
1139
  async function createThread(
1002
1140
  spaceSlug: string,
1003
1141
  input: CreateThreadInput
@@ -1144,7 +1282,8 @@ export function createDiscussionStoreDefinition(config: DiscussionStoreConfig) {
1144
1282
  })
1145
1283
 
1146
1284
  const responseData = toRecord(response.data)
1147
- const newReply = (responseData?.data ?? null) as Reply | null
1285
+ const createdReply = (responseData?.data ?? null) as Reply | null
1286
+ const newReply = createdReply ? normalizeReplyPayload(createdReply) : null
1148
1287
 
1149
1288
  if (newReply?.id) {
1150
1289
  locallyCreatedReplyIds.add(newReply.id)
@@ -1153,52 +1292,7 @@ export function createDiscussionStoreDefinition(config: DiscussionStoreConfig) {
1153
1292
  let didInsertReply = false
1154
1293
 
1155
1294
  if (newReply) {
1156
- const existingReplyIndex = replies.value.findIndex((reply) => reply.id === newReply.id)
1157
-
1158
- if (existingReplyIndex !== -1) {
1159
- const existingReply = replies.value[existingReplyIndex]
1160
- if (existingReply) {
1161
- replies.value[existingReplyIndex] = {
1162
- ...existingReply,
1163
- ...newReply,
1164
- }
1165
- }
1166
- } else if (input.parent_id) {
1167
- const parentIndex = replies.value.findIndex((reply) => reply.id === input.parent_id)
1168
- if (parentIndex !== -1) {
1169
- const parent = replies.value[parentIndex]
1170
- const parentDepth = parent?.depth ?? parent?.display_depth ?? 0
1171
- newReply.depth = parentDepth + 1
1172
- newReply.display_depth = parentDepth + 1
1173
- newReply.parent_reply_id = input.parent_id
1174
-
1175
- let insertIndex = parentIndex + 1
1176
- while (insertIndex < replies.value.length) {
1177
- const reply = replies.value[insertIndex]
1178
- const replyDepth = reply?.depth ?? reply?.display_depth ?? 0
1179
- if (replyDepth <= parentDepth) {
1180
- break
1181
- }
1182
- insertIndex += 1
1183
- }
1184
-
1185
- replies.value.splice(insertIndex, 0, newReply)
1186
- didInsertReply = true
1187
-
1188
- if (parent) {
1189
- parent.reply_count = (parent.reply_count ?? 0) + 1
1190
- parent.children_count = (parent.children_count ?? 0) + 1
1191
- }
1192
- } else {
1193
- replies.value.push(newReply)
1194
- didInsertReply = true
1195
- }
1196
- } else {
1197
- newReply.depth = 0
1198
- newReply.display_depth = 0
1199
- replies.value.push(newReply)
1200
- didInsertReply = true
1201
- }
1295
+ didInsertReply = insertReplyIntoActiveThread(newReply, input.parent_id)
1202
1296
  }
1203
1297
 
1204
1298
  if (didInsertReply && currentThread.value?.id === threadId) {
@@ -1380,29 +1474,18 @@ export function createDiscussionStoreDefinition(config: DiscussionStoreConfig) {
1380
1474
  return
1381
1475
  }
1382
1476
 
1383
- const normalizedPayload: Reply = (() => {
1384
- const normalized: Reply = { ...payload }
1477
+ const existingReply = replies.value.find((candidate) => candidate.id === payload.id) ?? null
1478
+ const normalizedPayload = normalizeReplyPayload(payload, existingReply)
1385
1479
 
1386
- const quotedReplyId = normalized.quoted_reply_id
1387
- const hasQuotedReplyId = typeof quotedReplyId === 'string' && quotedReplyId.trim().length > 0
1388
- if (!hasQuotedReplyId) {
1389
- normalized.quoted_reply_id = null
1390
- normalized.quoted_reply = null
1391
- normalized.quote_data = null
1392
- }
1393
-
1394
- return normalized
1395
- })()
1480
+ const didInsertReply = insertReplyIntoActiveThread(
1481
+ normalizedPayload,
1482
+ normalizedPayload.parent_reply_id ?? null
1483
+ )
1396
1484
 
1397
- const already = replies.value.some((reply) => reply.id === payload.id)
1398
- if (!already) {
1399
- replies.value.push(normalizedPayload)
1400
-
1401
- if (currentThread.value && payload.id && !realtimeCountedReplyIds.has(payload.id)) {
1402
- realtimeCountedReplyIds.add(payload.id)
1403
- currentThread.value.reply_count = (currentThread.value.reply_count || 0) + 1
1404
- currentThread.value.last_activity_at = payload.created_at ?? new Date().toISOString()
1405
- }
1485
+ if (didInsertReply && currentThread.value && payload.id && !realtimeCountedReplyIds.has(payload.id)) {
1486
+ realtimeCountedReplyIds.add(payload.id)
1487
+ currentThread.value.reply_count = (currentThread.value.reply_count || 0) + 1
1488
+ currentThread.value.last_activity_at = payload.created_at ?? new Date().toISOString()
1406
1489
  }
1407
1490
  }
1408
1491