forked from Gitlink/forgeplus-react
修改cloudIDE
This commit is contained in:
parent
cc443fa112
commit
b1725372b7
|
@ -86,7 +86,7 @@ module.exports = {
|
|||
externals: {
|
||||
"react": "React",
|
||||
"react-dom": "ReactDOM",
|
||||
'alex': 'Alex',
|
||||
"alex": "Alex",
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
|
|
|
@ -4,6 +4,15 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@ahooksjs/use-request": {
|
||||
"version": "2.8.15",
|
||||
"resolved": "https://registry.npmjs.org/@ahooksjs/use-request/-/use-request-2.8.15.tgz",
|
||||
"integrity": "sha512-xhVaM4fyIiAMdVFuuU5i3CFUdFa/IblF+fvITVMFaUEO3w/V5tVCAF6WIA3T03n1/RPuzRkA7Ao1PFtSGtGelw==",
|
||||
"requires": {
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.throttle": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"@ant-design/colors": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npm.taobao.org/@ant-design/colors/download/@ant-design/colors-3.2.2.tgz?cache=0&sync_timestamp=1612935637470&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Fcolors%2Fdownload%2F%40ant-design%2Fcolors-3.2.2.tgz",
|
||||
|
@ -415,6 +424,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.0.0.tgz",
|
||||
"integrity": "sha512-q95SP4FdkmF0CwO0F2q0H6ZgudsApaY/yCtAQNRn1gduef5fGpyEphzy0YCq/N0UFvDSnLg5V8jFK/YGXlDiCw=="
|
||||
},
|
||||
"@types/js-cookie": {
|
||||
"version": "2.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
|
||||
"integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA=="
|
||||
},
|
||||
"@types/jss": {
|
||||
"version": "9.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/jss/-/jss-9.5.8.tgz",
|
||||
|
@ -817,6 +831,23 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"ahooks": {
|
||||
"version": "2.10.14",
|
||||
"resolved": "https://registry.npmjs.org/ahooks/-/ahooks-2.10.14.tgz",
|
||||
"integrity": "sha512-axWa7VoAgu7bxA56dDl0CXW4rvaQmDBiov/d3tAy0x1YNYywYMKokL8TdLgJ5zO/oXGiWmG7BxlGOQGkqE/zkQ==",
|
||||
"requires": {
|
||||
"@ahooksjs/use-request": "^2.8.14",
|
||||
"@types/js-cookie": "^2.2.6",
|
||||
"dayjs": "^1.9.1",
|
||||
"intersection-observer": "^0.7.0",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"screenfull": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
|
||||
|
@ -4501,6 +4532,11 @@
|
|||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.12.0.tgz",
|
||||
"integrity": "sha512-qJgn99xxKnFgB1qL4jpxU7Q2t0LOn1p8KMIveef3UZD7kqjT3tpFNNdXJelEHhE+rUgffriXriw/sOSU+cS1Hw=="
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.11.3",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz",
|
||||
"integrity": "sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
|
@ -4839,7 +4875,7 @@
|
|||
},
|
||||
"dom-closest": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz",
|
||||
"resolved": "https://registry.npm.taobao.org/dom-closest/download/dom-closest-0.2.0.tgz",
|
||||
"integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=",
|
||||
"requires": {
|
||||
"dom-matches": ">=1.0.1"
|
||||
|
@ -4883,7 +4919,7 @@
|
|||
},
|
||||
"dom-matches": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz",
|
||||
"resolved": "https://registry.npm.taobao.org/dom-matches/download/dom-matches-2.0.0.tgz",
|
||||
"integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw="
|
||||
},
|
||||
"dom-scroll-into-view": {
|
||||
|
@ -5135,7 +5171,7 @@
|
|||
},
|
||||
"enquire.js": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
|
||||
"resolved": "https://registry.npm.taobao.org/enquire.js/download/enquire.js-2.1.6.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fenquire.js%2Fdownload%2Fenquire.js-2.1.6.tgz",
|
||||
"integrity": "sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ="
|
||||
},
|
||||
"entities": {
|
||||
|
@ -5660,7 +5696,7 @@
|
|||
},
|
||||
"eventlistener": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventlistener/-/eventlistener-0.0.1.tgz",
|
||||
"resolved": "https://registry.npm.taobao.org/eventlistener/download/eventlistener-0.0.1.tgz",
|
||||
"integrity": "sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg="
|
||||
},
|
||||
"events": {
|
||||
|
@ -7194,7 +7230,8 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -7215,12 +7252,14 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -7235,17 +7274,20 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -7362,7 +7404,8 @@
|
|||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -7374,6 +7417,7 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -7388,6 +7432,7 @@
|
|||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -7395,12 +7440,14 @@
|
|||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -7419,6 +7466,7 @@
|
|||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
|
||||
"integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
|
@ -7480,7 +7528,8 @@
|
|||
"npm-normalize-package-bin": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
|
||||
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
|
||||
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
|
||||
"optional": true
|
||||
},
|
||||
"npm-packlist": {
|
||||
"version": "1.4.8",
|
||||
|
@ -7508,7 +7557,8 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -7520,6 +7570,7 @@
|
|||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -7597,7 +7648,8 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -7633,6 +7685,7 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -7652,6 +7705,7 @@
|
|||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -7695,12 +7749,14 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -7974,7 +8030,7 @@
|
|||
},
|
||||
"hammerjs": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||
"resolved": "https://registry.npm.taobao.org/hammerjs/download/hammerjs-2.0.8.tgz",
|
||||
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
|
||||
},
|
||||
"handle-thing": {
|
||||
|
@ -8788,7 +8844,7 @@
|
|||
},
|
||||
"image-size": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "http://173.15.15.82:8081/repository/npm-all/image-size/-/image-size-0.5.5.tgz",
|
||||
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
|
||||
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
|
||||
"optional": true
|
||||
},
|
||||
|
@ -8807,7 +8863,7 @@
|
|||
},
|
||||
"immutable": {
|
||||
"version": "3.7.6",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
|
||||
"resolved": "https://registry.npm.taobao.org/immutable/download/immutable-3.7.6.tgz",
|
||||
"integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks="
|
||||
},
|
||||
"import-fresh": {
|
||||
|
@ -9021,6 +9077,11 @@
|
|||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
|
||||
"integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw=="
|
||||
},
|
||||
"intersection-observer": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.7.0.tgz",
|
||||
"integrity": "sha512-Id0Fij0HsB/vKWGeBe9PxeY45ttRiBmhFyyt/geBdDHBYNctMRTE3dC1U3ujzz3lap+hVXlEcVaB56kZP/eEUg=="
|
||||
},
|
||||
"invariant": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||
|
@ -9896,6 +9957,11 @@
|
|||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
|
||||
"integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ=="
|
||||
},
|
||||
"js-cookie": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
|
||||
"integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ=="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
@ -10253,7 +10319,7 @@
|
|||
},
|
||||
"less": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "http://173.15.15.82:8081/repository/npm-all/less/-/less-3.9.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz",
|
||||
"integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==",
|
||||
"requires": {
|
||||
"clone": "^2.1.2",
|
||||
|
@ -10269,7 +10335,7 @@
|
|||
"dependencies": {
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "http://173.15.15.82:8081/repository/npm-all/promise/-/promise-7.3.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
@ -10280,7 +10346,7 @@
|
|||
},
|
||||
"less-loader": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "http://173.15.15.82:8081/repository/npm-all/less-loader/-/less-loader-4.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz",
|
||||
"integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==",
|
||||
"requires": {
|
||||
"clone": "^2.1.1",
|
||||
|
@ -10290,7 +10356,7 @@
|
|||
"dependencies": {
|
||||
"pify": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "http://173.15.15.82:8081/repository/npm-all/pify/-/pify-3.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||
"integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="
|
||||
}
|
||||
}
|
||||
|
@ -10437,7 +10503,7 @@
|
|||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"resolved": "https://registry.npm.taobao.org/lodash.throttle/download/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"lodash.uniq": {
|
||||
|
@ -16030,6 +16096,11 @@
|
|||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"screenfull": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz",
|
||||
"integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA=="
|
||||
},
|
||||
"scroll": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/scroll/-/scroll-2.0.3.tgz",
|
||||
|
@ -16263,6 +16334,15 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"sha1": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz",
|
||||
"integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==",
|
||||
"requires": {
|
||||
"charenc": ">= 0.0.1",
|
||||
"crypt": ">= 0.0.1"
|
||||
}
|
||||
},
|
||||
"shallow-clone": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"@monaco-editor/react": "^2.3.0",
|
||||
"@novnc/novnc": "^1.1.0",
|
||||
"actioncable": "^5.2.4-3",
|
||||
"ahooks": "^2.10.14",
|
||||
"antd": "^3.26.15",
|
||||
"array-flatten": "^2.1.2",
|
||||
"autoprefixer": "7.1.6",
|
||||
|
@ -110,6 +111,7 @@
|
|||
"sass-loader": "7.3.1",
|
||||
"save-dev": "0.0.1-security",
|
||||
"scroll-into-view": "^1.14.2",
|
||||
"sha1": "^1.1.1",
|
||||
"showdown": "^1.9.1",
|
||||
"showdown-katex": "^0.8.0",
|
||||
"slick-carousel": "^1.8.1",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" class="notranslate translated-ltr" translate="no">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="Keywords" Content="gitLink,GitLink,gitlink,trustie,trustieforge,forge,确实让创建更美好,协同开发平台">
|
||||
|
@ -9,13 +10,14 @@
|
|||
<meta name="theme-color" content="#000000">
|
||||
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
<link href="https://gw.alipayobjects.com/os/lib/alipay/alex/1.9.10/bundle/alex.editor.all.global.min.css" rel="stylesheet">
|
||||
<link href="https://gw.alipayobjects.com/os/lib/alipay/alex-acr/2.0.7/bundle/acr.global.min.css" rel="stylesheet"/>
|
||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
|
||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
|
||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/merge.css">
|
||||
<%= htmlWebpackPlugin.tags.headTags %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--用于markdown转html -->
|
||||
<div id="md_div" style="display: none;"></div>
|
||||
|
@ -28,10 +30,9 @@
|
|||
<script src="%PUBLIC_URL%js/codemirror/codemirror.js"></script>
|
||||
<script src="%PUBLIC_URL%js/editormd/editormd.min.js"></script>
|
||||
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>
|
||||
<script src="%PUBLIC_URL%js/alex/alex.editor.all.global.min.js" defer ></script>
|
||||
<script src="%PUBLIC_URL%js/alex/languages.global.min.js" defer ></script>
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/alipay/alex/1.9.12/bundle/alex.editor.all.global.min.js" defer ></script>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/alipay/alex/1.9.12/languages/languages.global.min.js" defer ></script> -->
|
||||
<script src="https://gw.alipayobjects.com/os/lib/moment/2.29.4/moment.js"></script>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.7/bundle/alex.all.global.min.js"></script>
|
||||
<%= htmlWebpackPlugin.tags.bodyTags %>
|
||||
</body>
|
||||
|
||||
</html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -89,8 +89,13 @@ export function initAxiosInterceptors(props) {
|
|||
|
||||
if (response.data.status === 404) {
|
||||
let responseURL = response.request ? response.request.responseURL:'';
|
||||
// 组织和个人的拥有情况,404不跳转
|
||||
if (responseURL.indexOf('/api/users/') === -1 && responseURL.indexOf('/api/organizations/') === -1 ) {
|
||||
locationurl('/nopage');
|
||||
// 邀请页面不进行404跳转
|
||||
if( window.location.pathname.includes('/invite') && (responseURL.includes('/simple.json')||responseURL.includes('/detail.json')||responseURL.includes('/menu_list.json'))){
|
||||
}else{
|
||||
locationurl('/nopage');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ export function getImageUrl(path) {
|
|||
// https://www.educoder.net
|
||||
// https://testbdweb.trustie.net
|
||||
// const local = 'http://localhost:3000'
|
||||
path && !path.startsWith('/') && !path.startsWith('http') && (path = '/'.concat(path));
|
||||
const local = 'https://testforgeplus.trustie.net';
|
||||
if (isDev) {
|
||||
return `${local}/${path}`
|
||||
|
|
|
@ -80,7 +80,7 @@ function AddMember({getID,login,showNotification}){
|
|||
<AutoComplete
|
||||
dataSource={source}
|
||||
value={searchKey}
|
||||
style={{ width: 300 }}
|
||||
style={{ width: 250 }}
|
||||
onChange={changeInputUser}
|
||||
onSelect={selectInputUser}
|
||||
placeholder="搜索需要添加的用户..."
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, Button, } from 'antd';
|
||||
import { Base64 } from 'js-base64';
|
||||
import './index.scss';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getImageUrl } from 'educoder'
|
||||
import axios from 'axios';
|
||||
|
||||
const identify = {
|
||||
manager: "管理员",
|
||||
developer: "开发者",
|
||||
reporter: "报告者",
|
||||
owner: "所有者",
|
||||
}
|
||||
function Invite({ history, current_user, match, projectDetail, showNotification }) {
|
||||
|
||||
if (!current_user.login) {
|
||||
let { pathname, search } = window.location;
|
||||
window.location.href = `/login?go_page=${pathname}${search}`;
|
||||
}
|
||||
let permission = projectDetail && projectDetail.permission;
|
||||
const { projectsId, owner } = match.params;
|
||||
let inviteString = window.location.search && window.location.search.split('?invite=')[1];
|
||||
let data = inviteString && JSON.parse(Base64.decode(inviteString));
|
||||
const [detail, setDetail] = useState({});
|
||||
const [visible, setVisible] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (permission && detail.role && detail.role == permission.toLocaleLowerCase() || permission == 'Owner') {
|
||||
showNotification(`您已经是${identify[detail.role]}了`)
|
||||
setTimeout(() => {
|
||||
history.push(`/${owner}/${projectsId}`);
|
||||
}, 2000)
|
||||
} else {
|
||||
setVisible(true)
|
||||
}
|
||||
}, [permission, detail.role]);
|
||||
|
||||
useEffect(() => {
|
||||
const url = `/${owner}/${projectsId}/project_invite_links/show_link.json?invite_sign=${data.sign}`;
|
||||
axios.get(url).then(res => {
|
||||
if (res && res.data) {
|
||||
setDetail(res.data);
|
||||
} else {
|
||||
showNotification('查询邀请链接失败');
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
function accept() {
|
||||
const url = `/${owner}/${projectsId}/project_invite_links/redirect_link.json?invite_sign=${data.sign}`;
|
||||
axios.post(url).then(res => {
|
||||
if (res && res.data.message == 'success') {
|
||||
if (detail.is_apply) {
|
||||
setVisible(false);
|
||||
Modal.success({ content: '提交申请成功,请等待该仓库管理员审核' });
|
||||
} else {
|
||||
history.push(`/${owner}/${projectsId}`);
|
||||
}
|
||||
}
|
||||
}).catch(error => { })
|
||||
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
{data && <Modal
|
||||
visible={visible}
|
||||
className="invite_development"
|
||||
title={<div className="ownerImage"><img src={detail.project && getImageUrl(detail.project.owner.image_url)} /></div>}
|
||||
width="548px"
|
||||
closable={true}
|
||||
onCancel={() => { setVisible(false); history.push(`/${current_user.login}`) }}
|
||||
centered
|
||||
okText={'接受'}
|
||||
cancelText={'拒绝'}
|
||||
onOk={accept}
|
||||
maskClosable={false}
|
||||
>
|
||||
<Link className="invite_project link" target="_blank" to={`/${data.ownerLogin}/${data.projectId}`}>{detail.project && detail.project.owner.name}/{detail.project && detail.project.name}</Link>
|
||||
<div className="invite_content">
|
||||
<Link className="link" to={`/${detail.user && detail.user.login}`}>{detail.user && detail.user.name}</Link> 邀请您以{identify[detail.role]}的身份加入此代码库
|
||||
是否接受邀请?
|
||||
</div>
|
||||
</Modal>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default Invite;
|
|
@ -0,0 +1,100 @@
|
|||
.invite_development {
|
||||
font-family: PingFang SC;
|
||||
height: 366px;
|
||||
background-image: linear-gradient(
|
||||
359.37deg,
|
||||
#ebf3ff 0%,
|
||||
#eff5ff 55.01%,
|
||||
#cfdeff 100%
|
||||
);
|
||||
border: 1.5px solid #ffffff;
|
||||
border-radius: 4px;
|
||||
|
||||
.ant-modal-close{
|
||||
top:0 !important;
|
||||
}
|
||||
.ant-modal-close-x{
|
||||
font-size: 30px;
|
||||
color: #666;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
transform: scaleX(1.05);
|
||||
}
|
||||
.ant-modal-content {
|
||||
background-color: inherit;
|
||||
}
|
||||
.ant-modal-header {
|
||||
background: inherit;
|
||||
border: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.ownerImage {
|
||||
position: relative;
|
||||
top:-66px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: #e9f1ff;
|
||||
border: 1px solid #ffffff;
|
||||
border-radius: 50%;
|
||||
margin:0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
img{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.invite_project {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.invite_content {
|
||||
width: 422px;
|
||||
padding: 24px 48px;
|
||||
background-color: rgba(225, 231, 255, 0.71);
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
margin: 10px auto;
|
||||
font-weight: 500;
|
||||
font-size: 17px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
color: #151d40;
|
||||
}
|
||||
.link {
|
||||
cursor: pointer;
|
||||
color: $primary-color;
|
||||
&:hover {
|
||||
color: $primary-color-hover;
|
||||
}
|
||||
}
|
||||
.ant-modal-footer {
|
||||
text-align: center;
|
||||
border: 0;
|
||||
padding-bottom: 55px;
|
||||
button {
|
||||
width: 150px;
|
||||
height: 42px;
|
||||
border-radius: 5px;
|
||||
font-size: 15px;
|
||||
&:first-child {
|
||||
background-color: rgba(196, 0, 14, 0.07);
|
||||
border: 1px solid;
|
||||
border-color: #f60011;
|
||||
color: #f60011;
|
||||
&:hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
& + button {
|
||||
margin-left: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -333,6 +333,8 @@ function CoderDepot(props){
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
let n = fileInfo && fileInfo.name;
|
||||
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
|
||||
|
||||
|
@ -443,6 +445,7 @@ function CoderDepot(props){
|
|||
issuesFlag &&
|
||||
<a onClick={createIssue}>+ 疑修</a>
|
||||
}
|
||||
|
||||
</div>
|
||||
{ fileOperate &&
|
||||
<Dropdown
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Link, Route, Switch } from 'react-router-dom';
|
|||
import { Content, AlignTop } from '../Component/layout';
|
||||
import DetailBanner from './sub/DetailBanner';
|
||||
import cookie from 'react-cookies';
|
||||
import { Base64 } from 'js-base64';
|
||||
import '../css/index.scss'
|
||||
import './list.scss';
|
||||
|
||||
|
@ -145,6 +146,14 @@ const WikiEdit = Loadable({
|
|||
loader: () => import('../Wiki/EditWiki'),
|
||||
loading: Loading,
|
||||
});
|
||||
const Invite = Loadable({
|
||||
loader: () => import('../Invite/Index'),
|
||||
loading: Loading,
|
||||
});
|
||||
const Review = Loadable({
|
||||
loader: () => import('../Newfile/codeReview'),
|
||||
loading: Loading,
|
||||
});
|
||||
/**
|
||||
* permission:Manager:管理员,Reporter:报告人员(只有读取权限),Developer:开发人员(除不能设置仓库信息外)
|
||||
*/
|
||||
|
@ -227,12 +236,12 @@ class Detail extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
clearIssueCookies=(history)=>{
|
||||
clearIssueCookies = (history) => {
|
||||
const { pathname } = history;
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const { projectsId, owner } = this.props.match.params;
|
||||
let currentIssue = pathname.indexOf(`/${owner}/${projectsId}/issues`) === -1;
|
||||
if (currentIssue) {
|
||||
cookie.save('states', undefined,{ expires: 0,path:`/` });
|
||||
cookie.save('states', undefined, { expires: 0, path: `/` });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,15 +313,15 @@ class Detail extends Component {
|
|||
},
|
||||
disconnected: () => {
|
||||
console.log("###### cannot connected! ######");
|
||||
},
|
||||
},
|
||||
received: data => {
|
||||
console.log(`###### ---received data--- ######`);
|
||||
console.log(data);
|
||||
if (data) {
|
||||
if(deleteFlag){
|
||||
if (deleteFlag) {
|
||||
this.props.showNotification("镜像同步成功!");
|
||||
window.location.reload();
|
||||
}else{
|
||||
} else {
|
||||
if (data.project && data.project.mirror_status === 2) {
|
||||
this.deleteProjectBack();
|
||||
}
|
||||
|
@ -326,20 +335,20 @@ class Detail extends Component {
|
|||
cable.subscriptions.consumer.disconnect();
|
||||
}
|
||||
},
|
||||
onerror:()=>{
|
||||
onerror: () => {
|
||||
console.log("###### cannot connected! ######");
|
||||
}
|
||||
});
|
||||
|
||||
this.timerChannel = setTimeout(this.reloadDetail,5000);
|
||||
this.timerChannel = setTimeout(this.reloadDetail, 5000);
|
||||
}
|
||||
|
||||
reloadDetail=()=>{
|
||||
if(this.state.firstSync||this.state.secondSync){
|
||||
reloadDetail = () => {
|
||||
if (this.state.firstSync || this.state.secondSync) {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
deleteProjectBack = () => {
|
||||
const { history } = this.props;
|
||||
|
@ -364,22 +373,33 @@ class Detail extends Component {
|
|||
axios.get(url).then((result) => {
|
||||
if (result && result.data) {
|
||||
if (result.data.status === 404) {
|
||||
this.props.history.push('/nopage');
|
||||
if (window.location.pathname.includes('/invite')) {
|
||||
let inviteString = window.location.search && window.location.search.split('?invite=')[1];
|
||||
let data = inviteString && JSON.parse(Base64.decode(inviteString));
|
||||
const { project = {}, projectDetail = {} } = this.state
|
||||
this.setState({
|
||||
project: Object.assign(project, { author: { name: data.ownerName } }),
|
||||
projectDetail: Object.assign(projectDetail, { name: data.projectName })
|
||||
});
|
||||
} else {
|
||||
this.props.history.push('/nopage');
|
||||
}
|
||||
} else {
|
||||
this.setState({
|
||||
projectDetail: result.data,
|
||||
project_id: result.data.project_id,
|
||||
isManager: result.data.permission && (result.data.permission === "Manager" || result.data.permission === "Admin" || result.data.permission === "Owner"),
|
||||
isReporter: result.data.permission && result.data.permission === "Reporter",
|
||||
isDeveloper: result.data.permission && result.data.permission === "Developer",
|
||||
http_url: result.data.clone_url,
|
||||
praised: result.data.praised,
|
||||
watched: result.data.watched,
|
||||
watchers_count: result.data.watchers_count,
|
||||
praises_count: result.data.praises_count,
|
||||
forked_count: result.data.forked_count,
|
||||
defaultBranch: result.data.default_branch
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
projectDetail: result.data,
|
||||
project_id: result.data.project_id,
|
||||
isManager: result.data.permission && (result.data.permission === "Manager" || result.data.permission === "Admin" || result.data.permission === "Owner"),
|
||||
isReporter: result.data.permission && result.data.permission === "Reporter",
|
||||
isDeveloper: result.data.permission && result.data.permission === "Developer",
|
||||
http_url: result.data.clone_url,
|
||||
praised: result.data.praised,
|
||||
watched: result.data.watched,
|
||||
watchers_count: result.data.watchers_count,
|
||||
praises_count: result.data.praises_count,
|
||||
forked_count: result.data.forked_count,
|
||||
defaultBranch: result.data.default_branch
|
||||
})
|
||||
}
|
||||
}).catch((error) => { })
|
||||
}
|
||||
|
@ -464,7 +484,7 @@ class Detail extends Component {
|
|||
const url = `/${owner}/${projectsId}/forks.json`;
|
||||
axios.post(url).then(result => {
|
||||
if (result && result.data.status === 0) {
|
||||
if(result.data.message === "fork失败,你已拥有了这个项目"){
|
||||
if (result.data.message === "fork失败,你已拥有了这个项目") {
|
||||
this.props.history.push(`/${current_user && current_user.login}/${projectsId}`);
|
||||
return;
|
||||
}
|
||||
|
@ -490,7 +510,7 @@ class Detail extends Component {
|
|||
axios.post(url).then(result => {
|
||||
if (result && result.data && result.data.status === 0) {
|
||||
this.setState({
|
||||
secondSync:true
|
||||
secondSync: true
|
||||
})
|
||||
this.canvasChannel(true);
|
||||
} else {
|
||||
|
@ -527,10 +547,9 @@ class Detail extends Component {
|
|||
let pathname = checkPathname(projectsId, owner, url);
|
||||
|
||||
const { state } = this.props.history.location;
|
||||
|
||||
const common = {
|
||||
getDetail: this.getDetail,
|
||||
getBanner:this.getBanner,
|
||||
getBanner: this.getBanner,
|
||||
changeOpenDevops: this.changeOpenDevops,
|
||||
defaultBranch
|
||||
}
|
||||
|
@ -549,7 +568,7 @@ class Detail extends Component {
|
|||
<Link to={`/${owner}/${projectsId}`} className="projectN mt6">{projectDetail && projectDetail.name}</Link>
|
||||
</div>
|
||||
{projectDetail && projectDetail.private && <span className="privateTag mt6">私有</span>}
|
||||
{ !platform && <span className="privateTag red mt6">只读</span> }
|
||||
{!platform && <span className="privateTag red mt6">只读</span>}
|
||||
</AlignTop>
|
||||
<div className="mt8">
|
||||
{
|
||||
|
@ -718,7 +737,7 @@ class Detail extends Component {
|
|||
(props) => (<Setting {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
|
||||
{/*修改里程碑*/}
|
||||
<Route path="/:owner/:projectsId/milestones/:meilid/edit"
|
||||
render={
|
||||
|
@ -758,18 +777,18 @@ class Detail extends Component {
|
|||
{/* 修改详情 edit*/}
|
||||
<Route path="/:owner/:projectsId/issues/:orderId/edit"
|
||||
render={
|
||||
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"edit"}/>)
|
||||
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"edit"} />)
|
||||
}
|
||||
></Route>
|
||||
{/* 复制详情 copyetail*/}
|
||||
<Route path="/:owner/:projectsId/issues/:orderId/copyetail"
|
||||
render={
|
||||
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"copy"}/>)
|
||||
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"copy"} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
{/* 任务详情 */}
|
||||
<Route path="/:owner/:projectsId/issues/:orderId"
|
||||
|
||||
{/* 任务详情 */}
|
||||
<Route path="/:owner/:projectsId/issues/:orderId"
|
||||
render={
|
||||
(props) => (<OrderDetail {...this.props} {...this.state} {...props} {...common} />)
|
||||
}
|
||||
|
@ -802,6 +821,12 @@ class Detail extends Component {
|
|||
(props) => (<UpdateMerge {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
{/* review */}
|
||||
<Route path="/:owner/:projectsId/pulls/:mergeId/review"
|
||||
render={
|
||||
(props) => (<Review {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/:owner/:projectsId/pulls/:mergeId"
|
||||
render={
|
||||
(props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />)
|
||||
|
@ -861,6 +886,13 @@ class Detail extends Component {
|
|||
(props) => (<CoderDepot {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
{/* 邀请 */}
|
||||
<Route path="/:owner/:projectsId/invite"
|
||||
render={
|
||||
(props) => (<Invite {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
<Route path="/:owner/:projectsId/:subIndex"
|
||||
render={
|
||||
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)
|
||||
|
|
|
@ -293,6 +293,12 @@ class MessageCount extends Component {
|
|||
)
|
||||
}
|
||||
|
||||
codeReview=()=>{
|
||||
const {history,match}=this.props;
|
||||
const { projectsId, mergeId , owner } =match.params;
|
||||
history.push(`/${owner}/${projectsId}/pulls/${mergeId}/review`);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { projectsId, mergeId , owner } = this.props.match.params;
|
||||
|
||||
|
@ -449,6 +455,18 @@ class MessageCount extends Component {
|
|||
编辑
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{operate && (
|
||||
<Button
|
||||
type="blue"
|
||||
ghost
|
||||
className="ml20"
|
||||
onClick={this.codeReview}
|
||||
>
|
||||
代码评审
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{operate && (
|
||||
<Button
|
||||
type="danger"
|
||||
|
|
|
@ -117,6 +117,11 @@ form .ant-cascader-picker, form .ant-select {
|
|||
border:1px solid #28BD6C;
|
||||
color: #28BD6C;
|
||||
}
|
||||
/* 绿色按钮-type="blue" */
|
||||
.ant-btn.ant-btn-blue{
|
||||
border:1px solid #466aff;
|
||||
color: #466aff;
|
||||
}
|
||||
.copyTab{
|
||||
min-width: 370px;
|
||||
padding:0px 18px 22px 18px;
|
||||
|
|
|
@ -19,7 +19,7 @@ function CloudIDE({download_url}) {
|
|||
if (commands) {
|
||||
await commands.executeCommand(
|
||||
'ChangeThemePlugin.changeTheme',
|
||||
v ? 'ide-light' : 'ide-dark'
|
||||
v ? 'opensumi-light' : 'opensumi-dark'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,9 @@ function CloudIDE({download_url}) {
|
|||
plugins: [changeThemePlugin],
|
||||
workspaceDir: `${group}/${repo}`,
|
||||
defaultPreferences: {
|
||||
// 默认主题,如果暗色为 ide-dark
|
||||
// 'general.theme': 'ide-light',
|
||||
'general.theme': 'ide-light',
|
||||
// 默认主题,如果暗色为 opensumi-dark
|
||||
// 'general.theme': 'opensumi-light',
|
||||
'general.theme': 'opensumi-light',
|
||||
// 编辑器只读
|
||||
'editor.forceReadOnly': true,
|
||||
// 最后一行禁止继续滚动
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
module.exports = {
|
||||
extension: {
|
||||
publisher: 'cloud-ide-ext',
|
||||
name: 'editor-plugin-blame',
|
||||
version: '0.2.5',
|
||||
},
|
||||
packageJSON: {
|
||||
name: 'editor-plugin-blame',
|
||||
publisher: 'cloud-ide-ext',
|
||||
version: '0.2.5',
|
||||
repository: {
|
||||
type: 'git',
|
||||
url: 'http://code.alipay.com/yxy167584/editor-plugin-blame.git',
|
||||
},
|
||||
displayName: 'editor-plugin-blame',
|
||||
description: ' ',
|
||||
activationEvents: ['*'],
|
||||
kaitianContributes: {
|
||||
workerMain: './out/worker/index.js',
|
||||
},
|
||||
contributes: {
|
||||
commands: [
|
||||
{
|
||||
command: 'code.blame.toggleBlame',
|
||||
title: '查看blame',
|
||||
},
|
||||
{
|
||||
command: 'code.blame.acrToggleBlame',
|
||||
title: 'blame',
|
||||
},
|
||||
{
|
||||
command: 'code.blame.linktocommit',
|
||||
title: 'hover详情跳转',
|
||||
},
|
||||
],
|
||||
views: {},
|
||||
menus: {
|
||||
'editor/title': [
|
||||
{
|
||||
command: 'code.blame.acrToggleBlame',
|
||||
type: 'checkbox',
|
||||
group: 'navigation',
|
||||
toggledWhen: 'acr_blame_context',
|
||||
when: 'resourceScheme =~ /^git$|^diff$/',
|
||||
},
|
||||
],
|
||||
},
|
||||
workerMain: './out/worker/index.js',
|
||||
},
|
||||
},
|
||||
pkgNlsJSON: {},
|
||||
nlsList: [],
|
||||
extendConfig: {},
|
||||
webAssets: ['package.json', 'out/worker/index.js'],
|
||||
};
|
|
@ -0,0 +1,72 @@
|
|||
import { DependencyList, useEffect, useState, useMemo } from 'react';
|
||||
// @ts-ignore
|
||||
import { useEventEmitter, usePrevious } from 'ahooks';
|
||||
// @ts-ignore
|
||||
import sha1 from 'sha1';
|
||||
// @ts-ignore
|
||||
import differenceBy from 'lodash/differenceBy';
|
||||
|
||||
|
||||
export function useRequest(
|
||||
factory,
|
||||
options
|
||||
) {
|
||||
const { deps = [], initial, ready = true } = options || {};
|
||||
const [val, setVal] = useState(initial);
|
||||
useEffect(() => {
|
||||
if (!ready) return;
|
||||
let cancel = false;
|
||||
const promise = factory();
|
||||
if (promise === undefined || promise === null) return;
|
||||
promise
|
||||
.then((val) => {
|
||||
if (!cancel) {
|
||||
setVal(val);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
return () => {
|
||||
cancel = true;
|
||||
};
|
||||
}, [...deps, ready]);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
export function useFileReadMarkChange$(
|
||||
diffs,
|
||||
readMarks
|
||||
) {
|
||||
const fileReadMarkChange$ = useEventEmitter();
|
||||
const filePathShaMap = useMemo(() => {
|
||||
const map = new Map();
|
||||
for (const diff of diffs) {
|
||||
map.set(sha1(diff.name||diff.newPath), diff.name||diff.newPath);
|
||||
}
|
||||
return map;
|
||||
}, [diffs]);
|
||||
|
||||
const prevReadMarks = usePrevious(readMarks);
|
||||
useEffect(() => {
|
||||
const changesA = differenceBy(
|
||||
prevReadMarks,
|
||||
readMarks,
|
||||
(readMark) => readMark.filePathSha
|
||||
);
|
||||
const changesB = differenceBy(
|
||||
readMarks,
|
||||
prevReadMarks,
|
||||
(readMark) => readMark.filePathSha
|
||||
);
|
||||
for (const change of [...changesA, ...changesB]) {
|
||||
const filePath = filePathShaMap.get(change.filePathSha);
|
||||
if (filePath) {
|
||||
fileReadMarkChange$.emit(filePath);
|
||||
}
|
||||
}
|
||||
}, [readMarks, filePathShaMap]);
|
||||
|
||||
return fileReadMarkChange$;
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
// import { default as AntcodeCR } from 'acr';
|
||||
import { ACR } from 'alex';
|
||||
import { Button, Switch } from 'antd';
|
||||
// blame worker扩展
|
||||
import CodeBlame from './extensions/alex-ext-public.editor-plugin-blame.js';
|
||||
// 通过在plugin内调用扩展中的命令来进行外部交互
|
||||
import CodeBlamePlugin, { ExtensionCommand } from './plugins/code-blame.plugin';
|
||||
import {
|
||||
Provider,
|
||||
useGlobal,
|
||||
usePr,
|
||||
useNote,
|
||||
useReadMark,
|
||||
useAcr,
|
||||
useSetting,
|
||||
} from './model';
|
||||
import {
|
||||
DiscussionItem,
|
||||
Commenting,
|
||||
Menubar,
|
||||
AnnotationEntry,
|
||||
PRMoreActionLinks,
|
||||
} from './mock/component/index';
|
||||
import { projectService } from './mock/project.service';
|
||||
import { lsifService } from './mock/lsif.service';
|
||||
import { useFileReadMarkChange$ } from './hooks';
|
||||
import { repoService } from './mock/repo.service';
|
||||
|
||||
function findKey(obj) {
|
||||
// console.log('...............')
|
||||
for (const key in obj) {
|
||||
if (key == 'path') {
|
||||
console.log('path');
|
||||
console.log(obj);
|
||||
}
|
||||
let type = Object.prototype.toString.call(obj[key]);
|
||||
if (type.includes('Array')) {
|
||||
for (const item of obj[key]) {
|
||||
findKey(obj[key]);
|
||||
}
|
||||
} else if (type.includes('Object')) {
|
||||
findKey(obj[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CodeReview = (props) => {
|
||||
const { match: { params: { owner, projectsId, mergeId } }, projectDetail,current_user } = props;
|
||||
const [visible, setVisible] = React.useState(true);
|
||||
const [count, setCount] = React.useState(0);
|
||||
const [isFullscreen, setFullscreen] = React.useState(false);
|
||||
const { locale, setLocale, gbk, setGBK } = useSetting();
|
||||
const { commentPack } = useNote();
|
||||
const { project, user, setCurrent_user, setProjectDetail } = useGlobal();
|
||||
const { pr } = usePr();
|
||||
|
||||
console.log('projectDetail');
|
||||
console.log(projectDetail);
|
||||
console.log(current_user);
|
||||
console.log('project');
|
||||
console.log(project);
|
||||
console.log(user);
|
||||
console.log('user');
|
||||
|
||||
const {
|
||||
getFileReadStatus: _getFileReadStatus,
|
||||
markFileAsRead,
|
||||
markFileAsUnread,
|
||||
readMarks,
|
||||
} = useReadMark();
|
||||
// todo
|
||||
// const getFileReadStatus = usePersistFn(_getFileReadStatus);
|
||||
const getFileReadStatus = _getFileReadStatus;
|
||||
const {
|
||||
diffsPack,
|
||||
getDiffById,
|
||||
getFileContent,
|
||||
IDEMode,
|
||||
toggleViewerType,
|
||||
annotationPacks,
|
||||
} = useAcr();
|
||||
const fileReadMarkChange$ = useFileReadMarkChange$(
|
||||
diffsPack && diffsPack.diffs || [],
|
||||
readMarks
|
||||
);
|
||||
|
||||
// 插件激活
|
||||
const [pluginActivated, setPluginActivated] = useState(false);
|
||||
const blamePlugin = useMemo(() => {
|
||||
return new CodeBlamePlugin(
|
||||
() => setPluginActivated(true),
|
||||
(commitId) =>
|
||||
window.open(
|
||||
`/${owner}/${projectsId}/commit/${commitId}`
|
||||
),
|
||||
// 获取blame数据
|
||||
(projectId, commitId, path) =>
|
||||
repoService.getCodeBlame(projectId, commitId, path)
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pluginActivated) {
|
||||
return;
|
||||
}
|
||||
const projectData = {
|
||||
projectId: projectsId,
|
||||
prevSha: diffsPack.fromVersion && diffsPack.fromVersion.headCommitSha || diffsPack.toVersion.baseCommitSha,
|
||||
nextSha: diffsPack.toVersion.headCommitSha,
|
||||
};
|
||||
blamePlugin.commands && blamePlugin.commands.executeCommand(
|
||||
ExtensionCommand.setProjectData,
|
||||
projectData
|
||||
);
|
||||
}, [pluginActivated, diffsPack]);
|
||||
|
||||
if (!diffsPack) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const propsMock = {
|
||||
// 评审
|
||||
noteIdToReplyIdSet: commentPack.noteIdToReplyIdSet,
|
||||
// 增加的总行数
|
||||
addLineNum: diffsPack.addLineNum,
|
||||
// 删除的总行数
|
||||
deleteLineNum: diffsPack.delLineNum,
|
||||
// 上一个sha
|
||||
prevSha:
|
||||
diffsPack.fromVersion && diffsPack.fromVersion.headCommitSha || diffsPack.toVersion.baseCommitSha,
|
||||
// 下一个 sha
|
||||
nextSha: diffsPack.toVersion.headCommitSha,
|
||||
toggleViewerType,
|
||||
DiscussionItem,
|
||||
Commenting,
|
||||
getFileContent,
|
||||
lineToNoteIdSet: commentPack.lineToNoteIdSet,
|
||||
noteIdToNote: commentPack.noteIdToNote,
|
||||
noteUpdateFlag: commentPack.updateFlag,
|
||||
getDiffById,
|
||||
diffs: diffsPack.diffs,
|
||||
latestCommitSha: pr.diff.headCommitSha,
|
||||
projectId: project.id,
|
||||
projectPath: project.pathWithNamespace,
|
||||
pullRequestId: pr.id,
|
||||
pr,
|
||||
// 获取主语言
|
||||
getLanguages: () =>
|
||||
projectService
|
||||
.getLanguages(project.id, {
|
||||
aggBy: 'file_extension',
|
||||
// 按照语言文件个数排序
|
||||
orderBy: 'count',
|
||||
size: 20,
|
||||
})
|
||||
.then((res) => res && Object.keys(res)),
|
||||
getFileReadStatus,
|
||||
fileReadMarkChange$,
|
||||
markFileAsRead,
|
||||
markFileAsUnread,
|
||||
bulkChangeFiles: (actions, header) => {
|
||||
|
||||
let files = [];
|
||||
for (const item of actions) {
|
||||
files.push({
|
||||
action_type: item.actionType,
|
||||
content: item.content,
|
||||
encoding: item.encoding,
|
||||
file_path: item.filePath,
|
||||
});
|
||||
}
|
||||
let data = {
|
||||
files: files,
|
||||
// author_email: header.authorEmail,
|
||||
// author_name: header.authorName,
|
||||
// author_timeunix: 1658214400,
|
||||
committer_email: header.authorEmail,
|
||||
committer_name: header.authorName,
|
||||
// committer_timeunix: 1658214400,
|
||||
branch: header.branch,
|
||||
message: header.commitMessage,
|
||||
};
|
||||
console.log('bulkChangeFiles');
|
||||
console.log(actions);
|
||||
console.log(header);
|
||||
console.log(data);
|
||||
projectService.bulkChangeFiles(projectsId, owner, data)
|
||||
},
|
||||
// 菜单栏
|
||||
Menubar: () => (
|
||||
<Menubar
|
||||
initialFullscreen={isFullscreen}
|
||||
handleFullscreenChange={setFullscreen}
|
||||
toggleViewerType={toggleViewerType}
|
||||
logFullScreen={(bool) => console.log('>>>logFullScreen', bool)}
|
||||
/>
|
||||
),
|
||||
user,
|
||||
lsifService,
|
||||
defaultEncoding: project.encoding,
|
||||
// 初始化 编码
|
||||
encoding: gbk ? 'gbk' : 'utf-8',
|
||||
// 设置编码
|
||||
setEncoding: (val) => {
|
||||
setGBK(val === 'gbk');
|
||||
},
|
||||
locale,
|
||||
annotations: annotationPacks,
|
||||
AnnotationEntry,
|
||||
PRMoreActionLinks,
|
||||
// 全屏模式
|
||||
isFullscreen,
|
||||
appConfig: {
|
||||
// 插件 在插件中调用扩展的命令
|
||||
plugins: [blamePlugin],
|
||||
// 扩展
|
||||
extensionMetadata: [CodeBlame],
|
||||
},
|
||||
};
|
||||
|
||||
const IDEContainerStyle = {
|
||||
position: isFullscreen ? 'fixed' : 'static',
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: '100%',
|
||||
height: isFullscreen ? '100vh' : 'calc(100vh - 72px)',
|
||||
zIndex: 1100,
|
||||
};
|
||||
|
||||
console.log('propsMock:');
|
||||
console.log(propsMock);
|
||||
// findKey(propsMock);
|
||||
return (
|
||||
<div style={{ height: '100%' }}>
|
||||
{/* <div className="controller">
|
||||
{!IDEMode && (
|
||||
<>
|
||||
IDE 模式: <Switch checked={IDEMode} onChange={toggleViewerType} />
|
||||
</>
|
||||
)}
|
||||
</div> */}
|
||||
{/* <div className="pr-head">
|
||||
<div>{pr.description}</div>
|
||||
<div>
|
||||
评审人:
|
||||
{pr.review && pr.review.reviewers && pr.review.reviewers.map((r) => (
|
||||
<span style={{ marginRight: 4 }} key={r.id}>
|
||||
{r.name}
|
||||
</span>
|
||||
))}
|
||||
<span style={{ marginRight: 24 }}></span>
|
||||
合并人:{pr.assignee && pr.assignee.name}
|
||||
</div>
|
||||
</div> */}
|
||||
{IDEMode && (
|
||||
<div style={IDEContainerStyle}>
|
||||
{visible && <ACR {...propsMock} key={count} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const CodeReviewP = (props) => {
|
||||
return (
|
||||
<Provider {...props}><CodeReview {...props} /></Provider>
|
||||
)
|
||||
}
|
||||
export default CodeReviewP;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export const group = 'Gitlink';
|
||||
export const repo = 'forgeplus';
|
||||
export const prIid = '2';
|
||||
|
||||
export const project = `${group}/${repo}`;
|
|
@ -0,0 +1,67 @@
|
|||
import { request } from './request/index';
|
||||
|
||||
export const apiService = {
|
||||
get(
|
||||
url,
|
||||
query,
|
||||
options = {}
|
||||
) {
|
||||
return request(
|
||||
url,
|
||||
{
|
||||
method: 'GET',
|
||||
params: query,
|
||||
},
|
||||
options
|
||||
);
|
||||
},
|
||||
|
||||
post(
|
||||
url,
|
||||
query,
|
||||
body,
|
||||
options= {}
|
||||
) {
|
||||
return request(
|
||||
url,
|
||||
{
|
||||
method: 'POST',
|
||||
params: query,
|
||||
data: body,
|
||||
},
|
||||
options
|
||||
);
|
||||
},
|
||||
|
||||
put(
|
||||
url,
|
||||
query,
|
||||
body,
|
||||
options= {}
|
||||
) {
|
||||
return request(
|
||||
url,
|
||||
{
|
||||
method: 'PUT',
|
||||
params: query,
|
||||
data: body,
|
||||
},
|
||||
options
|
||||
);
|
||||
},
|
||||
|
||||
delete(
|
||||
url,
|
||||
query,
|
||||
options = {}
|
||||
) {
|
||||
return request(
|
||||
url,
|
||||
{
|
||||
method: 'DELETE',
|
||||
params: query,
|
||||
},
|
||||
options
|
||||
);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,538 @@
|
|||
import React, {
|
||||
FC,
|
||||
MutableRefObject,
|
||||
memo,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
useCallback,
|
||||
Fragment,
|
||||
} from "react";
|
||||
import {
|
||||
message,
|
||||
Dropdown,
|
||||
Icon,
|
||||
Menu,
|
||||
Button,
|
||||
Tooltip,
|
||||
Avatar,
|
||||
Modal,
|
||||
Switch,
|
||||
Input,
|
||||
Popconfirm,
|
||||
Checkbox,
|
||||
} from "antd";
|
||||
// @ts-ignore
|
||||
import { usePersistFn } from "ahooks";
|
||||
import "./style.module.less";
|
||||
import { useAcr, useNote, useGlobal } from "../../model";
|
||||
const AnnotationStatus ={
|
||||
Init : 'Init',
|
||||
Ignore : 'Ignore',
|
||||
Confirm : 'Confirm',
|
||||
FalsePositive : 'FalsePositive',
|
||||
}
|
||||
const noteType= {
|
||||
comment : 'Comment',
|
||||
problem : 'Problem',
|
||||
}
|
||||
|
||||
export const Commenting = (props) => {
|
||||
const { user } = useGlobal();
|
||||
const { addComment } = useNote();
|
||||
const { toVersion } = useAcr();
|
||||
console.log('Commenting--props');
|
||||
console.log(props);
|
||||
const submit = usePersistFn(async (note, type) => {
|
||||
try {
|
||||
await addComment({
|
||||
note,
|
||||
diffId: toVersion.id,
|
||||
lineCode: props.lineCode,
|
||||
discussionId: props.discussionId,
|
||||
path: props.path,
|
||||
type,
|
||||
});
|
||||
props.onClose&&props.onClose();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
const { replyNote } = props;
|
||||
|
||||
const [note, setNote] = useState("");
|
||||
const [isProblem, setIsProblem] = useState(false);
|
||||
|
||||
const onSubmit = useCallback(() => {
|
||||
const submitNote = replyNote ? insertReplyNote(note, replyNote) : note;
|
||||
submit(submitNote, isProblem ? noteType.problem : noteType.comment);
|
||||
}, [note, replyNote, isProblem]);
|
||||
|
||||
function insertReplyNote(baseNote, replyNote) {
|
||||
if (replyNote&&replyNote.note) {
|
||||
const replyLine = replyNote.note.split("\n");
|
||||
const blockquoteNote = [
|
||||
`<!-- reply id="${replyNote.id}" -->`,
|
||||
...replyLine,
|
||||
]
|
||||
.map((note) => `> ${note}`)
|
||||
.join("\n");
|
||||
return blockquoteNote + "\n\n" + baseNote;
|
||||
} else {
|
||||
return baseNote;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="arc-container">
|
||||
<div
|
||||
className={`arc-commenting-container ${props.noPadding ? "no-padding" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="avatar">
|
||||
<Tooltip title={user.name}>
|
||||
<Avatar src={user.avatarUrl} size={24} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Input.TextArea
|
||||
autoFocus
|
||||
placeholder="请输入描述信息"
|
||||
value={note}
|
||||
onChange={(e) => setNote(e.target.value)}
|
||||
style={{ height: 150 }}
|
||||
/>
|
||||
<div className="action">
|
||||
<div>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={onSubmit}
|
||||
disabled={!note.trim()}
|
||||
style={{ marginRight: 8 }}
|
||||
>
|
||||
开始评审
|
||||
</Button>
|
||||
{note.trim() ? (
|
||||
<Popconfirm
|
||||
title="你确定要取消?"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
onConfirm={props.onClose}
|
||||
>
|
||||
<Button>取消</Button>
|
||||
</Popconfirm>
|
||||
) : (
|
||||
<Button onClick={props.onClose}>取消</Button>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{replyNote && (
|
||||
<div className="reply-note">Reply to {replyNote.author.name}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DiscussionItem = memo((props) => {
|
||||
const { noteId } = props;
|
||||
const { commentPack } = useNote();
|
||||
|
||||
const [commenting, setCommenting] = useState(false);
|
||||
const [commentReply, setCommentReply] = useState();
|
||||
|
||||
const replyIdSet = commentPack.noteIdToReplyIdSet.get(noteId);
|
||||
const replyIds = replyIdSet
|
||||
? Array.from(replyIdSet).sort((a, b) => a - b)
|
||||
: null;
|
||||
|
||||
const pending = (function () {
|
||||
if (!commentPack.hasPendingReview) {
|
||||
return false;
|
||||
}
|
||||
const reviewId = commentPack.noteIdToReviewId.get(noteId);
|
||||
if (!reviewId) return false;
|
||||
const review = reviewId ? commentPack.reviewIdToReview.get(reviewId) : null;
|
||||
if (!review) return false;
|
||||
return review.pending;
|
||||
})();
|
||||
const hasPendingNote = (function () {
|
||||
if (pending) return true;
|
||||
if (replyIds) {
|
||||
for (const replyId of replyIds) {
|
||||
const reviewId = commentPack.noteIdToReviewId.get(replyId);
|
||||
if (reviewId && commentPack.reviewIdToReview.get(reviewId)&&commentPack.reviewIdToReview.get(reviewId).pending) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
const handleReply = (replyNote) => {
|
||||
if (replyNote) {
|
||||
setCommentReply(replyNote);
|
||||
}
|
||||
setCommenting(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!commenting) {
|
||||
setCommentReply(undefined);
|
||||
}
|
||||
}, [commenting]);
|
||||
|
||||
const note = commentPack.noteIdToNote.get(noteId);
|
||||
if (!note) return null;
|
||||
|
||||
return (
|
||||
<div className="arc-container">
|
||||
<div>
|
||||
{note.type === noteType.problem && (
|
||||
<Fragment>
|
||||
{note.state === "resolved" && !pending && "已解决"}
|
||||
{note.state === "opened" && !pending && "待解决"}
|
||||
{pending && "需要回应"}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ paddingLeft: 16 }}>
|
||||
<div>{note.author.name} 发表评论</div>
|
||||
<div>{note.note}</div>
|
||||
</div>
|
||||
{replyIds && (
|
||||
<div style={{ paddingLeft: 32 }}>
|
||||
{replyIds.map((replyId) => {
|
||||
const note = commentPack.noteIdToNote.get(replyId);
|
||||
if (!note) return null;
|
||||
return (
|
||||
<div key={replyId}>
|
||||
<div>{note.author.name} 发表评论</div>
|
||||
<div>{note.note}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const Menubar = memo((props) => {
|
||||
const [isFullscreen, setFullscreen] = React.useState(
|
||||
props.initialFullscreen || false
|
||||
);
|
||||
const handleChangeFullscreen = (isFullscreen) => {
|
||||
setFullscreen(isFullscreen);
|
||||
if (isFullscreen) {
|
||||
message.info("全屏模式支持直接使用 ↑/↓ 切换变更文件");
|
||||
}
|
||||
props.handleFullscreenChange(isFullscreen);
|
||||
props.logFullScreen(isFullscreen);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (isFullscreen) {
|
||||
document.body.style.overflow = "hidden";
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = "";
|
||||
};
|
||||
}, [isFullscreen]);
|
||||
|
||||
const { project, pr } = useGlobal();
|
||||
const { versions, fromVersion, toVersion, updateQuery } = useAcr();
|
||||
|
||||
const idToVerbose = new Map();
|
||||
idToVerbose.set(0, "Base Version");
|
||||
versions.forEach((version, index) => {
|
||||
if (index === 0) {
|
||||
idToVerbose.set(version.id, `Latest Version`);
|
||||
} else {
|
||||
idToVerbose.set(version.id, `Version ${versions.length - index}`);
|
||||
}
|
||||
});
|
||||
|
||||
const fromId = fromVersion&&fromVersion.id || 0;
|
||||
const toId = toVersion&&toVersion.id || 0;
|
||||
|
||||
const filterCleared = fromId === 0 && toId === versions[0]&&versions[0].id;
|
||||
|
||||
function renderMenuItem(version, selectedId) {
|
||||
const verbose = idToVerbose.get(version.id);
|
||||
return (
|
||||
<Menu.Item
|
||||
className={`arc-menuItem ${version.id === selectedId ? "selected" : ""
|
||||
}`}
|
||||
key={version.id}
|
||||
>
|
||||
<div className='headRow'>
|
||||
<span>{verbose}</span>
|
||||
<span>{version.headCommitSha.slice(0, 8)}</span>
|
||||
</div>
|
||||
<div className='desc'>
|
||||
{ }提交,包含 {version.commitsCount},{version.filesCount} 份文件变更
|
||||
</div>
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
const menuFrom = (
|
||||
<Menu
|
||||
onClick={(e) => {
|
||||
const val = parseInt(e.key);
|
||||
updateQuery({
|
||||
from: val === 0 ? undefined : val,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{versions.slice(1).map((version) => renderMenuItem(version, fromId))}
|
||||
<Menu.Item
|
||||
key={0}
|
||||
className={`${'menuItem'} ${fromId === 0 ? 'selected' : ""}`}
|
||||
>
|
||||
Base
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const menuTo = (
|
||||
<Menu
|
||||
onClick={(e) => {
|
||||
updateQuery({
|
||||
to: parseInt(e.key),
|
||||
});
|
||||
}}
|
||||
>
|
||||
{versions
|
||||
.slice(0, versions.length)
|
||||
.map((version) => renderMenuItem(version, toId))}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="arc-menubar">
|
||||
<div className={'group'}>
|
||||
{versions.length !== 0 && (
|
||||
<div className={'versionContainer'}>
|
||||
<Dropdown overlay={menuFrom} trigger={["click"]}>
|
||||
<div className={'versionItem'}>
|
||||
<span>{idToVerbose.get(fromId)}</span>
|
||||
<Icon type="down" />
|
||||
</div>
|
||||
</Dropdown>
|
||||
<div>
|
||||
<Icon type="arrow-right" />
|
||||
</div>
|
||||
<Dropdown overlay={menuTo} trigger={["click"]}>
|
||||
<div className={'versionItem'}>
|
||||
<span>{idToVerbose.get(toId)}</span>
|
||||
<Icon type="down" />
|
||||
</div>
|
||||
</Dropdown>
|
||||
|
||||
{!filterCleared && (
|
||||
<div>
|
||||
<a
|
||||
onClick={() => {
|
||||
updateQuery({
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
});
|
||||
}}
|
||||
>
|
||||
重置
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={'group'}>
|
||||
{/* {isFullscreen ? null : (
|
||||
<div className={'switchContainer'}>
|
||||
<span className={'ideLabel'}>IDE 模式</span>
|
||||
<Tooltip
|
||||
placement="top"
|
||||
title="提供 IDE 代码浏览体验、完善的语言服务和高效的代码编辑"
|
||||
>
|
||||
<Switch checked onChange={props.toggleViewerType} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)} */}
|
||||
{isFullscreen ? (
|
||||
<Icon
|
||||
type="fullscreen-exit"
|
||||
onClick={() => handleChangeFullscreen(false)}
|
||||
/>
|
||||
) : (
|
||||
<Tooltip title="全屏">
|
||||
<Icon
|
||||
type="fullscreen"
|
||||
onClick={() => handleChangeFullscreen(true)}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const AnnotationEntry = memo((props) => {
|
||||
const { annotation, checkSuite } = props;
|
||||
|
||||
// 定位
|
||||
const anchorOffsetRef = useRef(null);
|
||||
const hash = `#annotation_${annotation.id}`;
|
||||
const hashActive = hash === location.hash;
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
if (hashActive) {
|
||||
anchorOffsetRef.current&&anchorOffsetRef.current.scrollIntoView(true);
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
|
||||
const [isIgnore, setIsIgnore] = useState(
|
||||
annotation.feedBackStatus === AnnotationStatus.Ignore
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="arc-container">
|
||||
<div
|
||||
ref={anchorOffsetRef}
|
||||
id={`annotation_${annotation.id}`}
|
||||
className="arc-annotationContainer"
|
||||
>
|
||||
<div className={'codeLine'}>
|
||||
<div>{annotation.level}</div>
|
||||
<div className={'line'}>
|
||||
{annotation.startLine === annotation.endLine ? (
|
||||
<span>
|
||||
对{annotation.endLine}
|
||||
行代码分析
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
对{annotation.startLine}到{annotation.endLine}
|
||||
行代码分析
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={'botRow'}>
|
||||
<Avatar
|
||||
alt={checkSuite.service.nameShow}
|
||||
size={20}
|
||||
src="https://gw-office.alipayobjects.com/bmw-prod/a9596840-928c-4603-a865-b24373ab4b4d.png"
|
||||
/>
|
||||
<div>
|
||||
<div className={'strong'}>{checkSuite.service.nameShow}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'content'}>
|
||||
<p>{annotation.title}</p>
|
||||
<div>{annotation.message}</div>
|
||||
</div>
|
||||
<div className={'detail'}>
|
||||
<div>
|
||||
{annotation.bugId ? (
|
||||
isIgnore ? (
|
||||
<Button type="primary" style={{ marginRight: 8 }}>
|
||||
取消忽略
|
||||
</Button>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Button style={{ marginRight: 8 }}>忽略</Button>
|
||||
<Button style={{ marginRight: 8 }}>误报</Button>
|
||||
<Button>确认</Button>
|
||||
</Fragment>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
className={'halfPlainA'}
|
||||
href={`https://codeinsightapi.alipay.com/api/v1/describe?bug_type=${annotation.bugType}&bug_id=${annotation.bugId}`}
|
||||
target="_blank"
|
||||
>
|
||||
查看问题详情 <Icon type="double-right" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const PRMoreActionLinks = ({
|
||||
setVisible,
|
||||
}) => {
|
||||
const { project, pr } = useGlobal();
|
||||
const [checkoutBranchVisible, setCheckoutBranchVisible] = useState(false);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div
|
||||
className="arc-checkoutBranch"
|
||||
onClick={() => {
|
||||
setCheckoutBranchVisible(true);
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
检出分支
|
||||
</div>
|
||||
<a
|
||||
className="arc-flexA"
|
||||
download
|
||||
href={`http://code.test.alipay.net/${project.pathWithNamespace}/pull_requests/${pr.iid}.patch`}
|
||||
>
|
||||
<span onClick={() => setVisible(false)}>下载 patch 文件</span>
|
||||
</a>
|
||||
<a
|
||||
className="arc-flexA"
|
||||
download
|
||||
href={`http://code.test.alipay.net/${project.pathWithNamespace}/pull_requests/${pr.iid}.diff`}
|
||||
>
|
||||
<span onClick={() => setVisible(false)}>下载 diff 文件</span>
|
||||
</a>
|
||||
<Modal
|
||||
title="检出,评审,并在本地执行合并"
|
||||
visible={checkoutBranchVisible}
|
||||
footer={null}
|
||||
onCancel={() => setCheckoutBranchVisible(false)}
|
||||
>
|
||||
<div className="arc-checkout">
|
||||
<p>
|
||||
<span className="fw">Step 1.</span> Fetch and check out the branch
|
||||
for this pull request
|
||||
</p>
|
||||
<pre>
|
||||
git fetch origin
|
||||
<br />
|
||||
{`git checkout -b ${pr.sourceBranch} origin/${pr.sourceBranch}`}
|
||||
</pre>
|
||||
<p>
|
||||
<span className="fw">Step 2.</span> Review the changes locally
|
||||
</p>
|
||||
<p>
|
||||
<span className="fw">Step 3.</span> Merge the branch and fix any
|
||||
conflicts that come up
|
||||
</p>
|
||||
<pre>
|
||||
git fetch origin
|
||||
<br />
|
||||
{`git checkout origin/${pr.targetBranch}`}
|
||||
<br />
|
||||
git merge --no-ff master
|
||||
</pre>
|
||||
<p>
|
||||
<span className="fw">Step 4.</span> Push the result of the merge
|
||||
</p>
|
||||
<pre>{`git push origin ${pr.targetBranch}`}</pre>
|
||||
</div>
|
||||
</Modal>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,197 @@
|
|||
|
||||
.arc-container {
|
||||
border: solid 1px #eee;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.arc-menubar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> * {
|
||||
margin-right: 12px;
|
||||
transition: 300ms all ease-in-out;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.versionContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
> *:not(:last-child) {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.versionItem {
|
||||
cursor: pointer;
|
||||
padding: 4px 6px;
|
||||
.anticon {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.switchContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ideLabel {
|
||||
line-height: 1;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.arc-menuItem {
|
||||
background: #ffffff;
|
||||
width: 350px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
&.selected {
|
||||
background: #f0f5ff;
|
||||
}
|
||||
|
||||
.headRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-top: 2px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
}
|
||||
|
||||
.arc-annotationContainer {
|
||||
border-bottom: solid 1px #eee;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.codeLine {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.line {
|
||||
padding-top: 8px;
|
||||
padding-left: 8px;
|
||||
font-size: 12px;
|
||||
&.active {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
}
|
||||
|
||||
.botRow {
|
||||
padding: 8px 12px 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
> *:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 8px 12px 12px 48px;
|
||||
p {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
|
||||
.detail {
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
padding: 0 21px;
|
||||
border-top: 1px solid #ebedf0;
|
||||
}
|
||||
|
||||
.halfPlainA {
|
||||
color: inherit;
|
||||
&:hover {
|
||||
color: #2f54eb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.arc-checkoutBranch {
|
||||
font-size: 14px;
|
||||
color: #2f54eb;
|
||||
cursor: pointer;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.arc-flexA {
|
||||
display: flex !important;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
line-height: 32px;
|
||||
color: #2f54eb;
|
||||
|
||||
i {
|
||||
color: #2f54eb;
|
||||
}
|
||||
&:hover {
|
||||
color: #2f54eb;
|
||||
}
|
||||
}
|
||||
|
||||
.arc-checkout {
|
||||
pre {
|
||||
margin-bottom: 8px;
|
||||
padding: 8px 12px;
|
||||
background-color: #fbfbfb;
|
||||
border: 1px solid #ebedf0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.arc-commenting-container {
|
||||
position: relative;
|
||||
padding: 8px 12px 0 44px;
|
||||
|
||||
&.no-padding {
|
||||
padding: 0 0 0 32px;
|
||||
|
||||
.avatar {
|
||||
top: 2px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
|
||||
.reply-note {
|
||||
padding: 1px 5px;
|
||||
color: #8c8c8c;
|
||||
line-height: 20px;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import { apiService } from './api.service';
|
||||
|
||||
// 语言服务接口
|
||||
export const lsifService = {
|
||||
async lsifExists(projectPath, sha) {
|
||||
return !!(await apiService.post(
|
||||
`/webapi/projects/${encodeURIComponent(
|
||||
projectPath
|
||||
)}/repository/lsif/exists`,
|
||||
{
|
||||
sha,
|
||||
}
|
||||
)) ;
|
||||
},
|
||||
|
||||
async lsifHover(
|
||||
projectId,
|
||||
data
|
||||
) {
|
||||
return (await apiService.post(
|
||||
`/webapi/projects/42422/repository/lsif/hover`,
|
||||
undefined,
|
||||
{
|
||||
...data,
|
||||
method: 'hover',
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
async lsifDefinitions(
|
||||
projectId,
|
||||
data
|
||||
) {
|
||||
return (await apiService.post(
|
||||
`/webapi/projects/42422/repository/lsif/definitions`,
|
||||
undefined,
|
||||
data
|
||||
))
|
||||
},
|
||||
|
||||
async lsifReferences(
|
||||
projectId,
|
||||
data
|
||||
) {
|
||||
return (await apiService.post(
|
||||
`/webapi/projects/42422/repository/lsif/references`,
|
||||
undefined,
|
||||
data,
|
||||
{
|
||||
disableBodyConvert: true,
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
async lsifReferencesV2(
|
||||
projectId,
|
||||
data
|
||||
) {
|
||||
return (await apiService.post(
|
||||
`/webapi/projects/42422/repository/lsif/reference/v2`,
|
||||
undefined,
|
||||
data,
|
||||
{
|
||||
disableBodyConvert: true,
|
||||
}
|
||||
))
|
||||
},
|
||||
};
|
|
@ -0,0 +1,186 @@
|
|||
import { apiService } from './api.service';
|
||||
|
||||
import { calcChangeLineNum } from './utils/calc-change-line-num';
|
||||
import { underscoreToCamelcase } from './utils/camelcase-convert';
|
||||
import axios from 'axios';
|
||||
|
||||
export const prService = {
|
||||
async getPRByIid(projectsId, owner, mergeId) {
|
||||
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}.json`);
|
||||
let data = underscoreToCamelcase(res.data);
|
||||
let newData = {
|
||||
...data,
|
||||
checkSuites: [],
|
||||
iid: data.index,
|
||||
sourceBranch: data.base,
|
||||
sourceProjectId: projectsId,
|
||||
// state: "opened",
|
||||
// targetBranch: "master",
|
||||
// targetProjectId: 42422,
|
||||
diff: {
|
||||
baseCommitSha: data.baseCommitSha,
|
||||
commitsCount: data.commitNum,
|
||||
headCommitSha: data.headCommitSha,
|
||||
startCommitSha: data.mergeBase,
|
||||
// delLineNum: 379,
|
||||
// addLineNum: 753,
|
||||
// createdAt: "2021-05-20T14:27:46+0800",
|
||||
// filesCount: 22,
|
||||
// overflow: false,
|
||||
// startCommitSha: "2566c6dec7756e51f7f16267d9a2a63116ac015b",
|
||||
// updatedAt: "2021-05-20T14:27:46+0800",
|
||||
},
|
||||
|
||||
};
|
||||
console.log('getPRByIid');
|
||||
console.log(newData);
|
||||
return newData;
|
||||
return (await apiService.get(
|
||||
`/webapi/projects/42422/get_pull_request_by_iid`,
|
||||
));
|
||||
},
|
||||
|
||||
async getDiffVersions(projectsId, owner, mergeId) {
|
||||
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/versions.json`);
|
||||
return Array.isArray(res.data.versions) ? underscoreToCamelcase(res.data.versions) : []
|
||||
return (await apiService.get(
|
||||
`/api/v3/projects/42422/pull_requests/13055/diffs`
|
||||
))
|
||||
},
|
||||
|
||||
async getDiffs(
|
||||
projectId,
|
||||
owner,
|
||||
from,
|
||||
to,
|
||||
) {
|
||||
const res = await axios.get(`/v1/${owner}/42422/compare.json`, { params: { from, to } })
|
||||
console.log(res);
|
||||
const changeLineNum = calcChangeLineNum(res.diffs);
|
||||
return {
|
||||
...res,
|
||||
...changeLineNum,
|
||||
};
|
||||
},
|
||||
|
||||
async getDiffOverviews(projectsId, owner, mergeId, versionId) {
|
||||
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/versions/${versionId}/diff.json`);
|
||||
let files = res.data.files;
|
||||
console.log('getDiffOverviews');
|
||||
console.log(res.data.files);
|
||||
for (const [i, item] of files.entries()) {
|
||||
// item.aMode= "100644";
|
||||
// item.bMode= "0";
|
||||
// item.markAsRead= true;
|
||||
// item.tooLarge= false;
|
||||
// item.updatedAfterRead= false;
|
||||
|
||||
item.compareDiffId = versionId;
|
||||
item.id = item.name;
|
||||
item.addLineNum = item.addition;
|
||||
item.binaryFile = item.is_bin;
|
||||
item.delLineNum = item.deletion;
|
||||
item.deletedFile = item.is_deleted;
|
||||
item.newFile = item.is_created;
|
||||
item.newPath = item.name;
|
||||
item.oldPath = item.oldname;
|
||||
item.renamedFile = item.is_renamed;
|
||||
}
|
||||
return Array.isArray(res.data.files) ? underscoreToCamelcase(res.data.files) : []
|
||||
|
||||
return (await apiService.get(
|
||||
`/webapi/projects/42422/pull_requests/13055/diffs/31918/changes_overview`
|
||||
))
|
||||
},
|
||||
|
||||
async getCommentPack(
|
||||
projectId,
|
||||
prId,
|
||||
lastFetchedAt
|
||||
) {
|
||||
return (await apiService.get(
|
||||
'/webapi/projects/42422/pull_requests/13055/comments',
|
||||
{
|
||||
lastFetchedAt,
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
async editPRComment(
|
||||
projectId,
|
||||
prId,
|
||||
noteId,
|
||||
payload
|
||||
) {
|
||||
return (await apiService.put(
|
||||
`/api/v3/projects/42422/pull_requests/13055/comments/${noteId}`,
|
||||
undefined,
|
||||
payload
|
||||
))
|
||||
},
|
||||
|
||||
async getDiffById(
|
||||
projectsId, owner, mergeId, versionId,
|
||||
options = {}
|
||||
) {
|
||||
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/versions/${versionId}/diff.json`, { params: options });
|
||||
// console.log('getDiffById');
|
||||
// console.log(res.data);
|
||||
// return underscoreToCamelcase(res.data);
|
||||
|
||||
return (await apiService.get(
|
||||
`/webapi/projects/42422/pull_requests/13055/diffs/31918/changes/1079`,
|
||||
options
|
||||
));
|
||||
// return (await apiService.get(
|
||||
// `/webapi/projects/42422/pull_requests/13055/diffs/${versionId}/changes/${diffId}`,
|
||||
// options
|
||||
// ))
|
||||
},
|
||||
|
||||
async getFileReadMarks(projectId, prId) {
|
||||
return (await apiService.get(
|
||||
`/api/v3/projects/42422/pull_requests/13055/diffs/mark_files`
|
||||
))
|
||||
},
|
||||
async markFileAsRead(projectId, prId, filePathSha) {
|
||||
return (await apiService.put(
|
||||
`/webapi/projects/42422/pull_requests/13055/diffs/mark_file_as_read`,
|
||||
{
|
||||
filePathSha,
|
||||
}
|
||||
))
|
||||
},
|
||||
async markFileAsUnread(projectId, prId, filePathSha) {
|
||||
return (await apiService.put(
|
||||
`/webapi/projects/42422/pull_requests/13055/diffs/mark_file_as_unread`,
|
||||
{
|
||||
filePathSha,
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
async addComment(projectId, prId, data) {
|
||||
return (await apiService.post(
|
||||
`/webapi/projects/42422/pull_requests/13055/comments`,
|
||||
undefined,
|
||||
data
|
||||
))
|
||||
},
|
||||
|
||||
async createReview(projectId, prId) {
|
||||
return (await apiService.post(
|
||||
`/api/v3/projects/42422/pull_requests/13055/reviews`
|
||||
))
|
||||
},
|
||||
|
||||
async commitReview(projectId, prId, body) {
|
||||
return await apiService.put(
|
||||
`/api/v3/projects/42422/pull_requests/13055/reviews`,
|
||||
undefined,
|
||||
{
|
||||
body,
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,76 @@
|
|||
import { apiService } from './api.service';
|
||||
import { underscoreToCamelcase } from './utils/camelcase-convert';
|
||||
import axios from 'axios';
|
||||
|
||||
export const projectService = {
|
||||
async getUser() {
|
||||
return apiService.get(`/api/v3/user`);
|
||||
},
|
||||
|
||||
async getProject(namespace, projectName) {
|
||||
return (await apiService.get(
|
||||
`/webapi/projects/${namespace}/${projectName}/`
|
||||
))
|
||||
},
|
||||
|
||||
async getFileBlob(projectsId, owner,options={}) {
|
||||
const res = await axios.get(`/${owner}/${projectsId}/sub_entries.json`,{params:options});
|
||||
console.log(res.data);
|
||||
return res.data.entries.content;
|
||||
},
|
||||
|
||||
// async getFileBlob(
|
||||
// projectId,
|
||||
// sha,
|
||||
// filepath,
|
||||
// options= {}
|
||||
// ) {
|
||||
// return (await apiService.get(
|
||||
// `/api/v3/projects/42422/repository/blobs/ab32441adfd6c3c381457717a42f19a7fdd6d59b`,
|
||||
// {
|
||||
// filepath,
|
||||
// ...options,
|
||||
// }
|
||||
// ))
|
||||
|
||||
// // return (await apiService.get(
|
||||
// // `/api/v3/projects/42422/repository/blobs/${encodeURIComponent(
|
||||
// // sha
|
||||
// // )}`,
|
||||
// // {
|
||||
// // filepath,
|
||||
// // ...options,
|
||||
// // }
|
||||
// // ))
|
||||
// },
|
||||
|
||||
async getLanguages(
|
||||
projectId,
|
||||
params
|
||||
) {
|
||||
return (await apiService.get(
|
||||
`/api/v4/projects/42422/languages`,
|
||||
params,
|
||||
{
|
||||
disableResponseConvert: true,
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
async bulkChangeFiles(
|
||||
projectsId, owner,data
|
||||
) {
|
||||
|
||||
const res = await axios.post(`/v1/${owner}/${projectsId}/contents/batc`,data);
|
||||
console.log('bulkChangeFiles--data');
|
||||
console.log(res);
|
||||
// return (await apiService.post(
|
||||
// `/api/v4/projects/42422/repository/files`,
|
||||
// undefined,
|
||||
// {
|
||||
// actions,
|
||||
// header,
|
||||
// }
|
||||
// ))
|
||||
},
|
||||
};
|
|
@ -0,0 +1,88 @@
|
|||
import { apiService } from './api.service';
|
||||
import { underscoreToCamelcase } from './utils/camelcase-convert';
|
||||
import axios from 'axios';
|
||||
|
||||
export const repoService = {
|
||||
async getFileList(
|
||||
projectId,
|
||||
refName,
|
||||
path,
|
||||
withCommit
|
||||
) {
|
||||
return (await apiService.get(
|
||||
`/webapi/projects/42422/repository/tree`,
|
||||
{
|
||||
refName,
|
||||
path,
|
||||
withCommit,
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
async getTreeEntry(projectId, refName, path) {
|
||||
return (await apiService.get(
|
||||
`/api/v3/projects/42422/repository/tree_entry`,
|
||||
{
|
||||
refName,
|
||||
path,
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
// async getFileDetail(
|
||||
// projectId,
|
||||
// ref,
|
||||
// filepath,
|
||||
// charsetName
|
||||
// ) {
|
||||
// return await apiService.get(
|
||||
// '/api/v3/projects/42422/repository/blobs/ab32441adfd6c3c381457717a42f19a7fdd6d59b',
|
||||
// {
|
||||
// filepath,
|
||||
// charsetName,
|
||||
// },
|
||||
// {
|
||||
// disableResponseConvert: true,
|
||||
// getOriginalResponse: true,
|
||||
// }
|
||||
// );
|
||||
// // return await apiService.get(
|
||||
// // `/api/v3/projects/42422/repository/blobs/${encodeURIComponent(
|
||||
// // ref
|
||||
// // )}`,
|
||||
// // {
|
||||
// // filepath,
|
||||
// // charsetName,
|
||||
// // },
|
||||
// // {
|
||||
// // disableResponseConvert: true,
|
||||
// // getOriginalResponse: true,
|
||||
// // }
|
||||
// // );
|
||||
// },
|
||||
|
||||
async getCodeSymbols(projectId, ref, filepath) {
|
||||
return await apiService.get(
|
||||
`/api/v3/projects/42422/repository/file_symbols/${encodeURIComponent(
|
||||
ref
|
||||
)}/`,
|
||||
{
|
||||
filepath,
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async getCodeBlame(projectId, sha, filePath) {
|
||||
const res = await axios.get(`/v1/${owner}/${projectsId}/blame.json`);
|
||||
console.log(res);
|
||||
// return res.
|
||||
|
||||
return (await apiService.get(
|
||||
`/api/v3/projects/42422/repository/blame`,
|
||||
{
|
||||
sha,
|
||||
filePath,
|
||||
}
|
||||
))
|
||||
},
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
import { mockService } from './mock';
|
||||
import { underscoreToCamelcase } from '../utils/camelcase-convert';
|
||||
|
||||
|
||||
|
||||
|
||||
export async function request(
|
||||
url,
|
||||
options,
|
||||
extraOptions
|
||||
) {
|
||||
|
||||
// @ts-ignore
|
||||
const data = underscoreToCamelcase(mockService[url]);
|
||||
console.log('request ==== ', url);
|
||||
console.log(data)
|
||||
if (data) {
|
||||
return data;
|
||||
} else {
|
||||
return '文件展示内容只mock了 aaa/package.json';
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,32 @@
|
|||
import { message as messageTip } from 'antd';
|
||||
|
||||
|
||||
export function createApiError(message = '请求出错', response) {
|
||||
const err = new Error(message);
|
||||
err.isApiError = true;
|
||||
err.response = response;
|
||||
return err;
|
||||
}
|
||||
|
||||
export function isApiError(e) {
|
||||
return e&&e.isApiError;
|
||||
}
|
||||
/**
|
||||
* 目前存在一些请求成功也报错的情况
|
||||
* 返回值可能情况
|
||||
* 1. success === true 此时只提示有 errorMessage 的情况
|
||||
* 2. success === false 此时提示包括 message 的信息
|
||||
* todo: 目前由于intl使用的是react component注入的方式,这里暂时无法做到国际化,后续等移除 konjac 之后采用全局api方式即可支持国际化
|
||||
* @param responseData 返回值,各种可能
|
||||
*/
|
||||
export function successErrorMessage(responseData) {
|
||||
if (responseData) {
|
||||
const errorMessage = responseData.error || responseData.errorMessage;
|
||||
const message = errorMessage || responseData.message;
|
||||
if (responseData.success === false && message) {
|
||||
messageTip.error(message);
|
||||
} else if (errorMessage) {
|
||||
messageTip.error(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
export function calcChangeLineNum(diffs) {
|
||||
let addLineNum = 0;
|
||||
let delLineNum = 0;
|
||||
for (const diff of diffs) {
|
||||
if (diff.tooLarge) {
|
||||
continue;
|
||||
}
|
||||
const addition = diff.diff.match(/^\+/gm)&&diff.diff.match(/^\+/gm).length || 0;
|
||||
const deletion = diff.diff.match(/^-/gm)&&diff.diff.match(/^-/gm).length || 0;
|
||||
diff.addLineNum = addition;
|
||||
diff.delLineNum = deletion;
|
||||
addLineNum += addition;
|
||||
delLineNum += deletion;
|
||||
}
|
||||
return {
|
||||
addLineNum,
|
||||
delLineNum,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
export function underscoreToCamelcase(obj){
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => underscoreToCamelcase(item))
|
||||
}
|
||||
if (typeof obj !== 'object' || obj === null || obj === undefined) {
|
||||
return obj;
|
||||
}
|
||||
const ret = {};
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty && !obj.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
const newKey = key.replace(/_([a-z])/g, (m) => m[1].toUpperCase());
|
||||
ret[newKey] = underscoreToCamelcase(obj[key]);
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
export function camelcaseToUnderscore(obj) {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => camelcaseToUnderscore(item))
|
||||
}
|
||||
if (typeof obj !== 'object' || obj === null || obj === undefined) {
|
||||
return obj;
|
||||
}
|
||||
const ret= {};
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty && !obj.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
const newKey = key.replace(/([a-z][A-Z])/g, (m) => m[0] + '_' + m[1].toLowerCase());
|
||||
ret[newKey] = camelcaseToUnderscore(obj[key]);
|
||||
}
|
||||
return ret
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export const getLocale = () =>
|
||||
document.cookie.indexOf('LOCALE=en_US') > -1 ? 'en-US' : 'zh-CN';
|
|
@ -0,0 +1,8 @@
|
|||
export function mapGetSet(map, key, fallback) {
|
||||
let ret = map.get(key);
|
||||
if (!ret) {
|
||||
ret = fallback();
|
||||
map.set(key, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,743 @@
|
|||
import React, {
|
||||
useState,
|
||||
useMemo,
|
||||
useRef,
|
||||
useContext,
|
||||
useEffect,
|
||||
useCallback,
|
||||
createContext,
|
||||
createElement,
|
||||
FC,
|
||||
} from 'react';
|
||||
import { getLocale } from './mock/utils/locale';
|
||||
// @ts-ignore
|
||||
import sha1 from 'sha1';
|
||||
// @ts-ignore
|
||||
import memoize from 'lodash/memoize';
|
||||
import jCookie from 'js-cookie';
|
||||
import { useRequest } from './hooks';
|
||||
import { group, repo, prIid, project } from './meta';
|
||||
import { prService } from './mock/pr.service.js';
|
||||
import { projectService } from './mock/project.service.js';
|
||||
import { mapGetSet } from './mock/utils/map-get-set.js';
|
||||
import { isApiError } from './mock/utils/api-error.js';
|
||||
import { mockService } from './mock/request/mock';
|
||||
import './style.less';
|
||||
|
||||
|
||||
|
||||
const NoteType = {
|
||||
comment: 'Comment',
|
||||
problem: 'Problem',
|
||||
}
|
||||
|
||||
const NoteState = {
|
||||
opened: 'opened',
|
||||
resolved: 'resolved',
|
||||
invalid: 'invalid',
|
||||
}
|
||||
|
||||
const CharsetName = {
|
||||
gbk: 'GBK',
|
||||
utf8: 'UTF-8',
|
||||
};
|
||||
|
||||
|
||||
function createContainer(useHook, checkNull = false) {
|
||||
let Context = createContext(null);
|
||||
const Provider = ({ children }) => {
|
||||
const value = useHook();
|
||||
if (checkNull && !value) return null;
|
||||
return <Context.Provider value={value}>{children}</Context.Provider>;
|
||||
};
|
||||
const useContainer = () => React.useContext(Context);
|
||||
return [Provider, useContainer];
|
||||
}
|
||||
|
||||
let ContextProps = createContext(null);
|
||||
const useGlobal = () => React.useContext(ContextProps);
|
||||
|
||||
// const [GlobalProvider, useGlobal] = createContainer(() => {
|
||||
// const [current_user, setCurrent_user] = useState({});
|
||||
// const [projectDetail, setProjectDetail] = useState({});
|
||||
// // let project=mockService['/webapi/projects/Gitlink/forgeplus/'];
|
||||
// let user=mockService['/api/v3/user'];
|
||||
|
||||
// return {
|
||||
// user: {...current_user,...user, avatar_url: current_user.image_url, name: current_user.username },
|
||||
// project:{...projectDetail,...project,id:42422},
|
||||
// setProjectDetail,
|
||||
// setCurrent_user,
|
||||
// };
|
||||
|
||||
// // return {
|
||||
// // user: { ...current_user, avatar_url: current_user.image_url, name: current_user.username },
|
||||
// // project:{...projectDetail,id:42422},
|
||||
// // setProjectDetail,
|
||||
// // setCurrent_user,
|
||||
// // };
|
||||
|
||||
// // let data = useRequest(async () => {
|
||||
// // // const user = await projectService.getUser();
|
||||
// // // const project = await projectService.getProject(group, repo);
|
||||
// // // console.log(JSON.stringify(project)==JSON.stringify(project1))
|
||||
// // console.log('user');
|
||||
// // console.log(project);
|
||||
// // return {
|
||||
// // user: { ...user, ...current_user, avatar_url: current_user.image_url, name: current_user.username },
|
||||
// // project,
|
||||
// // setProjectDetail,
|
||||
// // setCurrent_user,
|
||||
// // };
|
||||
// // });
|
||||
// // return data;
|
||||
// },);
|
||||
|
||||
const [PrProvider, usePr] = createContainer(() => {
|
||||
const { params } = useGlobal();
|
||||
const { owner, projectsId ,mergeId} = params;
|
||||
const data = useRequest(async () => {
|
||||
const pr = await prService.getPRByIid(projectsId, owner, mergeId);
|
||||
console.log('getPRByIidpr');
|
||||
console.log(pr);
|
||||
return { pr }
|
||||
})
|
||||
return data;
|
||||
}, true);
|
||||
|
||||
// const [GlobalProvider, useGlobal] = createContainer((props) => {
|
||||
// // let gitProps = props.children.props.children.props.children.props.children.props;
|
||||
// // let match = gitProps.match;
|
||||
// // const { owner, projectsId } = match.params;
|
||||
// // const {current_user,projectDetail}=gitProps;
|
||||
// const [current_user,setCurrent_user]=useState();
|
||||
// const [projectDetail,setProjectDetail]=useState({});
|
||||
// console.log('projectDetail');
|
||||
// console.log(projectDetail);
|
||||
// let data = useRequest(async () => {
|
||||
// const user = await projectService.getUser();
|
||||
// const project = await projectService.getProject(group, repo);
|
||||
// const pr = await prService.getPRByIid(project.id, prIid);
|
||||
// console.log('pr:');
|
||||
// console.log(user);
|
||||
// console.log('project');
|
||||
// console.log(project);
|
||||
// console.log(pr);
|
||||
// // console.log(gitProps);
|
||||
// console.log('projectDetail');
|
||||
// console.log(projectDetail);
|
||||
// return {
|
||||
// user:{...user,...current_user,avatar_url:current_user.image_url,name:current_user.username},
|
||||
// project,
|
||||
// pr,
|
||||
// setProjectDetail,
|
||||
// setCurrent_user,
|
||||
// // updateProject,
|
||||
// };
|
||||
// });
|
||||
|
||||
// return data;
|
||||
// }, true);
|
||||
|
||||
const [SettingProvider, useSetting] = createContainer(() => {
|
||||
const { project } = useGlobal();
|
||||
const [gbk, setGBK] = useState(() => project.encoding === 'GBK');
|
||||
const charsetName = gbk ? CharsetName.gbk : CharsetName.utf8;
|
||||
const [locale, setLocal] = useState(() => getLocale());
|
||||
const setLocale = React.useCallback(() => {
|
||||
jCookie.set('LOCALE', locale === 'en-US' ? 'zh_CN' : 'en_US');
|
||||
window.location.reload();
|
||||
}, [locale]);
|
||||
|
||||
|
||||
return {
|
||||
gbk,
|
||||
setGBK,
|
||||
charsetName,
|
||||
locale,
|
||||
setLocale,
|
||||
};
|
||||
});
|
||||
|
||||
const useCommentPack = (projectId, prId) => {
|
||||
const lineToNoteIdSet = useMemo(() => new Map(), []);
|
||||
const noteIdToNote = useMemo(() => new Map(), []);
|
||||
const noteIdToReviewId = useMemo(() => new Map(), []);
|
||||
const reviewIdToReview = useMemo(() => new Map(), []);
|
||||
const noteIdToReplyIdSet = useMemo(() => new Map(), []);
|
||||
const pendingNoteIdSet = useMemo(() => new Set(), []);
|
||||
const recordNoteIdSet = useMemo(() => new Set(), []);
|
||||
const topLevelCommentNoteIdSet = useMemo(() => new Set(), []);
|
||||
const commentNoteIdSet = useMemo(() => new Set(), []);
|
||||
const openedProblemNoteIdSet = useMemo(() => new Set(), []);
|
||||
const [updateFlag, setUpdateFlag] = useState({});
|
||||
|
||||
const fetchingRef = useRef(false);
|
||||
const lastFetchAtRef = useRef();
|
||||
|
||||
const pendingReviewRef = useRef();
|
||||
const [hasPendingReview, setHasPendingReview] = useState(false); // TODO: 去掉这个state
|
||||
|
||||
function processNote(note, review = null) {
|
||||
noteIdToNote.set(note.id, note);
|
||||
noteIdToReviewId.set(note.id, review && review.id);
|
||||
if (note.type === NoteType.problem && note.state === NoteState.opened) {
|
||||
openedProblemNoteIdSet.add(note.id);
|
||||
} else {
|
||||
openedProblemNoteIdSet.delete(note.id);
|
||||
}
|
||||
if (!note.discussionId) {
|
||||
if (!note.lineCode && note.system) {
|
||||
recordNoteIdSet.add(note.id);
|
||||
} else {
|
||||
topLevelCommentNoteIdSet.add(note.id);
|
||||
}
|
||||
}
|
||||
if (note.discussionId || note.lineCode) {
|
||||
commentNoteIdSet.add(note.id);
|
||||
}
|
||||
if (note.lineCode) {
|
||||
const noteIdSet = mapGetSet(
|
||||
lineToNoteIdSet,
|
||||
note.lineCode,
|
||||
() => new Set()
|
||||
);
|
||||
noteIdSet.add(note.id);
|
||||
}
|
||||
if (note.discussionId) {
|
||||
const replyIdSet = mapGetSet(
|
||||
noteIdToReplyIdSet,
|
||||
note.discussionId,
|
||||
() => new Set()
|
||||
);
|
||||
replyIdSet.add(note.id);
|
||||
}
|
||||
if (review && review.pending) {
|
||||
pendingNoteIdSet.add(note.id);
|
||||
} else {
|
||||
pendingNoteIdSet.delete(note.id);
|
||||
}
|
||||
}
|
||||
|
||||
async function doRefresh(force = false) {
|
||||
if (fetchingRef.current) {
|
||||
return;
|
||||
}
|
||||
fetchingRef.current = true;
|
||||
const pack = await prService.getCommentPack(
|
||||
projectId,
|
||||
prId,
|
||||
force ? undefined : lastFetchAtRef.current
|
||||
);
|
||||
console.log('pack');
|
||||
console.log(pack);
|
||||
lastFetchAtRef.current = pack.currentFetchedAt;
|
||||
let updated = false;
|
||||
if (force) {
|
||||
lineToNoteIdSet.clear();
|
||||
noteIdToNote.clear();
|
||||
noteIdToReviewId.clear();
|
||||
reviewIdToReview.clear();
|
||||
noteIdToReplyIdSet.clear();
|
||||
pendingNoteIdSet.clear();
|
||||
recordNoteIdSet.clear();
|
||||
topLevelCommentNoteIdSet.clear();
|
||||
commentNoteIdSet.clear();
|
||||
openedProblemNoteIdSet.clear();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
for (const note of pack.notes) {
|
||||
processNote(note);
|
||||
updated = true;
|
||||
}
|
||||
for (const review of pack.committedReviews) {
|
||||
reviewIdToReview.set(review.id, review);
|
||||
for (const note of review.reviewNotes) {
|
||||
processNote(note, review);
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
if (pack.pendingReview) {
|
||||
reviewIdToReview.set(pack.pendingReview.id, pack.pendingReview);
|
||||
pendingReviewRef.current = pack.pendingReview;
|
||||
setHasPendingReview(true);
|
||||
for (const note of pack.pendingReview.reviewNotes) {
|
||||
processNote(note, pack.pendingReview);
|
||||
updated = true;
|
||||
}
|
||||
} else {
|
||||
setHasPendingReview(false);
|
||||
}
|
||||
if (updated) {
|
||||
setUpdateFlag({});
|
||||
}
|
||||
fetchingRef.current = false;
|
||||
}
|
||||
|
||||
function manualUpdateReview(review) {
|
||||
reviewIdToReview.set(review.id, review);
|
||||
if (review.pending) {
|
||||
pendingReviewRef.current = review;
|
||||
setHasPendingReview(!!review);
|
||||
}
|
||||
setUpdateFlag({});
|
||||
}
|
||||
|
||||
function manualAddNote(note, review = null) {
|
||||
processNote(note, review);
|
||||
setUpdateFlag({});
|
||||
}
|
||||
|
||||
function manualUpdateNote(note) {
|
||||
const reviewId = noteIdToReviewId.get(note.id);
|
||||
const review = reviewId ? reviewIdToReview.get(reviewId) : null;
|
||||
processNote(note, review);
|
||||
setUpdateFlag({});
|
||||
}
|
||||
|
||||
function getRelatedFilePathByNoteId(noteId) {
|
||||
const note = noteIdToNote.get(noteId);
|
||||
if (!note) return;
|
||||
let path = note.path;
|
||||
if (note.discussionId) {
|
||||
const parent = noteIdToNote.get(note.discussionId);
|
||||
path = parent && parent.path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
async function editNote(
|
||||
noteId, data
|
||||
) {
|
||||
const newNote = await prService.editPRComment(
|
||||
projectId,
|
||||
prId,
|
||||
noteId,
|
||||
data
|
||||
);
|
||||
manualUpdateNote(newNote);
|
||||
newNote.discussions && newNote.discussions.forEach((note) => manualUpdateNote(note));
|
||||
}
|
||||
|
||||
const pathShaToNoteCount = (function () {
|
||||
const ret = new Map();
|
||||
|
||||
function handleNote(note) {
|
||||
if (!note || !note.lineCode) return;
|
||||
const key = note.lineCode.split('_')[0];
|
||||
ret.set(key, (ret.get(key) || 0) + 1);
|
||||
}
|
||||
|
||||
topLevelCommentNoteIdSet.forEach((noteId) => {
|
||||
const note = noteIdToNote.get(noteId);
|
||||
handleNote(note);
|
||||
});
|
||||
return ret;
|
||||
})();
|
||||
|
||||
return {
|
||||
lineToNoteIdSet,
|
||||
noteIdToNote,
|
||||
noteIdToReviewId,
|
||||
reviewIdToReview,
|
||||
noteIdToReplyIdSet,
|
||||
pendingNoteIdSet,
|
||||
recordNoteIdSet,
|
||||
topLevelCommentNoteIdSet,
|
||||
openedProblemNoteIdSet,
|
||||
hasPendingReview,
|
||||
hasOpenedProblem: openedProblemNoteIdSet.size > 0,
|
||||
doRefresh,
|
||||
manualUpdateReview,
|
||||
manualAddNote,
|
||||
manualUpdateNote,
|
||||
pendingReviewRef,
|
||||
getRelatedFilePathByNoteId,
|
||||
editNote,
|
||||
updateFlag,
|
||||
pathShaToNoteCount,
|
||||
setUpdateFlag,
|
||||
};
|
||||
};
|
||||
|
||||
const [NoteProvider, useNote] = createContainer(() => {
|
||||
const {
|
||||
project: { id: projectId },
|
||||
// pr: { id: prId },
|
||||
} = useGlobal();
|
||||
const { pr: { id: prId } } = usePr();
|
||||
const commentPack = useCommentPack(projectId, prId);
|
||||
useEffect(() => {
|
||||
commentPack.doRefresh();
|
||||
}, []);
|
||||
|
||||
async function addComment(data) {
|
||||
if (!commentPack.hasPendingReview) {
|
||||
const review = await prService.createReview(projectId, prId);
|
||||
commentPack.manualUpdateReview(review);
|
||||
}
|
||||
const note = await prService.addComment(projectId, prId, data);
|
||||
commentPack.manualAddNote(note, commentPack.pendingReviewRef.current);
|
||||
}
|
||||
|
||||
async function commitReview(body) {
|
||||
if (!commentPack.hasPendingReview) {
|
||||
await prService.createReview(projectId, prId);
|
||||
}
|
||||
await prService.commitReview(projectId, prId, body);
|
||||
await commentPack.doRefresh(true);
|
||||
}
|
||||
|
||||
const activateRef = useRef();
|
||||
|
||||
return {
|
||||
commentPack,
|
||||
...commentPack,
|
||||
addComment,
|
||||
commitReview,
|
||||
prId,
|
||||
activateRef,
|
||||
};
|
||||
});
|
||||
|
||||
const [ReadMarkProvider, useReadMark] = createContainer(() => {
|
||||
const {
|
||||
project: { id: projectId },
|
||||
// pr: { id: prId },
|
||||
} = useGlobal();
|
||||
const { pr: { id: prId } } = usePr();
|
||||
|
||||
const [flag, updateFlag] = useState({});
|
||||
|
||||
const readMarks = useRequest(
|
||||
() => prService.getFileReadMarks(projectId, prId),
|
||||
{
|
||||
deps: [projectId, projectId, flag],
|
||||
}
|
||||
);
|
||||
|
||||
const readMarkMap = useMemo(() => {
|
||||
const map = new Map();
|
||||
if (!readMarks) return map;
|
||||
for (const mark of readMarks) {
|
||||
map.set(mark.filePathSha, mark);
|
||||
}
|
||||
return map;
|
||||
}, [readMarks]);
|
||||
|
||||
const memoizedSha1 = useMemo(() => memoize(sha1), []);
|
||||
|
||||
async function markFileAsRead(filePath) {
|
||||
const data = await prService.markFileAsRead(
|
||||
projectId,
|
||||
prId,
|
||||
memoizedSha1(filePath)
|
||||
);
|
||||
updateFlag({});
|
||||
return data;
|
||||
}
|
||||
|
||||
async function markFileAsUnread(filePath) {
|
||||
const data = await prService.markFileAsUnread(
|
||||
projectId,
|
||||
prId,
|
||||
memoizedSha1(filePath)
|
||||
);
|
||||
updateFlag({});
|
||||
return data;
|
||||
}
|
||||
|
||||
const getFileReadMark = useCallback(
|
||||
(filePath) => {
|
||||
const filePathSha = memoizedSha1(filePath);
|
||||
return readMarkMap.get(filePathSha);
|
||||
},
|
||||
[readMarkMap]
|
||||
);
|
||||
|
||||
const getFileReadStatus = useCallback(
|
||||
(filePath) => {
|
||||
const readMark = getFileReadMark(filePath);
|
||||
return readMark && readMark.markAsRead && !readMark.updatedAfterRead;
|
||||
},
|
||||
[getFileReadMark]
|
||||
);
|
||||
|
||||
return {
|
||||
readMarks,
|
||||
readMarkMap,
|
||||
getFileReadMark,
|
||||
getFileReadStatus,
|
||||
markFileAsRead,
|
||||
markFileAsUnread,
|
||||
};
|
||||
});
|
||||
|
||||
const [AcrProvider, useAcr] = createContainer(() => {
|
||||
const { project,params } = useGlobal();
|
||||
const { pr } = usePr();
|
||||
const { charsetName } = useSetting();
|
||||
const { commentPack } = useNote();
|
||||
|
||||
const { owner, projectsId ,mergeId} = params;
|
||||
const [IDEMode, setIDEMode] = useState(true);
|
||||
const toggleViewerType = useCallback(() => {
|
||||
setIDEMode((v) => !v);
|
||||
}, [setIDEMode]);
|
||||
|
||||
const [search, setSearch] = useState(location.search);
|
||||
const query = useMemo(() => {
|
||||
const searchParams = new URLSearchParams(search);
|
||||
const ret = {};
|
||||
if (searchParams.has('from')) {
|
||||
ret.from = parseInt(searchParams.get('from'));
|
||||
}
|
||||
if (searchParams.has('to')) {
|
||||
ret.to = parseInt(searchParams.get('to'));
|
||||
}
|
||||
return ret;
|
||||
}, [search]);
|
||||
|
||||
function updateQuery(data) {
|
||||
const searchParam = new URLSearchParams();
|
||||
if (data.from) {
|
||||
searchParam.set('from', String(data.from));
|
||||
}
|
||||
if (data.to) {
|
||||
searchParam.set('to', String(data.to));
|
||||
}
|
||||
const search = searchParam.toString();
|
||||
history.replaceState(
|
||||
null,
|
||||
'',
|
||||
`${location.pathname}${search ? '?' : ''}${search}`
|
||||
);
|
||||
setSearch(search);
|
||||
}
|
||||
|
||||
const versions = useRequest(
|
||||
async () => await prService.getDiffVersions(projectsId , owner, mergeId),
|
||||
{ initial: [] }
|
||||
);
|
||||
|
||||
|
||||
const versionMap = useMemo(() => {
|
||||
const map = new Map();
|
||||
for (const version of versions) {
|
||||
map.set(version.id, version);
|
||||
}
|
||||
return map;
|
||||
}, [versions]);
|
||||
const latestVersionId = versions[0] && versions[0].id || pr.diff.id;
|
||||
|
||||
const from = query.from;
|
||||
const to = query.to || latestVersionId;
|
||||
const fromVersion = from ? versionMap.get(from) : null;
|
||||
const toVersion = versionMap.get(to);
|
||||
|
||||
console.log('diffpack', from, to, fromVersion, toVersion);
|
||||
const ignoreWhiteSpace = false;
|
||||
// @ts-ignore
|
||||
const diffsPack = useRequest(
|
||||
async () => {
|
||||
if (!toVersion) return null;
|
||||
const isPartial = !!fromVersion || toVersion.id !== latestVersionId;
|
||||
const options = {
|
||||
ignoreWhiteSpaceChange: ignoreWhiteSpace,
|
||||
charsetName,
|
||||
};
|
||||
let res;
|
||||
if (fromVersion) {
|
||||
res = await prService.getDiffs(
|
||||
projectsId,owner,
|
||||
fromVersion.headCommitSha,
|
||||
toVersion.headCommitSha,
|
||||
options
|
||||
);
|
||||
console.log('getDiffs1');
|
||||
console.log(res);
|
||||
} else {
|
||||
if (ignoreWhiteSpace) {
|
||||
res = await prService.getDiffs(
|
||||
projectsId,owner,
|
||||
toVersion.baseCommitSha,
|
||||
toVersion.headCommitSha,
|
||||
options
|
||||
);
|
||||
console.log('getDiffs2');
|
||||
console.log(res);
|
||||
} else {
|
||||
const diffs = await prService.getDiffOverviews(
|
||||
projectsId,owner,
|
||||
mergeId,
|
||||
toVersion.id
|
||||
);
|
||||
res = {
|
||||
diffs,
|
||||
overflow: false,
|
||||
addLineNum: toVersion.addLineNum,
|
||||
delLineNum: toVersion.delLineNum,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
...res,
|
||||
fromVersion,
|
||||
toVersion,
|
||||
isPartial,
|
||||
};
|
||||
},
|
||||
{
|
||||
deps: [fromVersion, toVersion, charsetName],
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
commentPack.doRefresh();
|
||||
}, []);
|
||||
|
||||
async function addComment(data) {
|
||||
console.log('>>>addComment', data);
|
||||
}
|
||||
|
||||
async function commitReview(body) {
|
||||
console.log('>>>commitReview', body);
|
||||
}
|
||||
|
||||
function getDiffById(diffId) {
|
||||
return prService.getDiffById(projectsId,owner,mergeId,toVersion.id,
|
||||
{
|
||||
filepath:diffId,
|
||||
});
|
||||
// return prService.getDiffById(project.id, pr.id, toVersion.id, diffId, {
|
||||
// charsetName,
|
||||
// });
|
||||
}
|
||||
|
||||
async function getFileContent(path, sha, maxSize) {
|
||||
console.log('getFileContent--path');
|
||||
console.log(path);
|
||||
console.log('getFileContent--sha');
|
||||
console.log(sha);
|
||||
try {
|
||||
return await projectService.getFileBlob(projectsId, owner, {
|
||||
filepath:path,
|
||||
ref:sha,
|
||||
});
|
||||
// return await projectService.getFileBlob(project.id, sha, path, {
|
||||
// maxSize: maxSize || 250000,
|
||||
// charsetName,
|
||||
// });
|
||||
} catch (e) {
|
||||
if (isApiError(e) && e.response.status === 413) {
|
||||
return null;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
const annotationPacks = [];
|
||||
let countMap = new Map();
|
||||
for (const checkSuite of pr.checkSuites) {
|
||||
if (checkSuite.checkRuns) {
|
||||
for (const checkRun of checkSuite.checkRuns) {
|
||||
if (checkRun.annotations) {
|
||||
for (const annotation of checkRun.annotations) {
|
||||
const pack = {
|
||||
annotation,
|
||||
checkSuite,
|
||||
};
|
||||
annotationPacks.push(pack);
|
||||
countMap.set(
|
||||
annotation.path,
|
||||
(countMap.get(annotation.path) || 0) + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
diffsPack,
|
||||
versions,
|
||||
fromVersion,
|
||||
toVersion,
|
||||
getDiffById,
|
||||
getFileContent,
|
||||
IDEMode,
|
||||
toggleViewerType,
|
||||
updateQuery,
|
||||
annotationPacks,
|
||||
};
|
||||
});
|
||||
|
||||
export const Provider = (props) => {
|
||||
const match = props.match;
|
||||
let project = {
|
||||
// "encoding": "UTF-8",
|
||||
"id": 42422,
|
||||
"importStatus": "none",
|
||||
"namespace": {
|
||||
"avatar": {
|
||||
"url": null
|
||||
},
|
||||
"createdAt": "2020-07-02T10:34:49+0800",
|
||||
"description": "",
|
||||
"id": 23159,
|
||||
"name": "ide-s",
|
||||
"owner": null,
|
||||
"ownerId": null,
|
||||
"path": "ide-s",
|
||||
"permission": null,
|
||||
"public": false,
|
||||
"state": null,
|
||||
"tenant": {
|
||||
"createdAt": null,
|
||||
"id": 0,
|
||||
"name": "git",
|
||||
"path": "git",
|
||||
"updatedAt": null,
|
||||
"whileListIp": null
|
||||
},
|
||||
"type": "Group",
|
||||
"updatedAt": "2020-07-02T10:34:49+0800",
|
||||
"webUrl": "http://gitlab-test.alipay.net/groups/ide-s"
|
||||
},
|
||||
"path": "TypeScript-Node-Starter",
|
||||
"pathWithNamespace": "ide-s/TypeScript-Node-Starter"
|
||||
};
|
||||
let user = mockService['/api/v3/user'];
|
||||
if (props.projectDetail) {
|
||||
let details = props.projectDetail;
|
||||
project = {
|
||||
...project,
|
||||
pathWithNamespace: details.full_name,
|
||||
}
|
||||
}
|
||||
if (props.current_user) {
|
||||
let current_user = props.current_user;
|
||||
user = { ...current_user, ...user, avatar_url: current_user.image_url, name: current_user.username, };
|
||||
}
|
||||
return (
|
||||
<ContextProps.Provider value={{
|
||||
project, user,params:match.params
|
||||
}}>
|
||||
{/* <GlobalProvider> */}
|
||||
<PrProvider>
|
||||
<SettingProvider>
|
||||
<NoteProvider>
|
||||
<ReadMarkProvider>
|
||||
<AcrProvider>{props.children}</AcrProvider>
|
||||
</ReadMarkProvider>
|
||||
</NoteProvider>
|
||||
</SettingProvider>
|
||||
</PrProvider>
|
||||
{/* </GlobalProvider> */}
|
||||
</ContextProps.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export { useGlobal, usePr, useNote, useReadMark, useAcr, useSetting };
|
|
@ -0,0 +1,62 @@
|
|||
export const ExtensionCommand ={
|
||||
acrToggleBlame : 'code.blame.acrToggleBlame',
|
||||
linkToCommit : 'code.blame.linktocommit',
|
||||
onActive : 'code.blame.extension.active',
|
||||
setProjectData : 'code.blame.setProjectData',
|
||||
getBlameData : 'code.blame.getBlameData',
|
||||
}
|
||||
|
||||
export default class IDEPlugin {
|
||||
/**
|
||||
* 插件 ID,用于唯一标识插件
|
||||
*/
|
||||
PLUGIN_ID = 'ACR_BLAME_PLUGIN';
|
||||
|
||||
constructor(
|
||||
onActive,
|
||||
linkToCommit,
|
||||
getBlame
|
||||
) {
|
||||
this.onActivate = onActive;
|
||||
// 跳转
|
||||
this.linkToCommit = linkToCommit;
|
||||
// 传递blame数据
|
||||
this.getBlame = getBlame;
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活插件
|
||||
*/
|
||||
activate = (ctx) => {
|
||||
const { commands, context } = ctx;
|
||||
this.commands = commands;
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand(ExtensionCommand.onActive, () => {
|
||||
this.onActivate();
|
||||
}),
|
||||
commands.registerCommand(ExtensionCommand.linkToCommit, (params) => {
|
||||
const { commitId } = params;
|
||||
this.linkToCommit(commitId);
|
||||
}),
|
||||
commands.registerCommand(
|
||||
ExtensionCommand.getBlameData,
|
||||
async (sendData) => {
|
||||
const { projectId, prevSha, nextSha, filePath } = sendData;
|
||||
// kaitian内uri 和 antcode内不同 多了一个 /
|
||||
const path = filePath.startsWith('/') ? filePath.slice(1) : filePath;
|
||||
const response = await this.getBlame(projectId, nextSha, path).then(
|
||||
(res) => {
|
||||
return res;
|
||||
}
|
||||
);
|
||||
return response;
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 注销插件,可在此时机清理副作用
|
||||
*/
|
||||
deactivate() {}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
@c45: rgba(0, 0, 0, 0.45);
|
||||
@c85: rgba(0, 0, 0, 0.85);
|
||||
html,
|
||||
body,
|
||||
#main {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#main {
|
||||
padding: 8px 24px 24px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
background-color: #f7f8fa;
|
||||
}
|
||||
|
||||
.pr-head {
|
||||
padding: 24px;
|
||||
background-color: #fff;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.controller {
|
||||
margin-bottom: 24px;
|
||||
> * {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.monaco-editor-hover {
|
||||
border: none !important;
|
||||
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
||||
transform: translate(-20px, 0px);
|
||||
}
|
||||
|
||||
.monaco-editor-hover-content .markdown-hover .hover-contents {
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
|
||||
h1 {
|
||||
color: @c85;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: @c45;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import AddMember from '../Component/AddMember';
|
|||
import AddGroup from '../Component/AddGroup';
|
||||
import Member from './CollaboratorMember';
|
||||
import Group from './CollaboratorGroup';
|
||||
import MemberByLink from './CollaboratorMemberByLink';
|
||||
|
||||
function Collaborator(props){
|
||||
const [ nav , setNav] = useState("1");
|
||||
|
@ -23,11 +24,10 @@ function Collaborator(props){
|
|||
setNewGroupId(id);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<WhiteBack>
|
||||
<div className="flex-a-center baseForm bbr">
|
||||
{
|
||||
{/* {
|
||||
author && author.type === "Organization" ?
|
||||
<span>
|
||||
<span style={{cursor:"pointer"}} className={nav === "1" ? "font-18 text-black color-blue":"font-18 text-black"} onClick={()=>{setNav("1");setNewId(undefined)}}>协作者管理</span>
|
||||
|
@ -35,22 +35,26 @@ function Collaborator(props){
|
|||
</span>
|
||||
:
|
||||
<span className="font-18 text-black">协作者管理</span>
|
||||
}
|
||||
} */}
|
||||
<span>
|
||||
<span style={{cursor:"pointer"}} className={nav === "1" ? "font-15 text-black color-blue":"font-15 text-black"} onClick={()=>{setNav("1");setNewId(undefined)}}>协作者管理</span>
|
||||
<span style={{cursor:"pointer"}} className={nav === "3" ? "font-15 text-black color-blue ml30":"font-15 text-black ml30"} onClick={()=>{setNav("3");}}>邀请成员</span>
|
||||
{author && author.type === "Organization" && <span style={{cursor:"pointer"}} className={nav === "2" ? "font-15 text-black ml30 color-blue":"font-15 text-black ml30"} onClick={()=>{setNav("2");setNewId(undefined);setNewGroupId(undefined)}}>团队管理</span>}
|
||||
</span>
|
||||
{
|
||||
nav === "1" &&
|
||||
<AddMember getID={getID} login showNotification={props.showNotification}/>
|
||||
}
|
||||
{
|
||||
(nav !== "1" && addOperation) &&
|
||||
(nav === "2" && addOperation) &&
|
||||
<AddGroup getGroupID={getGroupID} organizeId={owner}/>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
{
|
||||
nav === "1" ?
|
||||
<Member newId={newId} flag={newIdFlag} projectsId={projectsId} owner={owner} project_id={props.project_id} author={props.projectDetail && props.projectDetail.author} showNotification={props.showNotification}/>
|
||||
:
|
||||
<Group setAddOperation={setAddOperation} owner={owner} projectsId={projectsId} newGroupId={newGroupId}/>
|
||||
nav === "1" ? <Member newId={newId} flag={newIdFlag} projectsId={projectsId} owner={owner} project_id={props.project_id} author={props.projectDetail && props.projectDetail.author} showNotification={props.showNotification}/>
|
||||
: nav === "2" ? <Group setAddOperation={setAddOperation} owner={owner} projectsId={projectsId} newGroupId={newGroupId}/>
|
||||
: <MemberByLink newId={newId} flag={newIdFlag} projectsId={projectsId} owner={owner} project_id={props.project_id} author={props.projectDetail && props.projectDetail.author} showNotification={props.showNotification}/>
|
||||
}
|
||||
</div>
|
||||
</WhiteBack>
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import React, { useEffect, useState, useImperativeHandle, forwardRef } from 'react';
|
||||
import { Select, Checkbox, Button, Input, Spin, Table, Tooltip, Pagination, Popconfirm } from "antd";
|
||||
import axios from 'axios';
|
||||
import { Base64 } from 'js-base64';
|
||||
import NoneData from "../Nodata";
|
||||
import { Link } from "react-router-dom";
|
||||
import { getImageUrl } from "educoder";
|
||||
|
||||
const { Search } = Input;
|
||||
const LIMIT = 15;
|
||||
const optionList = [
|
||||
{ value: "manager", name: "管理员 - 拥有仓库设置功能、代码库读、写操作权限" },
|
||||
{ value: "developer", name: "开发人员 - 拥有代码库读、写操作权限" },
|
||||
{ value: "reporter", name: "报告者 - 拥有代码库读操作权限" }
|
||||
];
|
||||
function CollaboratorMemberByLink({ projectsId, owner, project_id, author, showNotification, newId, flag }) {
|
||||
|
||||
const [role, setRole] = useState('developer');
|
||||
const [is_apply, setIs_apply] = useState(true);
|
||||
const [inviteUrl, setinviteUrl] = useState('');
|
||||
const [copy, setCopy] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const url = `/${owner}/${projectsId}/project_invite_links/current_link.json`;
|
||||
axios.get(url, {
|
||||
params: {
|
||||
role,
|
||||
is_apply
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
let params = {
|
||||
projectName: res.data.project.name,
|
||||
projectId: res.data.project.identifier,
|
||||
// role: res.data.role,
|
||||
ownerLogin: res.data.project.owner.login,
|
||||
ownerName: res.data.project.owner.name,
|
||||
// userName: res.data.user.name,
|
||||
// userLogin: res.data.user.login,
|
||||
sign:res.data.sign,
|
||||
};
|
||||
let urlParams = JSON.stringify(params);
|
||||
let content = Base64.encode(urlParams);
|
||||
|
||||
setinviteUrl(`${window.location.origin}/${owner}/${projectsId}/invite?invite=${content}`);
|
||||
setCopy(false);
|
||||
}
|
||||
}).catch(error => { })
|
||||
}, [role, is_apply]);
|
||||
|
||||
function inviteClick() {
|
||||
const copyEle = document.querySelector('#inviteUrl'); // 获取要复制的节点
|
||||
if (!copyEle) {
|
||||
console.error("您的CopyTool未设置正确的inputId");
|
||||
return;
|
||||
}
|
||||
copyEle.select(); // 执行选中元素
|
||||
if (document.execCommand('copy')) {
|
||||
document.execCommand('copy');
|
||||
setCopy(true);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='addMemByLinkBox'>
|
||||
<div className='font-16 mt20 mb10'>请选择邀请用户权限</div>
|
||||
<Select className='selectBox' defaultValue="developer" onChange={(v) => { setRole(v) }}>
|
||||
{optionList.map(item => {
|
||||
return <Select.Option value={item.value} key={item.value}>{item.name}</Select.Option>
|
||||
})}
|
||||
</Select>
|
||||
|
||||
<Checkbox checked={is_apply} className='font-15 checkBox' onChange={e => { setIs_apply(e.target.checked) }}>需要管理员审核</Checkbox>
|
||||
|
||||
<div className='font-16 mt25 mb10'>邀请链接</div>
|
||||
<Input
|
||||
id="inviteUrl"
|
||||
value={inviteUrl}
|
||||
readOnly
|
||||
addonAfter={<Button type='primary' onClick={inviteClick}>{copy ? '复制成功' : '复制链接'}</Button>} className='linkBox'
|
||||
/>
|
||||
<div className='tipBox mt25'>
|
||||
<div>注: </div>
|
||||
<div className='ml5'>
|
||||
1、管理员可通过分享邀请链接的方式,邀请其他成员加入项目<br />
|
||||
2、若已勾选管理员审核选项,用户接收邀请后管理员可在个人主页中“待办事项”窗口审核成员审核信息,若不需要管理员审核,成员接收邀请后,将直接加入项目
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default forwardRef(CollaboratorMemberByLink);
|
|
@ -57,7 +57,7 @@ class Index extends Component {
|
|||
<li className={flag ? "active" : ""}>
|
||||
<p>
|
||||
<Link to={`/${owner}/${projectsId}/settings`} className="w-100">
|
||||
<i className="iconfont icon-huabanfuben font-18 mr10"></i>基本设置
|
||||
<i className="iconfont icon-huabanfuben font-15 mr10"></i>基本设置
|
||||
</Link>
|
||||
</p>
|
||||
</li>
|
||||
|
@ -68,7 +68,7 @@ class Index extends Component {
|
|||
>
|
||||
<p>
|
||||
<Link to={`/${owner}/${projectsId}/settings/collaborators`} className="w-100">
|
||||
<i className="iconfont icon-chengyuan font-18 mr10"></i>
|
||||
<i className="iconfont icon-chengyuan font-15 mr10"></i>
|
||||
协作者管理
|
||||
</Link>
|
||||
</p>
|
||||
|
@ -80,7 +80,7 @@ class Index extends Component {
|
|||
>
|
||||
<p>
|
||||
<Link to={`/${owner}/${projectsId}/settings/webhooks`} className="w-100">
|
||||
<i className="iconfont icon-a-xuanzhongwebhookicon font-18 mr10 color-grey-9"></i>
|
||||
<i className="iconfont icon-a-xuanzhongwebhookicon font-15 mr10 color-grey-9"></i>
|
||||
网络钩子
|
||||
</Link>
|
||||
</p>
|
||||
|
@ -92,7 +92,7 @@ class Index extends Component {
|
|||
>
|
||||
<p>
|
||||
<Link to={`/${owner}/${projectsId}/settings/branches`} className="w-100">
|
||||
<i className="iconfont icon-fenzhi font-20 mr10"></i>
|
||||
<i className="iconfont icon-fenzhi font-15 mr10"></i>
|
||||
分支设置
|
||||
</Link>
|
||||
</p>
|
||||
|
@ -102,7 +102,7 @@ class Index extends Component {
|
|||
>
|
||||
<p>
|
||||
<Link to={`/${owner}/${projectsId}/settings/labels`} className="w-100">
|
||||
<i className="iconfont icon-xiangmubiaoqian font-18 mr10 color-grey-6"></i>
|
||||
<i className="iconfont icon-xiangmubiaoqian font-15 mr10 color-grey-6"></i>
|
||||
项目标记
|
||||
</Link>
|
||||
</p>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
content: '';
|
||||
}
|
||||
.baseForm{
|
||||
padding:15px 30px!important;
|
||||
padding:15px 0!important;
|
||||
}
|
||||
.collaboratorList{
|
||||
min-height: 400px;
|
||||
|
@ -243,4 +243,31 @@
|
|||
&>div:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
.addMemByLinkBox{
|
||||
color:#202d40;
|
||||
.selectBox {
|
||||
width: 55%;
|
||||
display: block;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.checkBox{
|
||||
color:#151d40;
|
||||
}
|
||||
.tipBox{
|
||||
background-color:rgba(199, 209, 255, 0.17);
|
||||
color:#7e849e;
|
||||
padding: 20px 150px 20px 25px;
|
||||
display: flex;
|
||||
}
|
||||
.linkBox{
|
||||
width: 55%;
|
||||
.ant-input-group-addon{
|
||||
padding: 0;
|
||||
}
|
||||
.ant-btn{
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -225,24 +225,24 @@ form{
|
|||
margin-bottom: 12px;
|
||||
border-radius:2px;
|
||||
background-color: #fff;
|
||||
// box-shadow: 0px 0px 2px rgba(0,0,0,0.2);
|
||||
padding: 20px 0;
|
||||
border:1px solid rgba(79, 108, 188, 0.21);
|
||||
&>li{
|
||||
font-size: 1rem;
|
||||
padding:0px 0px 0px 20px;
|
||||
font-size: 15px;
|
||||
padding:0px 0px 0px 25px;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
position: relative;
|
||||
& > p{
|
||||
height: 62px;
|
||||
line-height: 62px;
|
||||
height: 49px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding-right: 20px;
|
||||
margin:0px;
|
||||
border-bottom: 1px solid #eee;
|
||||
a{
|
||||
width:100%;
|
||||
color: #202d40;
|
||||
|
@ -264,10 +264,9 @@ form{
|
|||
& li.active::before{
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 15px;
|
||||
width: 6px;
|
||||
content: '';
|
||||
height: 33px;
|
||||
height: 100%;
|
||||
// background: #4CACFF;
|
||||
background: $primary-color;
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ class InfosUser extends Component {
|
|||
placeholder="输入项目名称关键字进行搜索"
|
||||
enterButton="搜索"
|
||||
size="large"
|
||||
onSearch={this.get_projects}
|
||||
onSearch={()=>{this.get_projects()}}
|
||||
className="list-r-Search"
|
||||
value={search}
|
||||
onChange={this.changeSearchValue}
|
||||
|
|
Loading…
Reference in New Issue