kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cd7cada2f86b4e866a15b4323bb8d6d7ab5bba8b 124 lines 3.4 kB view raw
1import { 2 findExternalLinksByTask, 3 updateExternalLink, 4} from "../../github/services/link-manager"; 5import { formatIssueBody } from "../../github/utils/format"; 6import type { PluginContext, TaskDescriptionChangedEvent } from "../../types"; 7import type { GiteaConfig } from "../config"; 8import { createGiteaClient } from "../utils/gitea-api"; 9 10type LinkSyncState = { 11 timestamp: string; 12 source: string; 13 value: string; 14}; 15 16type LinkMetadata = { 17 lastSync?: { 18 description?: LinkSyncState; 19 }; 20 [key: string]: unknown; 21}; 22 23export async function handleTaskDescriptionChanged( 24 event: TaskDescriptionChangedEvent, 25 context: PluginContext, 26): Promise<void> { 27 const config = context.config as GiteaConfig; 28 if (!config.baseUrl || !config.accessToken) { 29 return; 30 } 31 32 const { repositoryOwner, repositoryName } = config; 33 34 try { 35 const links = await findExternalLinksByTask(event.taskId); 36 const issueLink = links.find( 37 (link) => 38 link.integrationId === context.integrationId && 39 link.resourceType === "issue", 40 ); 41 42 if (!issueLink) { 43 return; 44 } 45 46 let metadata: LinkMetadata = {}; 47 if (issueLink.metadata) { 48 try { 49 metadata = JSON.parse(issueLink.metadata) as LinkMetadata; 50 } catch (error) { 51 console.warn( 52 "Failed to parse Gitea issue link metadata for description sync", 53 { 54 issueLinkId: issueLink.id, 55 taskId: issueLink.taskId, 56 metadata: issueLink.metadata, 57 error, 58 }, 59 ); 60 } 61 } 62 63 const lastDescSync = metadata.lastSync?.description; 64 const newDescNormalized = event.newDescription || ""; 65 66 if (lastDescSync) { 67 if ( 68 lastDescSync.value === newDescNormalized && 69 lastDescSync.source === "gitea" 70 ) { 71 console.log("Skipping description sync - already synced from Gitea"); 72 return; 73 } 74 75 const timeSinceLastSync = 76 Date.now() - new Date(lastDescSync.timestamp).getTime(); 77 if ( 78 timeSinceLastSync < 2000 && 79 lastDescSync.source === "gitea" && 80 newDescNormalized === lastDescSync.value 81 ) { 82 console.log( 83 `Skipping description sync - recent sync detected (${timeSinceLastSync}ms ago)`, 84 ); 85 return; 86 } 87 } 88 89 const client = createGiteaClient(config); 90 const issueNumber = Number.parseInt(issueLink.externalId, 10); 91 if (Number.isNaN(issueNumber)) { 92 console.warn("Skipping Gitea description sync for invalid issue number", { 93 issueLinkId: issueLink.id, 94 externalId: issueLink.externalId, 95 taskId: issueLink.taskId, 96 }); 97 return; 98 } 99 100 const formattedBody = formatIssueBody(event.newDescription, event.taskId); 101 102 await client.updateIssue(repositoryOwner, repositoryName, issueNumber, { 103 body: formattedBody, 104 }); 105 106 await updateExternalLink(issueLink.id, { 107 metadata: { 108 ...metadata, 109 lastSync: { 110 ...(metadata.lastSync ?? {}), 111 description: { 112 timestamp: new Date().toISOString(), 113 source: "kaneo", 114 value: newDescNormalized, 115 }, 116 }, 117 }, 118 }); 119 120 console.log(`Synced task description to Gitea issue #${issueNumber}`); 121 } catch (error) { 122 console.error("Failed to update Gitea issue description:", error); 123 } 124}