This resource's view is not closable

Hello,
I’m developing a custom field and using the customFieldType module.
Here is a summary of my configuration:

render: native
resource: issue-picker-view
edit:
    resource: issue-picker-edit
    render: native
resources:
  - key: issue-picker-view
    path: src/frontend/IssueField/View.jsx
  - key: issue-picker-edit
    path: src/frontend/IssueField/Edit.jsx

However, I have encountered an issue during development. If the user is working on a previously created issue, there is no problem. But when the custom field is added to the issue create screen, I get the error message “resources view is not closable.”
Additionally, on the issue create screen, the edit screen opens automatically even if I do not click on the field.
I’m unable to resolve this problem. I would really appreciate your help.
Thank you.

Is anyone has any idea about this problem? Also my code like this:

import React, { useEffect, useState } from 'react';
import ForgeReconciler, { Form, LoadingButton, Text, useProductContext } from '@forge/react';
import { view, invoke } from '@forge/bridge';
import { ModalFormForEditField } from './components/ModalFormForEditField';

const App = () => {
    const context = useProductContext();

    /* ---------- state ---------- */
    const [loading, setLoading] = useState(true);
    const [fieldId, setFieldId] = useState(null);
    const [fieldValue, setFieldValue] = useState(null);
    const [project, setProject] = useState(null);
    const [issue, setIssue] = useState();
    const [checkboxes, setCheckboxes] = useState({
        limitToProject: false,
        showKey: false,
        showStatus: false,
        multiSelect: false,
        jqlAsApp: false,
        showCount: false,
        deleteLink: false,
    });

    const [name, setName] = useState('');
    const [linkType, setLinkType] = useState(null);
    const [jql, setJql] = useState('');

    /* ---------- pick up context ---------- */
    useEffect(() => {
        if (!context) return;
        console.log('Context:', context.extension);
        setFieldId(context.extension?.fieldId ?? null);
        setFieldValue(context.extension?.fieldValue ?? null);
        setProject(context.extension?.project?.key ?? null);
        setIssue(context.extension?.issue?.key ?? null);
    }, [context]);                     // <‑ only re‑run when the reference genuinely changes

    /* ---------- load saved config ---------- */
    useEffect(() => {
        if (!fieldId) {                  // nothing to look up yet
            setLoading(false);             // drop the spinner if we end up with no field
            return;
        }

        const load = async () => {
            setLoading(true);

            const { status, res, message } = await invoke('getStorage', { key: 'issuePicker' });

            if (status !== 200 || !Array.isArray(res)) {
                console.error(message ?? 'Storage fetch failed');
                setLoading(false);
                return;
            }
            const cfg = res.find((item) => item.id === fieldId);
            if (!cfg) {
                console.warn(`No saved config for ${fieldId}`);
                setLoading(false);
                return;
            }

            setCheckboxes({
                limitToProject: !!cfg.isCheckedLimitOptionsToCurrentProject,
                showKey: !!cfg.isCheckedShowIssueKeyOfSelectedIssues,
                showStatus: !!cfg.isCheckedShowStatusOfSelectedIssues,
                multiSelect: !!cfg.isCheckedMultiSelect,
                jqlAsApp: !!cfg.isCheckedSearchJQLAsApp,
                showCount: !!cfg.isCheckedShowIssueCountDuringEdit,
                deleteLink: !!cfg.isCheckedDeleteAllLinks
            });

            setName(cfg.name ?? '');
            setLinkType(cfg.selectedLinkType ?? null);
            setJql(cfg.jql ?? '');

            setLoading(false);
        };

        load();
    }, [fieldId]);

    const closeModal = () => view.close();

    if (loading) return <LoadingButton appearance='subtle' isLoading={loading} />

    return (
        <ModalFormForEditField
            isMulti={checkboxes.multiSelect}
            isJqlAsApp={checkboxes.jqlAsApp}
            isCountVisible={checkboxes.showCount}
            isShowStatusVisible={checkboxes.showStatus}
            isShowKeyVisible={checkboxes.showKey}
            isLimitOptionToCurrentProject={checkboxes.limitToProject}
            name={name}
            fieldValue={fieldValue}
            linkType={linkType}
            deleteLink={checkboxes.deleteLink}
            preDefinedJQL={jql}
            project={project}
            onClose={closeModal}
            issue={issue}
        />
    );
};

ForgeReconciler.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>
);

import React, { useCallback, useEffect, useState } from "react"
import { Box, Button, Form, Inline, Label, LoadingButton, Modal, ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition, Select, Text, xcss } from "@forge/react"
import { invoke, view } from "@forge/bridge";
import { checkIssuesIsNull, defineJql, lozangeStatus, sleep } from "../helper";
import { FIELDS, MAX_RESULTS } from "../constants";
import { ViewField } from "../../components/ViewOfField";

export const ModalFormForEditField = ({
    isMulti,
    isJqlAsApp,
    isCountVisible,
    isShowStatusVisible,
    isShowKeyVisible,
    isLimitOptionToCurrentProject,
    name,
    fieldValue,
    linkType,
    deleteLink,
    preDefinedJQL,
    project,
    issue,
    onClose,
}) => {
    const [searchText, setSearchText] = useState("");
    const [options, setOptions] = useState([]);
    const [fetchedOptions, setFetchedOptions] = useState([]);
    const [issueCount, setIssueCount] = useState(0);
    const [isloading, setIsLoading] = useState(false);
    const [isCountLoading, setIsCountLoading] = useState(false);
    const [lastJql, setLastJql] = useState("")
    const [isFromPicker, setIsFromPicker] = useState(true);
    const [selectedIssues, setSelectedIssues] = useState([]);
    const [loading, setLoading] = useState(false);
    const getIssues = useCallback(async () => {
        setIsLoading(true);
        setIsFromPicker(true);
        await sleep(500);
        console.log("LINKTYPE:", linkType);
        console.log("DELETE LINK:", deleteLink);
        console.log("PREDEFINED JQL:", preDefinedJQL)
        console.log("IS LIMIT OPTION:", isLimitOptionToCurrentProject)
        console.log("PROJECT:", project);
        const lastDefinedJQL = defineJql({
            payload: {
                preDefinedJQL,
                isLimitOptionToCurrentProject,
                project
            }
        })
        console.log("LAST DEFINED JQL:", lastDefinedJQL);
        if (searchText === "") {
            if (isCountVisible) {
                setIsCountLoading(true);
                const cnt = await invoke("issueCount", { jql: lastDefinedJQL, asApp: isJqlAsApp });
                if (cnt.status === 200) {
                    setIssueCount(cnt.res.count);
                }
            }
            setFetchedOptions([]);
            setIsLoading(false);
            setIsCountLoading(false);
            return;
        }
        //Historysi olan issuelar geliyor ancak kullanıcı bir issue yarattıysa ve hiç girmediyse gelmiyor o yüzden getIssues çağrılıyor
        let picker = await invoke("getIssuePicker", { query: searchText, currentJql: lastDefinedJQL, asApp: isJqlAsApp });
        let allIssues = checkIssuesIsNull(picker, true);

        if (allIssues.length === 0) {
            let fallbackJql = isLimitOptionToCurrentProject
                ? `(issuekey = ${searchText} OR summary ~ ${searchText}\\u002a) AND project = ${project}`
                : `(issuekey = ${searchText} OR summary ~ ${searchText}\\u002a)`;
            console.log("FALLBACK JQL:", fallbackJql);
            const search = await invoke("getIssues", { jql: fallbackJql, nextPageToken: null, maxResults: MAX_RESULTS, fields: FIELDS, asApp: isJqlAsApp });
            allIssues = checkIssuesIsNull(search, false);
            setIsFromPicker(false);
        }

        // key'e göre aynı keyden iki tane gözükmemesi için bir Map kullanıyrouz.
        const map = new Map();
        allIssues.forEach(i => map.set(i.key, i));
        console.log("ARRAY FROM: ", Array.from(map.values()));
        setFetchedOptions(Array.from(map.values()));
        setIsLoading(false);
    }, [searchText, preDefinedJQL, isLimitOptionToCurrentProject, project, isJqlAsApp, isCountVisible])

    useEffect(() => {
        getIssues();
    }, []);

    useEffect(() => {
        if (searchText) {
            getIssues();
        }
    }, [searchText, getIssues]);

    useEffect(() => {
        if (fetchedOptions.length === 0) setOptions([])
        else {
            console.log("FETCHED OPTIONS:", fetchedOptions);
            const opts = fetchedOptions.map(issue => ({
                label: isFromPicker ? `${issue?.key}: ${issue?.summaryText}`
                    : <><Text weight="bold" as="strong">{issue?.key}</Text>: {issue?.fields?.summary}</>,
                value: issue.key
            }));
            setOptions(opts);
        }
    }, [fetchedOptions, isFromPicker])

    const handleSelectChange = async (options) => {
        if (!options || (Array.isArray(options) && options.length === 0)) {
            setSelectedIssues([]);
            return;
        }
        const optionArray = Array.isArray(options) ? options : [options];
        const jqlClause = `issue in (${optionArray.map(item => `"${item.value}"`).join(", ")})`;
        const previewIssues = await invoke('getIssues', {
            jql: jqlClause,
            nextPageToken: null,
            maxResults: MAX_RESULTS,
            fields: FIELDS,
            asApp: isJqlAsApp
        });
        if (previewIssues.status === 200 && previewIssues.res?.issues) {
            const normalized = previewIssues.res.issues.map(issue => ({
                key: issue.key,
                summary: issue.fields.summary,
                status: issue.fields.status.name
            }));
            setSelectedIssues(normalized);
        } else {
            setSelectedIssues([]);
        }

    }
    const handleInputChange = (input) => setSearchText(input)
    const onSubmit = async () => {
        setLoading(true);
        const keys = selectedIssues.map(issue => issue.key);

        if (linkType?.value?.linkType === "None") {
            console.log("Link type is None, skipping link operations.");
            setLoading(false);
            await view.submit(keys);
            return;
        }

        if (linkType?.value?.linkType !== "None") {
            const linkTypeName = linkType.value.linkTypeName;
            const isInward = linkType?.value?.isInwardOrOutward === "inward";

            // Kaldırılan issue'ların linklerini sil (deleteLink true ve kaldırılan issue varsa)
            if (deleteLink) {
                const issueLinks = await invoke('getIssue', {
                    issueKey: issue,
                    fields: 'issuelinks'
                });

                if (issueLinks.status === 200 && issueLinks.res?.fields?.issuelinks) {
                    const links = issueLinks.res.fields.issuelinks;

                    // linkTypeName ve yön bilgisine göre eşleşen linkleri filtrele
                    const linksToDelete = links.filter(link => {
                        const isMatchingType = link.type.name === linkTypeName;
                        const isMatchingDirection = isInward
                            ? link.inwardIssue?.key
                            : link.outwardIssue?.key
                        return isMatchingType && isMatchingDirection;
                    });
                    console.log("LINKS TO DELETE:", linksToDelete);

                    // Filtrelenen linkleri sil
                    for (const link of linksToDelete) {
                        console.log("Deleting link:", link.id);
                        await invoke('deleteIssueLink', { linkId: link.id });
                        await sleep(200);
                    }
                } else {
                    console.error("Failed to fetch issue links for deletion.");
                }
            }

            // Yeni seçilen issue'lar için link oluştur
            if (keys.length > 0) {
                for (const key of keys) {
                    if (issue === key) continue; // Kendisiyle link oluşturmayı atla
                    let payload;
                    if (isInward) {
                        payload = {
                            inwardIssueKey: key,
                            outwardIssueKey: issue,
                            linkType: linkTypeName
                        };
                    } else {
                        payload = {
                            inwardIssueKey: issue,
                            outwardIssueKey: key,
                            linkType: linkTypeName
                        };
                    }
                    await invoke("createIssueLink", {
                        inwardIssueId: payload.inwardIssueKey,
                        outwardIssueId: payload.outwardIssueKey,
                        linkType: payload.linkType
                    });
                    await sleep(200);
                }
            }
        }

        console.log("Submitting keys:", keys);
        setLoading(false);
        await view.submit(keys);

    };
    return (
        <ModalTransition>
            <Modal width={'large'} onClose={onClose}>
                <Form onSubmit={onSubmit}>
                    <ModalHeader>
                        <ModalTitle>{name} Issue Picker</ModalTitle>
                    </ModalHeader>
                    <ModalBody>
                        <Box>
                            <Label labelFor="Issue Picker">{isMulti ? "Choose issue(s) for picker" : "Choose an issue for picker"}</Label>
                            <Select
                                placeholder="Type issue key or summary"
                                isMulti={isMulti}
                                isSearchable
                                onChange={handleSelectChange}
                                onInputChange={handleInputChange}
                                isClearable
                                isLoading={isloading}
                                options={options}
                            />
                            {isCountVisible && (isCountLoading ? (
                                <LoadingButton
                                    appearance="subtle"
                                    isLoading={isCountLoading}
                                />
                            ) : (
                                <Text>
                                    Searching {issueCount} issues that fit the underlying Filter.
                                </Text>
                            ))}
                        </Box>
                        <Box xcss={xcss({
                            marginBlock: "space.100"
                        })}>
                            <Inline alignBlock="center" space="space.100">
                                <Label labelFor="preview">Preview: </Label>
                                <ViewField
                                    isKeySelected={isShowKeyVisible}
                                    isStatusSelected={isShowStatusVisible}
                                    issues={selectedIssues}
                                />
                            </Inline>
                        </Box>
                    </ModalBody>
                    <ModalFooter>
                        <Button appearance="subtle" onClick={onClose}>Close</Button>
                        <LoadingButton appearance="primary" type="submit" onClick={onSubmit} isLoading={loading}>Submit</LoadingButton>
                    </ModalFooter>
                </Form>
            </Modal>
        </ModalTransition >
    )
}