mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
core&ui: drop graphql apis
This commit is contained in:
1 parent
0f7961d675
commit
6e16d03f67
52 files changed
+485
-583
No files matched your search
@@ -134,7 +134,8 @@ export default async function (env: { watch?: boolean, production?: boolean, mea
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx', '.cjs'],
|
||||
alias: {
|
||||
vj: root(),
|
||||
'graphql-ws': root('../../modules/nop.ts'),
|
||||
'react/jsx-runtime': require.resolve('react/jsx-runtime'),
|
||||
react: require.resolve('react'),
|
||||
},
|
||||
},
|
||||
module: {
|
||||
@@ -291,7 +292,6 @@ export default async function (env: { watch?: boolean, production?: boolean, mea
|
||||
{ from: root('components/navigation/nav-logo-small_dark.png'), to: 'components/navigation/nav-logo-small_dark.png' },
|
||||
{ from: root(`${dirname(require.resolve('streamsaver/package.json'))}/mitm.html`), to: 'streamsaver/mitm.html' },
|
||||
{ from: root(`${dirname(require.resolve('streamsaver/package.json'))}/sw.js`), to: 'streamsaver/sw.js' },
|
||||
{ from: root(`${dirname(require.resolve('graphiql/package.json'))}/graphiql.min.css`), to: 'graphiql.min.css' },
|
||||
{ from: `${dirname(require.resolve('monaco-themes/package.json'))}/themes`, to: 'monaco/themes/' },
|
||||
],
|
||||
}),
|
||||
|
||||
+2
-8
@@ -2,7 +2,7 @@ import { AutoComplete, AutoCompleteHandle, AutoCompleteProps } from '@hydrooj/co
|
||||
import type { ProblemDoc } from 'hydrooj/src/interface';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { forwardRef } from 'react';
|
||||
import { api, gql, request } from 'vj/utils';
|
||||
import { api, request } from 'vj/utils';
|
||||
|
||||
const ProblemSelectAutoComplete = forwardRef<AutoCompleteHandle<ProblemDoc>, AutoCompleteProps<ProblemDoc>>((props, ref) => (
|
||||
<AutoComplete<ProblemDoc>
|
||||
@@ -12,13 +12,7 @@ const ProblemSelectAutoComplete = forwardRef<AutoCompleteHandle<ProblemDoc>, Aut
|
||||
const { pdocs } = await request.get(`/d/${UiContext.domainId}/p`, { q: query, quick: true });
|
||||
return pdocs;
|
||||
}}
|
||||
fetchItems={(ids) => api(gql`
|
||||
problems(ids: ${ids.map((i) => +i)}) {
|
||||
docId
|
||||
pid
|
||||
title
|
||||
}
|
||||
`, ['data', 'problems'])}
|
||||
fetchItems={(ids) => api('problems', { ids: ids.map((i) => +i) }, ['docId', 'pid', 'title'])}
|
||||
itemText={(pdoc) => `${`${pdoc.docId} ${pdoc.title}`}`}
|
||||
itemKey={(pdoc) => `${pdoc.docId || pdoc}`}
|
||||
renderItem={(pdoc) => (
|
||||
|
||||
@@ -2,27 +2,14 @@ import { AutoComplete, AutoCompleteHandle, AutoCompleteProps } from '@hydrooj/co
|
||||
import type { Udoc } from 'hydrooj/src/interface';
|
||||
import PropTypes from 'prop-types';
|
||||
import { forwardRef } from 'react';
|
||||
import { api, gql } from 'vj/utils';
|
||||
import { api } from 'vj/utils';
|
||||
|
||||
const UserSelectAutoComplete = forwardRef<AutoCompleteHandle<Udoc>, AutoCompleteProps<Udoc>>((props, ref) => (
|
||||
<AutoComplete<Udoc>
|
||||
ref={ref as any}
|
||||
cacheKey="user"
|
||||
queryItems={(query) => api(gql`
|
||||
users(search: ${query}) {
|
||||
_id
|
||||
uname
|
||||
displayName
|
||||
avatarUrl
|
||||
}
|
||||
`, ['data', 'users'])}
|
||||
fetchItems={(ids) => api(gql`
|
||||
users(auto: ${ids}) {
|
||||
_id
|
||||
uname
|
||||
displayName
|
||||
}
|
||||
`, ['data', 'users'])}
|
||||
queryItems={(query) => api('users', { search: query }, ['_id', 'uname', 'displayName', 'avatarUrl'])}
|
||||
fetchItems={(ids) => api('users', { auto: ids }, ['_id', 'uname', 'displayName'])}
|
||||
itemText={(user) => user.uname + (user.displayName ? ` (${user.displayName})` : '')}
|
||||
itemKey={(user) => ((props.multi || /^[+-]?\d+$/.test(user.uname.trim())) ? user._id.toString() : user.uname)}
|
||||
renderItem={(user) => (
|
||||
|
||||
@@ -16,6 +16,7 @@ const mapStateToProps = (state) => ({
|
||||
: null,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export default connect(mapStateToProps)(class MessagePadDialogueContentContainer extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
$(this.refs.list).scrollLock({ strict: true });
|
||||
|
||||
@@ -21,6 +21,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(class MessagePadDialogueListContainer extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
$(this.refs.list).scrollLock({ strict: true });
|
||||
|
||||
@@ -42,6 +42,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(class MessagePadInputContainer extends React.PureComponent {
|
||||
static contextTypes = {
|
||||
store: PropTypes.object,
|
||||
|
||||
@@ -16,6 +16,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export default connect(null, mapDispatchToProps)(class MessagePadContainer extends React.PureComponent {
|
||||
static propTypes = {
|
||||
onAdd: PropTypes.func.isRequired,
|
||||
|
||||
@@ -2,7 +2,7 @@ import keyword from 'emojis-keywords';
|
||||
import list from 'emojis-list';
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
import qface from 'qface';
|
||||
import { api, gql } from 'vj/utils';
|
||||
import { api } from 'vj/utils';
|
||||
|
||||
function emoji(range) {
|
||||
return keyword.map((i, index) => ({
|
||||
@@ -40,12 +40,7 @@ monaco.languages.registerCodeLensProvider('markdown', {
|
||||
dispose: () => { },
|
||||
};
|
||||
}
|
||||
const { data } = await api(gql`
|
||||
users(ids: ${users.map((i) => +i.matches[1])}) {
|
||||
_id
|
||||
uname
|
||||
}
|
||||
`);
|
||||
const data = await api('users', { ids: users.map((i) => +i.matches[1]) }, ['_id', 'uname']);
|
||||
return {
|
||||
lenses: users.map((i, index) => ({
|
||||
range: i.range,
|
||||
@@ -53,7 +48,7 @@ monaco.languages.registerCodeLensProvider('markdown', {
|
||||
command: {
|
||||
id: 'hydro.openUserPage',
|
||||
arguments: [i.matches[1]],
|
||||
title: `@${data.users.find((doc) => doc._id.toString() === i.matches[1])?.uname || i.matches[1]}`,
|
||||
title: `@${data.find((doc) => doc._id.toString() === i.matches[1])?.uname || i.matches[1]}`,
|
||||
},
|
||||
})),
|
||||
dispose: () => { },
|
||||
@@ -82,14 +77,7 @@ monaco.languages.registerCompletionItemProvider('markdown', {
|
||||
endColumn: word.endColumn,
|
||||
};
|
||||
if (prefix === '@') {
|
||||
const users = await api(gql`
|
||||
users(search: ${word.word}) {
|
||||
_id
|
||||
uname
|
||||
avatarUrl
|
||||
priv
|
||||
}
|
||||
`, ['data', 'users']);
|
||||
const users = await api('users', { search: word.word }, ['_id', 'uname']);
|
||||
return {
|
||||
suggestions: users.map((i) => ({
|
||||
label: { label: `@${i.uname}`, description: `UID=${i._id}` },
|
||||
|
||||
@@ -4,7 +4,7 @@ import ReactDOM from 'react-dom/client';
|
||||
import { STATUS_CODES } from 'vj/constant/record';
|
||||
import { AutoloadPage } from 'vj/misc/Page';
|
||||
import {
|
||||
api, gql, i18n, request,
|
||||
api, i18n, request,
|
||||
} from 'vj/utils';
|
||||
|
||||
export default new AutoloadPage('omnibar', () => {
|
||||
@@ -92,14 +92,7 @@ export default new AutoloadPage('omnibar', () => {
|
||||
setSearching?.(true);
|
||||
[{ pdocs, psdict }, udocs] = await Promise.all([
|
||||
request.get(`/d/${UiContext.domainId}/p`, { q: query, limit: 10 }),
|
||||
api(gql`
|
||||
users(search: ${query}) {
|
||||
_id
|
||||
uname
|
||||
displayName
|
||||
avatarUrl
|
||||
}
|
||||
`, ['data', 'users']),
|
||||
api('users', { search: query }, ['_id', 'uname', 'displayName', 'avatarUrl']),
|
||||
]);
|
||||
setSearching?.(false);
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ export function AddTestcase() {
|
||||
const [autoOutput, setAutoOutput] = React.useState(true);
|
||||
const testdata = useSelector((state: RootState) => state.testdata);
|
||||
const store = useStore<RootState>();
|
||||
const refInput = useRef();
|
||||
const refOutput = useRef();
|
||||
const refInput = useRef(null);
|
||||
const refOutput = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const inputValid = testdata.find((i) => i.name === input);
|
||||
|
||||
@@ -13,6 +13,7 @@ interface ScratchpadOptions {
|
||||
commandDone?: () => void;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export default connect((state: any) => ({
|
||||
value: state.editor.code,
|
||||
language: window.LANGS[state.editor.lang]?.monaco,
|
||||
|
||||
@@ -27,6 +27,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(class ScratchpadPretestContainer extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
|
||||
@@ -11,6 +11,7 @@ const mapStateToProps = (state) => ({
|
||||
isLoading: state.ui.records.isLoading,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export default connect(mapStateToProps)(class ScratchpadRecordsContainer extends React.PureComponent {
|
||||
render() {
|
||||
const cn = classNames('data-table is--full-row scratchpad__records__table', {
|
||||
|
||||
@@ -46,6 +46,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => ({
|
||||
data: stateProps.items[ownProps.id],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export default connect(mapStateToProps, null, mergeProps)(class ScratchpadRecordsRowContainer extends React.PureComponent {
|
||||
handleRowClick(ev, id) {
|
||||
const url = substitute(
|
||||
|
||||
@@ -73,6 +73,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
const availableLangs = getAvailableLangs(UiContext.pdoc.config.langs);
|
||||
const keys = Object.keys(availableLangs);
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(class ScratchpadToolbarContainer extends React.PureComponent {
|
||||
static contextTypes = {
|
||||
store: PropTypes.object,
|
||||
|
||||
@@ -4,7 +4,7 @@ import PQueue from 'p-queue';
|
||||
import streamsaver from 'streamsaver';
|
||||
import Notification from 'vj/components/notification';
|
||||
import {
|
||||
api, createZipStream, gql, i18n, pipeStream, request,
|
||||
api, createZipStream, i18n, pipeStream, request,
|
||||
} from 'vj/utils';
|
||||
import { ctx } from '../../context';
|
||||
|
||||
@@ -94,23 +94,21 @@ export async function downloadProblemSet(pids, name = 'Export') {
|
||||
try {
|
||||
await ctx.serial('problemset/download', pids, name, targets);
|
||||
for (const pid of pids) {
|
||||
const pdoc = await api(gql`
|
||||
problem(id: ${+pid}) {
|
||||
pid
|
||||
owner
|
||||
title
|
||||
content
|
||||
tag
|
||||
nSubmit
|
||||
nAccept
|
||||
data {
|
||||
name
|
||||
}
|
||||
additional_file {
|
||||
name
|
||||
}
|
||||
}
|
||||
`, ['data', 'problem']);
|
||||
const pdoc = await api('problem', { id: +pid }, {
|
||||
pid: 1,
|
||||
owner: 1,
|
||||
title: 1,
|
||||
content: 1,
|
||||
tag: 1,
|
||||
nSubmit: 1,
|
||||
nAccept: 1,
|
||||
data: {
|
||||
name: 1,
|
||||
},
|
||||
additional_file: {
|
||||
name: 1,
|
||||
},
|
||||
});
|
||||
targets.push({
|
||||
filename: `${pid}/problem.yaml`,
|
||||
content: dump({
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"cli": false
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blueprintjs/core": "^5.18.0",
|
||||
"@blueprintjs/core": "^5.19.0",
|
||||
"@fontsource/dm-mono": "^5.2.5",
|
||||
"@fontsource/fira-code": "^5.2.5",
|
||||
"@fontsource/inconsolata": "^5.2.5",
|
||||
@@ -21,7 +21,7 @@
|
||||
"@fontsource/source-code-pro": "^5.2.5",
|
||||
"@fontsource/ubuntu-mono": "^5.2.5",
|
||||
"@hydrooj/utils": "workspace:^",
|
||||
"@sentry/browser": "^9.13.0",
|
||||
"@sentry/browser": "^9.14.0",
|
||||
"@sentry/webpack-plugin": "^3.3.1",
|
||||
"@simplewebauthn/browser": "13.1.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
@@ -60,7 +60,6 @@
|
||||
"emojis-list": "2.1.0",
|
||||
"esbuild-loader": "^4.3.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"graphiql": "^3.8.3",
|
||||
"idb": "^8.0.2",
|
||||
"jquery": "^3.7.1",
|
||||
"jquery-scroll-lock": "^3.1.3",
|
||||
@@ -99,7 +98,7 @@
|
||||
"redux-promise-middleware": "^6.2.0",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"rupture": "^0.7.1",
|
||||
"sass": "^1.86.3",
|
||||
"sass": "^1.87.0",
|
||||
"sass-loader": "^16.0.5",
|
||||
"schemastery": "^3.16.1",
|
||||
"schemastery-react": "^0.1.2",
|
||||
@@ -116,7 +115,7 @@
|
||||
"timeago.js": "^4.0.2",
|
||||
"ts-loader": "^9.5.2",
|
||||
"web-streams-polyfill": "^4.1.0",
|
||||
"webpack": "^5.99.6",
|
||||
"webpack": "^5.99.7",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-dev-server": "^5.2.1",
|
||||
"webpack-manifest-plugin": "^5.0.1",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
.graphiql-container .doc-explorer-title, .graphiql-container .history-title
|
||||
overflow: hidden;
|
||||
padding: 5px 40px 0px 0px !important
|
||||
|
||||
.graphiql-container .docExplorerWrap
|
||||
font-family: var(--code-font-family) !important
|
||||
|
||||
.graphiql-container .doc-explorer-back
|
||||
overflow: hidden;
|
||||
padding: 8px 20px 0px 20px !important
|
||||
@@ -1,29 +0,0 @@
|
||||
import { pick } from 'lodash';
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { NamedPage } from 'vj/misc/Page';
|
||||
import { request } from 'vj/utils';
|
||||
|
||||
const defaultQuery = `\
|
||||
query Example(
|
||||
$name: String!
|
||||
) {
|
||||
user(uname: $name) {
|
||||
_id
|
||||
uname
|
||||
}
|
||||
}`;
|
||||
|
||||
export default new NamedPage('api', async () => {
|
||||
const { GraphiQL } = await import('graphiql');
|
||||
GraphiQL.Logo = function () {
|
||||
return <p></p>;
|
||||
} as any;
|
||||
createRoot(document.getElementById('main')!).render(
|
||||
<GraphiQL
|
||||
fetcher={(body, opts = {}) => request.post(`/d/${UiContext.domain._id}/api`, body, pick(opts, 'headers'))}
|
||||
defaultQuery={defaultQuery}
|
||||
variables='{"name": "Hydro"}'
|
||||
/>,
|
||||
);
|
||||
});
|
||||
@@ -5,22 +5,19 @@ import { ActionDialog, ConfirmDialog } from 'vj/components/dialog';
|
||||
import Notification from 'vj/components/notification';
|
||||
import { NamedPage } from 'vj/misc/Page';
|
||||
import {
|
||||
api, delay, gql, i18n, tpl,
|
||||
api, delay, i18n, tpl,
|
||||
} from 'vj/utils';
|
||||
|
||||
function update(name: string, uids: number[]) {
|
||||
return api(gql`
|
||||
domain { manage { group {
|
||||
update(name: ${name}, uids: ${uids})
|
||||
} } }
|
||||
`);
|
||||
function update(name: string, uids: number[] = []) {
|
||||
return api('domain.group', {
|
||||
name,
|
||||
uids,
|
||||
}, []);
|
||||
}
|
||||
function del(name: string) {
|
||||
return api(gql`
|
||||
domain { manage { group {
|
||||
del(name: ${name})
|
||||
} } }
|
||||
`);
|
||||
return api('domain.group', {
|
||||
name,
|
||||
}, []);
|
||||
}
|
||||
|
||||
const page = new NamedPage('domain_group', () => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import VjNotification from 'vj/components/notification';
|
||||
import selectUser from 'vj/components/selectUser';
|
||||
import { ctx, Service } from 'vj/context';
|
||||
import { NamedPage } from 'vj/misc/Page';
|
||||
import { api, gql, loadReactRedux } from 'vj/utils';
|
||||
import { api, loadReactRedux } from 'vj/utils';
|
||||
|
||||
class MessagePadService extends Service {
|
||||
WebSocket: typeof import('../components/socket').default;
|
||||
@@ -91,14 +91,10 @@ const page = new NamedPage('home_messages', () => {
|
||||
async function loadSendTarget() {
|
||||
const target = new URL(window.location.href).searchParams.get('target');
|
||||
if (!target) return;
|
||||
const user = await api(gql`
|
||||
users(search: ${target}, exact: true) {
|
||||
_id
|
||||
uname
|
||||
avatarUrl
|
||||
mail
|
||||
}
|
||||
`, ['data', 'users']);
|
||||
const user = await api(
|
||||
'users', { search: target, exact: true },
|
||||
['_id', 'uname', 'avatarUrl', 'mail'],
|
||||
);
|
||||
if (!user?.length) return;
|
||||
createDialog(user[0]);
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{% extends "layout/basic.html" %}
|
||||
{% block content %}
|
||||
<div style="height: 90vh" id="main"></div>
|
||||
<style>
|
||||
#main {
|
||||
font-family: var(--code-font-family)
|
||||
}
|
||||
</style>
|
||||
<link href="/graphiql.min.css" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
@@ -23,7 +23,6 @@
|
||||
</h1>
|
||||
<div class="footer__category__expander"><ul class="footer__category__list">
|
||||
<li class="footer__category__item"><a href="https://github.com/hydro-dev/Hydro" target="_blank">{{ _('Open Source') }}</a></li>
|
||||
<li class="footer__category__item"><a href="{{ url('api') }}">{{ _('API') }}</a></li>
|
||||
</ul></div>
|
||||
</div>
|
||||
<div class="medium-3 large-2 columns footer__category expandable end">
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
<div class="section__body no-padding">
|
||||
<div data-fragment-id="ranking">
|
||||
{% if not udocs.length %}
|
||||
<blockquote class="note">{{ _('The ranking page is not updated realtime.') }}</blockquote>
|
||||
{% if page == 1 %}
|
||||
<blockquote class="note">{{ _('The ranking page is not updated realtime.') }}</blockquote>
|
||||
{% endif %}
|
||||
{{ nothing.render('Sorry, there are no users in this domain.') }}
|
||||
{% else %}
|
||||
<table class="data-table">
|
||||
|
||||
@@ -2,30 +2,11 @@ import 'streamsaver/examples/zip-stream';
|
||||
|
||||
import { request } from './base';
|
||||
|
||||
export async function api(q: string, path: string[] = []) {
|
||||
let query = q.trim();
|
||||
if (!query.startsWith('query')) query = `query{${query}}`;
|
||||
const res = await request.post(`/d/${UiContext.domainId}/api`, { query });
|
||||
if (res.errors) throw new Error(res.errors[0].message);
|
||||
let cursor = res;
|
||||
for (const p of path) {
|
||||
cursor = cursor[p];
|
||||
if (!cursor) return undefined;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
export const gql = (
|
||||
pieces: TemplateStringsArray,
|
||||
...templates: (string | number | string[] | number[])[]
|
||||
) => {
|
||||
let res = '';
|
||||
for (let i = 0; i < pieces.length; i++) {
|
||||
res += pieces[i];
|
||||
if (templates[i]) res += JSON.stringify(templates[i]);
|
||||
}
|
||||
export async function api(method: string, args: Record<string, any>, projection: any) {
|
||||
const res = await request.post(`/d/${UiContext.domainId}/api/${encodeURIComponent(method)}`, { args, projection });
|
||||
if (res.error) throw new Error(res.error);
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
export function getAvailableLangs(langsList?: string[]) {
|
||||
const prefixes = new Set(Object.keys(window.LANGS).filter((i) => i.includes('.')).map((i) => i.split('.')[0]));
|
||||
|
||||
Reference in new issue
Block a user