diff --git a/resources/html/reactView.html b/resources/html/reactView.html
index 3e0803df5..6a3cd2169 100644
--- a/resources/html/reactView.html
+++ b/resources/html/reactView.html
@@ -11,7 +11,7 @@
diff --git a/resources/html/reactWebview.html b/resources/html/reactWebview.html
index af6eb1fc4..010457e56 100644
--- a/resources/html/reactWebview.html
+++ b/resources/html/reactWebview.html
@@ -10,7 +10,7 @@
diff --git a/src/atlclients/clientManager.ts b/src/atlclients/clientManager.ts
index d3f6dbd40..e8b0206a6 100644
--- a/src/atlclients/clientManager.ts
+++ b/src/atlclients/clientManager.ts
@@ -153,6 +153,7 @@ export class ClientManager implements Disposable {
if (isOAuthInfo(info)) {
Logger.debug(`${tag}: creating client for ${site.baseApiUrl}`);
+ Logger.debug(`${tag}: Sites123: ${JSON.stringify(site)}`);
client = new JiraCloudClient(
site,
oauthJiraTransportFactory(site),
diff --git a/src/atlclients/strategyData.ts b/src/atlclients/strategyData.ts
index 64342b527..c62495c30 100644
--- a/src/atlclients/strategyData.ts
+++ b/src/atlclients/strategyData.ts
@@ -117,7 +117,7 @@ export class OAuthStrategyData {
accessibleResourcesURL: 'https://api.atlassian.com/oauth/token/accessible-resources',
callbackURL: remoteAuthConfig.callbackURL,
apiURL: 'api.atlassian.com',
- scope: 'read:jira-user read:jira-work write:jira-work offline_access manage:jira-project',
+ scope: 'read:jira-user read:jira-work write:jira-work offline_access manage:jira-project manage:jira-configuration manage:jira-webhook read:application-role:jira read:audit-log:jira read:avatar:jira write:avatar:jira delete:avatar:jira read:project.avatar:jira write:project.avatar:jira delete:project.avatar:jira read:dashboard:jira write:dashboard:jira delete:dashboard:jira read:dashboard.property:jira write:dashboard.property:jira delete:dashboard.property:jira read:filter:jira write:filter:jira delete:filter:jira read:filter.column:jira write:filter.column:jira delete:filter.column:jira read:filter.default-share-scope:jira write:filter.default-share-scope:jira read:group:jira write:group:jira delete:group:jira read:license:jira read:issue:jira write:issue:jira delete:issue:jira read:issue-meta:jira send:notification:jira read:attachment:jira write:attachment:jira delete:attachment:jira read:comment:jira write:comment:jira delete:comment:jira read:comment.property:jira write:comment.property:jira delete:comment.property:jira read:field:jira write:field:jira delete:field:jira read:field.default-value:jira write:field.default-value:jira read:field.option:jira write:field.option:jira delete:field.option:jira read:field-configuration-scheme:jira write:field-configuration-scheme:jira delete:field-configuration-scheme:jira read:custom-field-contextual-configuration:jira write:custom-field-contextual-configuration:jira read:field-configuration:jira write:field-configuration:jira delete:field-configuration:jira read:field.options:jira read:issue-link:jira write:issue-link:jira delete:issue-link:jira read:issue-link-type:jira write:issue-link-type:jira delete:issue-link-type:jira read:notification-scheme:jira read:priority:jira read:issue.property:jira write:issue.property:jira delete:issue.property:jira read:issue.remote-link:jira write:issue.remote-link:jira delete:issue.remote-link:jira read:resolution:jira read:issue-details:jira read:issue-security-scheme:jira read:issue-type:jira write:issue-type:jira delete:issue-type:jira read:issue-type-scheme:jira write:issue-type-scheme:jira delete:issue-type-scheme:jira read:issue-type-screen-scheme:jira write:issue-type-screen-scheme:jira delete:issue-type-screen-scheme:jira read:issue-type.property:jira write:issue-type.property:jira delete:issue-type.property:jira read:issue.watcher:jira write:issue.watcher:jira read:issue-worklog:jira write:issue-worklog:jira delete:issue-worklog:jira read:issue-worklog.property:jira write:issue-worklog.property:jira delete:issue-worklog.property:jira read:issue-field-values:jira read:issue-security-level:jira read:issue-status:jira read:issue-type-hierarchy:jira read:issue-type-transition:jira read:issue.changelog:jira read:issue.transition:jira write:issue.vote:jira read:issue-event:jira read:jira-expressions:jira read:user:jira read:user.columns:jira read:label:jira read:permission:jira write:permission:jira delete:permission:jira read:permission-scheme:jira write:permission-scheme:jira delete:permission-scheme:jira read:project:jira write:project:jira delete:project:jira read:project-category:jira write:project-category:jira delete:project-category:jira read:project.component:jira write:project.component:jira delete:project.component:jira read:project.property:jira write:project.property:jira delete:project.property:jira read:project-role:jira write:project-role:jira delete:project-role:jira read:project-version:jira write:project-version:jira delete:project-version:jira read:project.feature:jira write:project.feature:jira read:screen:jira write:screen:jira delete:screen:jira read:screen-scheme:jira write:screen-scheme:jira delete:screen-scheme:jira read:screen-field:jira read:screen-tab:jira write:screen-tab:jira delete:screen-tab:jira read:screenable-field:jira write:screenable-field:jira delete:screenable-field:jira read:issue.time-tracking:jira write:issue.time-tracking:jira read:user.property:jira write:user.property:jira delete:user.property:jira read:webhook:jira write:webhook:jira delete:webhook:jira read:workflow:jira write:workflow:jira delete:workflow:jira read:workflow-scheme:jira write:workflow-scheme:jira delete:workflow-scheme:jira read:status:jira read:workflow.property:jira write:workflow.property:jira delete:workflow.property:jira delete:async-task:jira read:instance-configuration:jira write:instance-configuration:jira read:jql:jira validate:jql:jira read:project-type:jira read:project.email:jira write:project.email:jira read:role:jira read:user-configuration:jira write:user-configuration:jira delete:user-configuration:jira read:email-address:jira',
authParams: {
audience: 'api.atlassian.com',
prompt: 'consent',
@@ -135,7 +135,7 @@ export class OAuthStrategyData {
accessibleResourcesURL: 'https://api.atlassian.com/oauth/token/accessible-resources',
callbackURL: 'http://127.0.0.1:31415/' + OAuthProvider.JiraCloud,
apiURL: 'api.atlassian.com',
- scope: 'read:jira-user read:jira-work write:jira-work offline_access manage:jira-project',
+ scope: 'read:jira-user read:jira-work write:jira-work offline_access manage:jira-project manage:jira-configuration manage:jira-webhook read:application-role:jira read:audit-log:jira read:avatar:jira write:avatar:jira delete:avatar:jira read:project.avatar:jira write:project.avatar:jira delete:project.avatar:jira read:dashboard:jira write:dashboard:jira delete:dashboard:jira read:dashboard.property:jira write:dashboard.property:jira delete:dashboard.property:jira read:filter:jira write:filter:jira delete:filter:jira read:filter.column:jira write:filter.column:jira delete:filter.column:jira read:filter.default-share-scope:jira write:filter.default-share-scope:jira read:group:jira write:group:jira delete:group:jira read:license:jira read:issue:jira write:issue:jira delete:issue:jira read:issue-meta:jira send:notification:jira read:attachment:jira write:attachment:jira delete:attachment:jira read:comment:jira write:comment:jira delete:comment:jira read:comment.property:jira write:comment.property:jira delete:comment.property:jira read:field:jira write:field:jira delete:field:jira read:field.default-value:jira write:field.default-value:jira read:field.option:jira write:field.option:jira delete:field.option:jira read:field-configuration-scheme:jira write:field-configuration-scheme:jira delete:field-configuration-scheme:jira read:custom-field-contextual-configuration:jira write:custom-field-contextual-configuration:jira read:field-configuration:jira write:field-configuration:jira delete:field-configuration:jira read:field.options:jira read:issue-link:jira write:issue-link:jira delete:issue-link:jira read:issue-link-type:jira write:issue-link-type:jira delete:issue-link-type:jira read:notification-scheme:jira read:priority:jira read:issue.property:jira write:issue.property:jira delete:issue.property:jira read:issue.remote-link:jira write:issue.remote-link:jira delete:issue.remote-link:jira read:resolution:jira read:issue-details:jira read:issue-security-scheme:jira read:issue-type:jira write:issue-type:jira delete:issue-type:jira read:issue-type-scheme:jira write:issue-type-scheme:jira delete:issue-type-scheme:jira read:issue-type-screen-scheme:jira write:issue-type-screen-scheme:jira delete:issue-type-screen-scheme:jira read:issue-type.property:jira write:issue-type.property:jira delete:issue-type.property:jira read:issue.watcher:jira write:issue.watcher:jira read:issue-worklog:jira write:issue-worklog:jira delete:issue-worklog:jira read:issue-worklog.property:jira write:issue-worklog.property:jira delete:issue-worklog.property:jira read:issue-field-values:jira read:issue-security-level:jira read:issue-status:jira read:issue-type-hierarchy:jira read:issue-type-transition:jira read:issue.changelog:jira read:issue.transition:jira write:issue.vote:jira read:issue-event:jira read:jira-expressions:jira read:user:jira read:user.columns:jira read:label:jira read:permission:jira write:permission:jira delete:permission:jira read:permission-scheme:jira write:permission-scheme:jira delete:permission-scheme:jira read:project:jira write:project:jira delete:project:jira read:project-category:jira write:project-category:jira delete:project-category:jira read:project.component:jira write:project.component:jira delete:project.component:jira read:project.property:jira write:project.property:jira delete:project.property:jira read:project-role:jira write:project-role:jira delete:project-role:jira read:project-version:jira write:project-version:jira delete:project-version:jira read:project.feature:jira write:project.feature:jira read:screen:jira write:screen:jira delete:screen:jira read:screen-scheme:jira write:screen-scheme:jira delete:screen-scheme:jira read:screen-field:jira read:screen-tab:jira write:screen-tab:jira delete:screen-tab:jira read:screenable-field:jira write:screenable-field:jira delete:screenable-field:jira read:issue.time-tracking:jira write:issue.time-tracking:jira read:user.property:jira write:user.property:jira delete:user.property:jira read:webhook:jira write:webhook:jira delete:webhook:jira read:workflow:jira write:workflow:jira delete:workflow:jira read:workflow-scheme:jira write:workflow-scheme:jira delete:workflow-scheme:jira read:status:jira read:workflow.property:jira write:workflow.property:jira delete:workflow.property:jira delete:async-task:jira read:instance-configuration:jira write:instance-configuration:jira read:jql:jira validate:jql:jira read:project-type:jira read:project.email:jira write:project.email:jira read:role:jira read:user-configuration:jira write:user-configuration:jira delete:user-configuration:jira read:email-address:jira',
authParams: {
audience: 'api.atlassian.com',
prompt: 'consent',
@@ -153,7 +153,7 @@ export class OAuthStrategyData {
accessibleResourcesURL: 'https://api.stg.atlassian.com/oauth/token/accessible-resources',
callbackURL: 'http://127.0.0.1:31415/' + OAuthProvider.JiraCloudStaging,
apiURL: 'api.stg.atlassian.com',
- scope: 'read:jira-user read:jira-work write:jira-work offline_access manage:jira-project',
+ scope: 'read:jira-user read:jira-work write:jira-work offline_access manage:jira-project manage:jira-configuration manage:jira-webhook read:application-role:jira read:audit-log:jira read:avatar:jira write:avatar:jira delete:avatar:jira read:project.avatar:jira write:project.avatar:jira delete:project.avatar:jira read:dashboard:jira write:dashboard:jira delete:dashboard:jira read:dashboard.property:jira write:dashboard.property:jira delete:dashboard.property:jira read:filter:jira write:filter:jira delete:filter:jira read:filter.column:jira write:filter.column:jira delete:filter.column:jira read:filter.default-share-scope:jira write:filter.default-share-scope:jira read:group:jira write:group:jira delete:group:jira read:license:jira read:issue:jira write:issue:jira delete:issue:jira read:issue-meta:jira send:notification:jira read:attachment:jira write:attachment:jira delete:attachment:jira read:comment:jira write:comment:jira delete:comment:jira read:comment.property:jira write:comment.property:jira delete:comment.property:jira read:field:jira write:field:jira delete:field:jira read:field.default-value:jira write:field.default-value:jira read:field.option:jira write:field.option:jira delete:field.option:jira read:field-configuration-scheme:jira write:field-configuration-scheme:jira delete:field-configuration-scheme:jira read:custom-field-contextual-configuration:jira write:custom-field-contextual-configuration:jira read:field-configuration:jira write:field-configuration:jira delete:field-configuration:jira read:field.options:jira read:issue-link:jira write:issue-link:jira delete:issue-link:jira read:issue-link-type:jira write:issue-link-type:jira delete:issue-link-type:jira read:notification-scheme:jira read:priority:jira read:issue.property:jira write:issue.property:jira delete:issue.property:jira read:issue.remote-link:jira write:issue.remote-link:jira delete:issue.remote-link:jira read:resolution:jira read:issue-details:jira read:issue-security-scheme:jira read:issue-type:jira write:issue-type:jira delete:issue-type:jira read:issue-type-scheme:jira write:issue-type-scheme:jira delete:issue-type-scheme:jira read:issue-type-screen-scheme:jira write:issue-type-screen-scheme:jira delete:issue-type-screen-scheme:jira read:issue-type.property:jira write:issue-type.property:jira delete:issue-type.property:jira read:issue.watcher:jira write:issue.watcher:jira read:issue-worklog:jira write:issue-worklog:jira delete:issue-worklog:jira read:issue-worklog.property:jira write:issue-worklog.property:jira delete:issue-worklog.property:jira read:issue-field-values:jira read:issue-security-level:jira read:issue-status:jira read:issue-type-hierarchy:jira read:issue-type-transition:jira read:issue.changelog:jira read:issue.transition:jira write:issue.vote:jira read:issue-event:jira read:jira-expressions:jira read:user:jira read:user.columns:jira read:label:jira read:permission:jira write:permission:jira delete:permission:jira read:permission-scheme:jira write:permission-scheme:jira delete:permission-scheme:jira read:project:jira write:project:jira delete:project:jira read:project-category:jira write:project-category:jira delete:project-category:jira read:project.component:jira write:project.component:jira delete:project.component:jira read:project.property:jira write:project.property:jira delete:project.property:jira read:project-role:jira write:project-role:jira delete:project-role:jira read:project-version:jira write:project-version:jira delete:project-version:jira read:project.feature:jira write:project.feature:jira read:screen:jira write:screen:jira delete:screen:jira read:screen-scheme:jira write:screen-scheme:jira delete:screen-scheme:jira read:screen-field:jira read:screen-tab:jira write:screen-tab:jira delete:screen-tab:jira read:screenable-field:jira write:screenable-field:jira delete:screenable-field:jira read:issue.time-tracking:jira write:issue.time-tracking:jira read:user.property:jira write:user.property:jira delete:user.property:jira read:webhook:jira write:webhook:jira delete:webhook:jira read:workflow:jira write:workflow:jira delete:workflow:jira read:workflow-scheme:jira write:workflow-scheme:jira delete:workflow-scheme:jira read:status:jira read:workflow.property:jira write:workflow.property:jira delete:workflow.property:jira delete:async-task:jira read:instance-configuration:jira write:instance-configuration:jira read:jql:jira validate:jql:jira read:project-type:jira read:project.email:jira write:project.email:jira read:role:jira read:user-configuration:jira write:user-configuration:jira delete:user-configuration:jira read:email-address:jira',
authParams: {
audience: 'api.stg.atlassian.com',
prompt: 'consent',
diff --git a/src/jira/jira-client/providers.ts b/src/jira/jira-client/providers.ts
index 0113adb76..34ce06d1a 100644
--- a/src/jira/jira-client/providers.ts
+++ b/src/jira/jira-client/providers.ts
@@ -47,6 +47,7 @@ export function basicJiraTransportFactory(site: DetailedSiteInfo): TransportFact
}
export const jiraTokenAuthProvider = (token: string): AuthorizationProvider => {
+ console.debug(`Bearer ${token}`);
return (method: string, url: string) => {
return Promise.resolve(`Bearer ${token}`);
};
diff --git a/src/webviews/components/issue/common/AtlaskitEditor/AtlaskitEditor.tsx b/src/webviews/components/issue/common/AtlaskitEditor/AtlaskitEditor.tsx
index 186627232..3f06815b8 100644
--- a/src/webviews/components/issue/common/AtlaskitEditor/AtlaskitEditor.tsx
+++ b/src/webviews/components/issue/common/AtlaskitEditor/AtlaskitEditor.tsx
@@ -3,8 +3,12 @@ import './AtlaskitEditor.css';
import { ComposableEditor, EditorNextProps } from '@atlaskit/editor-core/composable-editor';
import { createDefaultPreset } from '@atlaskit/editor-core/preset-default';
import { usePreset } from '@atlaskit/editor-core/use-preset';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { gridPlugin } from '@atlaskit/editor-plugin-grid';
import { insertBlockPlugin } from '@atlaskit/editor-plugin-insert-block';
import { listPlugin } from '@atlaskit/editor-plugin-list';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { mediaPlugin } from '@atlaskit/editor-plugin-media';
import { mentionsPlugin } from '@atlaskit/editor-plugin-mentions';
import { textColorPlugin } from '@atlaskit/editor-plugin-text-color';
import { toolbarListsIndentationPlugin } from '@atlaskit/editor-plugin-toolbar-lists-indentation';
@@ -19,6 +23,8 @@ interface AtlaskitEditorProps extends Omit, 'onChange'
onContentChange?: (content: string) => void;
onChange?: (content: string) => void;
onBlur?: (content: string) => void;
+ getMediaAuth?: () => Promise<{ token: string; clientId: string; baseUrl?: string; collectionName?: string }>;
+ issueKey?: string;
}
const AtlaskitEditor: React.FC = (props: AtlaskitEditorProps) => {
@@ -31,8 +37,68 @@ const AtlaskitEditor: React.FC = (props: AtlaskitEditorProp
onBlur,
onContentChange,
mentionProvider,
+ getMediaAuth,
+ issueKey,
} = props;
+ // Media plugin options with Jira API token provider
+ const mediaOptions = React.useMemo(() => {
+ const base: any = {
+ allowMediaSingle: true,
+ allowMediaGroup: true,
+ allowResizing: true,
+ allowAnnotation: true,
+ allowLinking: true,
+ allowResizingInTables: true,
+ allowMediaInline: true,
+ allowCaptions: true,
+ allowAltTextOnImages: true,
+ featureFlags: { mediaInline: true },
+ };
+
+ // Only provide media provider if we have auth function
+ if (!getMediaAuth) {
+ return base;
+ }
+
+ const provider = Promise.resolve({
+ uploadMediaClientConfig: {
+ authProvider: async () => {
+ try {
+ const auth = await getMediaAuth();
+ return {
+ token: auth.token,
+ clientId: auth.clientId,
+ baseUrl: auth.baseUrl ?? 'https://api.media.atlassian.com',
+ } as any;
+ } catch (error) {
+ console.error('Error getting media auth for upload:', error);
+ throw error;
+ }
+ },
+ },
+ viewMediaClientConfig: {
+ authProvider: async () => {
+ try {
+ const auth = await getMediaAuth();
+ return {
+ token: auth.token,
+ clientId: auth.clientId,
+ baseUrl: auth.baseUrl ?? 'https://api.media.atlassian.com',
+ } as any;
+ } catch (error) {
+ console.error('Error getting media auth for view:', error);
+ throw error;
+ }
+ },
+ },
+ uploadParams: {
+ collection: issueKey ?? 'atlascode',
+ },
+ });
+ return { ...base, provider };
+ }, [getMediaAuth, issueKey]);
+
const { preset, editorApi } = usePreset(() => {
return (
createDefaultPreset({
@@ -46,20 +112,22 @@ const AtlaskitEditor: React.FC = (props: AtlaskitEditorProp
platform: 'web',
},
})
- // You can extend this with other plugins if you need them
+ // Add additional plugins
+ .add(gridPlugin)
.add(listPlugin)
.add([
toolbarListsIndentationPlugin,
{ showIndentationButtons: false, allowHeadingAndParagraphIndentation: false },
])
.add(textColorPlugin)
+ .add([mediaPlugin, mediaOptions])
.add([
insertBlockPlugin,
- { toolbarShowPlusInsertOnly: true, appearance: appearance, allowExpand: true },
+ { toolbarShowPlusInsertOnly: false, appearance: appearance, allowExpand: true },
])
- .add(mentionsPlugin)
+ .add([mentionsPlugin, { mentionProvider }])
);
- }, []);
+ }, [mediaOptions, mentionProvider]);
// Helper function to get current document content
const getCurrentContent = React.useCallback(async (): Promise => {
try {
diff --git a/src/webviews/components/issue/view-issue-screen/JiraIssuePage.tsx b/src/webviews/components/issue/view-issue-screen/JiraIssuePage.tsx
index 56e0d3092..586b0394a 100644
--- a/src/webviews/components/issue/view-issue-screen/JiraIssuePage.tsx
+++ b/src/webviews/components/issue/view-issue-screen/JiraIssuePage.tsx
@@ -630,6 +630,16 @@ export default class JiraIssuePage extends AbstractIssueEditorPage {
+ const nonce = v4();
+ return (await this.postMessageWithEventPromise(
+ { action: 'getMediaAuth', site: this.state.siteDetails, issueKey: this.state.key, nonce },
+ 'mediaAuth',
+ 15000,
+ nonce,
+ )) as any;
+ }}
+ issueKey={this.state.key}
/>
{this.advancedMain()}
{this.state.fields['comment'] && (
@@ -654,6 +664,21 @@ export default class JiraIssuePage extends AbstractIssueEditorPage {
+ const nonce = v4();
+ return (await this.postMessageWithEventPromise(
+ {
+ action: 'getMediaAuth',
+ site: this.state.siteDetails,
+ issueKey: this.state.key,
+ nonce,
+ },
+ 'mediaAuth',
+ 15000,
+ nonce,
+ )) as any;
+ }}
+ issueKey={this.state.key}
/>
)}
diff --git a/src/webviews/components/issue/view-issue-screen/mainpanel/IssueCommentComponent.tsx b/src/webviews/components/issue/view-issue-screen/mainpanel/IssueCommentComponent.tsx
index b29456d18..a15a15180 100644
--- a/src/webviews/components/issue/view-issue-screen/mainpanel/IssueCommentComponent.tsx
+++ b/src/webviews/components/issue/view-issue-screen/mainpanel/IssueCommentComponent.tsx
@@ -36,6 +36,8 @@ export type IssueCommentComponentProps = {
onEditingCommentChange: (editing: boolean) => void;
isAtlaskitEditorEnabled?: boolean;
mentionProvider: AtlascodeMentionProvider;
+ getMediaAuth?: () => Promise<{ token: string; clientId: string; baseUrl?: string; collectionName?: string }>;
+ issueKey?: string;
};
const CommentComponent: React.FC<{
siteDetails: DetailedSiteInfo;
@@ -47,6 +49,8 @@ const CommentComponent: React.FC<{
isServiceDeskProject?: boolean;
isAtlaskitEditorEnabled?: boolean;
mentionProvider: AtlascodeMentionProvider;
+ getMediaAuth?: () => Promise<{ token: string; clientId: string; baseUrl?: string; collectionName?: string }>;
+ issueKey?: string;
}> = ({
siteDetails,
comment,
@@ -57,6 +61,8 @@ const CommentComponent: React.FC<{
isServiceDeskProject,
isAtlaskitEditorEnabled,
mentionProvider,
+ getMediaAuth,
+ issueKey,
}) => {
const { openEditor, closeEditor, isEditorActive } = useEditorState();
const editorId = `edit-comment-${comment.id}` as const;
@@ -135,6 +141,8 @@ const CommentComponent: React.FC<{
isAtlaskitEditorEnabled ? (
{
setIsSaving(true);
closeEditorHandler();
@@ -201,6 +209,8 @@ const AddCommentComponent: React.FC<{
isEditing: boolean;
setIsEditing: (editing: boolean) => void;
mentionProvider: AtlascodeMentionProvider;
+ getMediaAuth?: () => Promise<{ token: string; clientId: string; baseUrl?: string; collectionName?: string }>;
+ issueKey?: string;
}> = ({
fetchUsers,
user,
@@ -212,6 +222,8 @@ const AddCommentComponent: React.FC<{
isEditing,
setIsEditing,
mentionProvider,
+ getMediaAuth,
+ issueKey,
}) => {
const { openEditor, closeEditor } = useEditorState();
@@ -278,6 +290,8 @@ const AddCommentComponent: React.FC<{
{
if (content && content.trim() !== '') {
onCreate(content, undefined);
@@ -340,6 +354,8 @@ export const IssueCommentComponent: React.FC = ({
onEditingCommentChange,
isAtlaskitEditorEnabled,
mentionProvider,
+ getMediaAuth,
+ issueKey,
}) => {
return (
= ({
isEditing={isEditingComment}
setIsEditing={onEditingCommentChange}
mentionProvider={mentionProvider}
+ getMediaAuth={getMediaAuth}
+ issueKey={issueKey}
/>
{comments
.sort((a, b) => (a.created > b.created ? -1 : 1))
@@ -372,6 +390,8 @@ export const IssueCommentComponent: React.FC = ({
isServiceDeskProject={isServiceDeskProject}
isAtlaskitEditorEnabled={isAtlaskitEditorEnabled}
mentionProvider={mentionProvider}
+ getMediaAuth={getMediaAuth}
+ issueKey={issueKey}
/>
))}
diff --git a/src/webviews/components/issue/view-issue-screen/mainpanel/IssueMainPanel.tsx b/src/webviews/components/issue/view-issue-screen/mainpanel/IssueMainPanel.tsx
index d8e5ccfe4..1406fdb79 100644
--- a/src/webviews/components/issue/view-issue-screen/mainpanel/IssueMainPanel.tsx
+++ b/src/webviews/components/issue/view-issue-screen/mainpanel/IssueMainPanel.tsx
@@ -43,6 +43,8 @@ type Props = {
onIssueUpdate?: (issueKey: string, fieldKey: string, newValue: any) => void;
isAtlaskitEditorEnabled?: boolean;
mentionProvider: AtlascodeMentionProvider;
+ getMediaAuth?: () => Promise<{ token: string; clientId: string; baseUrl?: string; collectionName?: string }>;
+ issueKey?: string;
};
const IssueMainPanel: React.FC = ({
@@ -66,6 +68,8 @@ const IssueMainPanel: React.FC = ({
onIssueUpdate,
isAtlaskitEditorEnabled,
mentionProvider,
+ getMediaAuth,
+ issueKey,
}) => {
const attachments = fields['attachment'] && fieldValues['attachment'] ? fieldValues['attachment'] : undefined;
const subtasks =
@@ -214,6 +218,8 @@ const IssueMainPanel: React.FC = ({
isAtlaskitEditorEnabled ? (
{
handleInlineEdit(fields['description'], content);
closeEditorHandler();
diff --git a/src/webviews/jiraIssueWebview.ts b/src/webviews/jiraIssueWebview.ts
index ddca5e65e..072ccffc6 100644
--- a/src/webviews/jiraIssueWebview.ts
+++ b/src/webviews/jiraIssueWebview.ts
@@ -1209,6 +1209,57 @@ export class JiraIssueWebview
}
break;
}
+ case 'getMediaAuth': {
+ handled = true;
+ try {
+ const site = (msg as any).site;
+ const issueKey = (msg as any).issueKey;
+
+ if (!issueKey) {
+ throw new Error('Issue key is required for media upload credentials');
+ }
+
+ const client = await Container.clientManager.jiraClient(site);
+
+ // Use Jira's public REST API to get upload credentials for media service
+ // This endpoint generates an upload token with 20 min TTL, same as Jira Web
+ const credentialsUrl = `${client.baseUrl}/rest/api/2/attachment/upload/issue/${issueKey}/credentials`;
+
+ const response = await client.transportFactory().get(credentialsUrl, {
+ method: 'GET',
+ headers: {
+ Authorization: await client.authorizationProvider('GET', credentialsUrl),
+ },
+ });
+ console.log(credentialsUrl, 'credentialsUrl');
+ console.log('Authorization header:', await client.authorizationProvider('GET', credentialsUrl));
+ console.log(response, 'response');
+
+ if (!response || !response.data) {
+ throw new Error('Failed to get media upload credentials from Jira API');
+ }
+
+ const credentials = response.data;
+
+ // Extract the necessary fields from Jira's response
+ await this.postMessage({
+ type: 'mediaAuth',
+ token: credentials.token || credentials.uploadToken,
+ clientId: credentials.clientId,
+ baseUrl: credentials.baseUrl || 'https://api.media.atlassian.com',
+ collectionName: credentials.collectionName || issueKey,
+ nonce: (msg as any).nonce,
+ });
+ } catch (e) {
+ Logger.error(e, 'Error getting media upload credentials');
+ await this.postMessage({
+ type: 'error',
+ reason: this.formatErrorReason(e, 'Error getting media upload credentials'),
+ nonce: (msg as any).nonce,
+ });
+ }
+ break;
+ }
}
}
diff --git a/webpack.react.dev.js b/webpack.react.dev.js
index ea99a2fee..92083a6b2 100644
--- a/webpack.react.dev.js
+++ b/webpack.react.dev.js
@@ -58,6 +58,7 @@ module.exports = {
fallback: {
path: require.resolve('path-browserify'),
process: false,
+ buffer: require.resolve('buffer'),
},
alias: {
// Resolve ProseMirror conflicts by using unified versions
@@ -113,6 +114,7 @@ module.exports = {
}),
new webpack.ProvidePlugin({
process: 'process/browser',
+ Buffer: ['buffer', 'Buffer'],
}),
],
module: {
diff --git a/webpack.react.prod.js b/webpack.react.prod.js
index 6fcefe51a..c3f0c056d 100644
--- a/webpack.react.prod.js
+++ b/webpack.react.prod.js
@@ -83,6 +83,7 @@ module.exports = {
plugins: [new TsconfigPathsPlugin({ configFile: resolveApp('./tsconfig.notest.json') })],
fallback: {
path: require.resolve('path-browserify'),
+ buffer: require.resolve('buffer'),
},
alias: {
// Resolve ProseMirror conflicts by using unified versions
@@ -145,6 +146,7 @@ module.exports = {
}),
new webpack.ProvidePlugin({
process: 'process/browser',
+ Buffer: ['buffer', 'Buffer'],
}),
],
performance: {