Skip to content

Commit e98df90

Browse files
committed
Handle creating issue from list
1 parent 8eb1f59 commit e98df90

File tree

3 files changed

+188
-57
lines changed

3 files changed

+188
-57
lines changed

apple/InlineIOS/Chat/UIMessageView.swift

+85-24
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,32 @@ class UIMessageView: UIView {
323323

324324
bubbleView.addInteraction(interaction)
325325
}
326+
327+
func extractListItems(from message: String) -> [String] {
328+
let pattern = #"^\s*-\s+(.*)$"#
329+
guard let regex = try? NSRegularExpression(pattern: pattern, options: [.anchorsMatchLines, .caseInsensitive]) else {
330+
return []
331+
}
332+
333+
var items: [String] = []
334+
let nsString = message as NSString
335+
336+
regex.enumerateMatches(in: message, options: [], range: NSRange(location: 0, length: message.utf16.count)) { match, _, _ in
337+
guard let match = match, match.numberOfRanges > 1 else { return }
338+
339+
let contentRange = match.range(at: 1)
340+
if contentRange.location != NSNotFound {
341+
let content = nsString.substring(with: contentRange)
342+
.trimmingCharacters(in: .whitespacesAndNewlines)
343+
344+
if !content.isEmpty {
345+
items.append(content)
346+
}
347+
}
348+
}
349+
350+
return items
351+
}
326352
}
327353

328354
// MARK: - Context Menu
@@ -353,6 +379,7 @@ extension UIMessageView: UIContextMenuInteractionDelegate {
353379

354380
let createIssueAction = UIAction(title: "Create Linear issue") { _ in
355381

382+
let items = self.extractListItems(from: self.message.text ?? "")
356383
Task { @MainActor in
357384
do {
358385
let result = try await ApiClient.shared.getIntegrations(userId: Auth.shared.getCurrentUserId() ?? 0)
@@ -363,40 +390,74 @@ extension UIMessageView: UIContextMenuInteractionDelegate {
363390
systemImage: "link.circle"
364391
)
365392
} else {
366-
ToastManager.shared.showToast(
367-
"Creating Linear issue...",
368-
type: .loading,
369-
systemImage: "circle.dotted"
370-
)
371-
372-
do {
373-
let result = try await ApiClient.shared.createLinearIssue(
374-
text: self.message.text ?? "",
375-
messageId: self.message.messageId,
376-
chatId: self.message.chatId
393+
if items.count > 1 {
394+
ToastManager.shared.showToast(
395+
"Creating Linear issues...",
396+
type: .loading,
397+
systemImage: "circle.dotted"
377398
)
378399

400+
for item in items {
401+
print("item is \(item)")
402+
do {
403+
let result = try await ApiClient.shared.createLinearIssue(
404+
text: item,
405+
messageId: self.message.messageId,
406+
chatId: self.message.chatId
407+
)
408+
} catch {
409+
print("FAILED to create issue \(error)")
410+
ToastManager.shared.hideToast()
411+
ToastManager.shared.showToast(
412+
"Failed to create issue",
413+
type: .info,
414+
systemImage: "xmark.circle.fill"
415+
)
416+
}
417+
}
379418
ToastManager.shared.showToast(
380-
"Issue created",
419+
"\(items.count) Issues created",
381420
type: .success,
382-
systemImage: "checkmark.circle.fill",
383-
action: {
384-
if let url = URL(string: result.link ?? "") {
385-
UIApplication.shared.open(url)
386-
}
387-
},
388-
actionTitle: "Open"
421+
systemImage: "checkmark.circle.fill"
389422
)
390-
} catch {
391-
print("FAILED to create issue \(error)")
423+
424+
} else {
392425
ToastManager.shared.showToast(
393-
"Failed to create issue",
394-
type: .info,
395-
systemImage: "xmark.circle.fill"
426+
"Creating Linear issue...",
427+
type: .loading,
428+
systemImage: "circle.dotted"
396429
)
430+
431+
do {
432+
let result = try await ApiClient.shared.createLinearIssue(
433+
text: self.message.text ?? "",
434+
messageId: self.message.messageId,
435+
chatId: self.message.chatId
436+
)
437+
ToastManager.shared.showToast(
438+
"Issue created",
439+
type: .success,
440+
systemImage: "checkmark.circle.fill",
441+
action: {
442+
if let url = URL(string: result.link ?? "") {
443+
UIApplication.shared.open(url)
444+
}
445+
},
446+
actionTitle: "Open"
447+
)
448+
} catch {
449+
print("FAILED to create issue \(error)")
450+
ToastManager.shared.hideToast()
451+
ToastManager.shared.showToast(
452+
"Failed to create issue",
453+
type: .info,
454+
systemImage: "xmark.circle.fill"
455+
)
456+
}
397457
}
398458
}
399459
} catch {
460+
ToastManager.shared.hideToast()
400461
print("Failed to get integrations \(error)")
401462
}
402463
}

apple/InlineIOS/Localizable.xcstrings

+9
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
},
2929
"%lld members" : {
3030

31+
},
32+
"💬" : {
33+
3134
},
3235
"Accent Color" : {
3336

@@ -40,6 +43,9 @@
4043
},
4144
"Add a caption..." : {
4245

46+
},
47+
"Add a space or start a chat with someone to get started." : {
48+
4349
},
4450
"Add Member" : {
4551

@@ -402,6 +408,9 @@
402408
},
403409
"No archived chats" : {
404410

411+
},
412+
"No chats or spaces" : {
413+
405414
},
406415
"No messages yet" : {
407416

server/src/methods/createLinearIssue.ts

+94-33
Original file line numberDiff line numberDiff line change
@@ -41,41 +41,102 @@ export const handler = async (
4141
const linearUsers = await getLinearUsers({ userId: currentUserId })
4242

4343
const message = `
44-
You are an expert linguist creating accurate task titles from messages in any language. Follow these steps:
45-
the message is : ${text}.
46-
2. TITLE CREATION:
47-
a. Start with simple, human-action verb (e.g., "Fix", "Update", "Review")
48-
b. Maintain original message's key detail density
49-
c. Strictly avoid AI-related terms like "optimize", "leverage", "streamline"
50-
d. Should be sentence case.
51-
e. Make sure you added the core issue (what is the issue) and how this issue was exactly created in title.
52-
eg. Message: Dena please fix open DM chats on notification click, it's working randomly for me.
53-
Title: Fix open DM on notifications click
54-
Message2: @Mo this message failed to translate. It was a long message from a zh user
55-
Title2: Fix translation failure on long zh messages
56-
57-
3. LINEAR INTEGRATION:
58-
Labels: ${JSON.stringify(labels.labels, null, 2)}
59-
Users: ${JSON.stringify(linearUsers.users, null, 2)}
60-
Match using semantic similarity thresholds >0.7
61-
62-
Return JSON with this exact structure:
63-
{
64-
"title": "<Translated/Original Text as Natural Task Title>",
65-
"description": "${text}",
66-
"labelIds": ["<Matching-Label-ID>"] || [],
67-
"assigneeId": "<@Mention-Matched-ID>" || ""
68-
}
69-
70-
Key Requirements:
71-
- Description must remain verbatim original text
72-
- Empty values allowed for missing matches
73-
- Title verb must be everyday action word
74-
- Never explain your reasoning
75-
`
44+
You are an expert linguist creating accurate task titles from messages in any language. Follow these steps:
45+
46+
1. INPUT MESSAGE: "${text}"
47+
48+
2. TITLE CREATION RULES:
49+
a. Make sure to start with an everyday action verb (e.g., "Fix", "Update", "Add", "Remove")
50+
b. Use sentence case (First word capitalized)
51+
c. Include specific issue reproduction steps or feature context
52+
d. Maintain key information density from original message
53+
e. PROHIBITED: AI jargon ("optimize", "leverage", "streamline", "capability")
54+
f. IGNORE time annotations: "-2h", "(2h)", etc.
55+
g.Be careful to not count everything as issue.
56+
t. Make sure you do not add any of these words in sentences : "feature" or "functionality" but you can count them as label. eg. this is wrong: Add SMS sign in **feature**
57+
s. Make sure you are not returning the sentence with it's own verb without making it task title and adding the action verb in the beginning of the title eg.
58+
Message: edit message
59+
title should be "Add edit message" no "Edit message"
60+
61+
TITLE FORMAT EXAMPLES:
62+
Message: "Dena please fix open DM chats on notification click, it's working randomly for me."
63+
Title: "Fix random DM opening behavior on notification clicks"
64+
65+
Message: "@Mo this message failed to translate. It was a long message from a zh user"
66+
Title: "Fix translation failures for long Chinese messages"
67+
68+
Message: "video sending"
69+
Title: "Add video"
70+
71+
3. ASSIGNEE DETECTION:
72+
- Trigger on exact @ mentions
73+
- Match against provided user list
74+
Users: ${JSON.stringify(linearUsers.users, null, 2)}
75+
76+
4. LABEL MATCHING:
77+
- Use semantic similarity (threshold >0.7)
78+
- Match against provided labels
79+
Labels: ${JSON.stringify(labels.labels, null, 2)}
80+
81+
OUTPUT FORMAT:
82+
{
83+
"title": "<Action Verb + Specific Context>",
84+
"description": "${text}",
85+
"labelIds": ["<Matching-Label-ID>"] || [],
86+
"assigneeId": "<Mentioned-User-ID>" || ""
87+
}
88+
89+
REQUIREMENTS:
90+
- Description must be exact original text
91+
- Empty arrays/strings allowed for unmatched fields
92+
- Title must use concrete action verbs
93+
- No explanations in output
94+
`
95+
96+
// const message = `
97+
// You are an expert linguist creating accurate task titles from messages in any language. Follow these steps:
98+
// the message is : ${text}.
99+
// 2. TITLE CREATION:
100+
// a. Start with simple, human-action verb (e.g., "Fix", "Update", "Add")
101+
// b. Maintain original message's key detail density
102+
// c. Strictly avoid AI-related terms like "optimize", "leverage", "streamline"
103+
// d. Should be sentence case.
104+
// e. Make sure you added the core part (what is the issue or feature to add) and if it's an issue how this issue was exactly created in title.
105+
// eg. Message: Dena please fix open DM chats on notification click, it's working randomly for me.
106+
// Title: Fix open DM on notifications click
107+
// Message2: @Mo this message failed to translate. It was a long message from a zh user
108+
// Title2: Fix translation failure on long zh messages
109+
// Message3: handle video messages
110+
// Title3: Add video message
111+
// f. if the text was like this: Load more & paginate -  2h. or like this: Load more & paginate (2h) ignore the - 2h / (2h) part in title generation
112+
113+
// 3. LINEAR INTEGRATION:
114+
// Labels: ${JSON.stringify(labels.labels, null, 2)}
115+
// Users: ${JSON.stringify(linearUsers.users, null, 2)}
116+
// Match using semantic similarity thresholds >0.7
117+
118+
// Return JSON with this exact structure:
119+
// {
120+
// "title": "<Translated/Original Text as Natural Task Title>",
121+
// "description": "${text}",
122+
// "labelIds": ["<Matching-Label-ID>"] || [],
123+
// "assigneeId": "<@Mention-Matched-ID>" || ""
124+
// }
125+
126+
// Key Requirements:
127+
// - Description must remain verbatim original text
128+
// - Empty values allowed for missing matches
129+
// - Title verb must be everyday action word
130+
// - Never explain your reasoning
131+
// `
76132

77133
const response = await openaiClient?.chat.completions.create({
78-
messages: [{ role: "user", content: message }],
134+
messages: [
135+
{
136+
role: "user",
137+
content: message + "\n\nRespond with valid JSON using the required format.",
138+
},
139+
],
79140
model: "gpt-4o",
80141
response_format: { type: "json_object" },
81142
})

0 commit comments

Comments
 (0)