pull/837/head
kate! 2023-01-30 18:34:17 +08:00
commit 6f5fdf5bf4
13 changed files with 156 additions and 58 deletions

View File

@ -15,22 +15,27 @@ jobs:
gh api graphql -f query=' gh api graphql -f query='
query { query {
organization(login: "revoltchat"){ organization(login: "revoltchat"){
projectNext(number: 3) { projectV2(number: 3) {
id id
fields(first:20) { fields(first:20) {
nodes { nodes {
id ... on ProjectV2SingleSelectField {
name id
settings name
options {
id
name
}
}
} }
} }
} }
} }
}' > project_data.json }' > project_data.json
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
echo 'TODO_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV echo 'TODO_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
- name: Add issue to project - name: Add issue to project
env: env:
@ -39,11 +44,11 @@ jobs:
run: | run: |
item_id="$( gh api graphql -f query=' item_id="$( gh api graphql -f query='
mutation($project:ID!, $issue:ID!) { mutation($project:ID!, $issue:ID!) {
addProjectNextItem(input: {projectId: $project, contentId: $issue}) { addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) {
projectNextItem { item {
id id
} }
} }
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')" }' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectV2ItemById.item.id')"
echo 'ITEM_ID='$item_id >> $GITHUB_ENV echo 'ITEM_ID='$item_id >> $GITHUB_ENV

View File

@ -15,22 +15,27 @@ jobs:
gh api graphql -f query=' gh api graphql -f query='
query { query {
organization(login: "revoltchat"){ organization(login: "revoltchat"){
projectNext(number: 3) { projectV2(number: 3) {
id id
fields(first:20) { fields(first:20) {
nodes { nodes {
id ... on ProjectV2SingleSelectField {
name id
settings name
options {
id
name
}
}
} }
} }
} }
} }
}' > project_data.json }' > project_data.json
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
echo 'INCOMING_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Incoming PRs") |.id' project_data.json) >> $GITHUB_ENV echo 'INCOMING_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Incoming PRs") |.id' project_data.json) >> $GITHUB_ENV
- name: Add PR to project - name: Add PR to project
env: env:
@ -39,13 +44,13 @@ jobs:
run: | run: |
item_id="$( gh api graphql -f query=' item_id="$( gh api graphql -f query='
mutation($project:ID!, $pr:ID!) { mutation($project:ID!, $pr:ID!) {
addProjectNextItem(input: {projectId: $project, contentId: $pr}) { addProjectV2ItemById(input: {projectId: $project, contentId: $pr}) {
projectNextItem { item {
id id
} }
} }
}' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectNextItem.projectNextItem.id')" }' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectV2ItemById.item.id')"
echo 'ITEM_ID='$item_id >> $GITHUB_ENV echo 'ITEM_ID='$item_id >> $GITHUB_ENV
- name: Set fields - name: Set fields
@ -59,14 +64,16 @@ jobs:
$status_field: ID! $status_field: ID!
$status_value: String! $status_value: String!
) { ) {
set_status: updateProjectNextItemField(input: { set_status: updateProjectV2ItemFieldValue(input: {
projectId: $project projectId: $project
itemId: $item itemId: $item
fieldId: $status_field fieldId: $status_field
value: $status_value value: {
singleSelectOptionId: $status_value
}
}) { }) {
projectNextItem { projectV2Item {
id id
} }
} }
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent }' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent

2
external/lang vendored

@ -1 +1 @@
Subproject commit f95ec6dc8200adba7925425d4cf2ae0c16f049c3 Subproject commit 4eec9dd02afa6a7f9b68c336282d4819b9c30790

2
external/revolt.js vendored

@ -1 +1 @@
Subproject commit ab064f41a4154acee7c1751ea5b59d9ee4345a0b Subproject commit e1a98853b49adb78698af175e8b7c971ef501d5b

View File

@ -1,4 +1,7 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Link } from "react-router-dom";
import { Channel, User } from "revolt.js"; import { Channel, User } from "revolt.js";
import { Emoji as CustomEmoji } from "revolt.js/esm/maps/Emojis";
import styled, { css } from "styled-components/macro"; import styled, { css } from "styled-components/macro";
import { StateUpdater, useState } from "preact/hooks"; import { StateUpdater, useState } from "preact/hooks";
@ -7,6 +10,8 @@ import { emojiDictionary } from "../../assets/emojis";
import { useClient } from "../../controllers/client/ClientController"; import { useClient } from "../../controllers/client/ClientController";
import ChannelIcon from "./ChannelIcon"; import ChannelIcon from "./ChannelIcon";
import Emoji from "./Emoji"; import Emoji from "./Emoji";
import ServerIcon from "./ServerIcon";
import Tooltip from "./Tooltip";
import UserIcon from "./user/UserIcon"; import UserIcon from "./user/UserIcon";
export type AutoCompleteState = export type AutoCompleteState =
@ -14,7 +19,7 @@ export type AutoCompleteState =
| ({ selected: number; within: boolean } & ( | ({ selected: number; within: boolean } & (
| { | {
type: "emoji"; type: "emoji";
matches: string[]; matches: (string | CustomEmoji)[];
} }
| { | {
type: "user"; type: "user";
@ -104,16 +109,23 @@ export function useAutoComplete(
if (type === "emoji") { if (type === "emoji") {
// ! TODO: we should convert it to a Binary Search Tree and use that // ! TODO: we should convert it to a Binary Search Tree and use that
const matches = Object.keys(emojiDictionary) const matches = [
.filter((emoji: string) => emoji.match(regex)) ...Object.keys(emojiDictionary).filter((emoji: string) =>
.splice(0, 5); emoji.match(regex),
),
...Array.from(client.emojis.values()).filter((emoji) =>
emoji.name.match(regex),
),
].splice(0, 5);
if (matches.length > 0) { if (matches.length > 0) {
const currentPosition = const currentPosition =
state.type !== "none" ? state.selected : 0; state.type !== "none" ? state.selected : 0;
setState({ setState({
// @ts-ignore-next-line are you high
type: "emoji", type: "emoji",
// @ts-ignore-next-line
matches, matches,
selected: Math.min(currentPosition, matches.length - 1), selected: Math.min(currentPosition, matches.length - 1),
within: false, within: false,
@ -233,10 +245,13 @@ export function useAutoComplete(
const content = el.value.split(""); const content = el.value.split("");
if (state.type === "emoji") { if (state.type === "emoji") {
const selected = state.matches[state.selected];
content.splice( content.splice(
index, index,
search.length, search.length,
state.matches[state.selected], selected instanceof CustomEmoji
? selected._id
: selected,
": ", ": ",
); );
} else if (state.type === "user") { } else if (state.type === "user") {
@ -388,12 +403,17 @@ export default function AutoComplete({
setState, setState,
onClick, onClick,
}: Pick<AutoCompleteProps, "detached" | "state" | "setState" | "onClick">) { }: Pick<AutoCompleteProps, "detached" | "state" | "setState" | "onClick">) {
const client = useClient();
return ( return (
<Base detached={detached}> <Base detached={detached}>
<div> <div>
{state.type === "emoji" && {state.type === "emoji" &&
state.matches.map((match, i) => ( state.matches.map((match, i) => (
<button <button
style={{
display: "flex",
justifyContent: "space-between",
}}
key={match} key={match}
className={i === state.selected ? "active" : ""} className={i === state.selected ? "active" : ""}
onMouseEnter={() => onMouseEnter={() =>
@ -412,15 +432,61 @@ export default function AutoComplete({
}) })
} }
onClick={onClick}> onClick={onClick}>
<Emoji <div
emoji={ style={{
(emojiDictionary as Record<string, string>)[ display: "flex",
match flexDirection: "row",
] justifyContent: "center",
} }}>
size={20} {match instanceof CustomEmoji ? (
/> <img
:{match}: loading="lazy"
src={match.imageURL}
style={{
width: `20px`,
height: `20px`,
}}
/>
) : (
<Emoji
emoji={
(
emojiDictionary as Record<
string,
string
>
)[match]
}
size={20}
/>
)}
<span style={{ paddingLeft: "4px" }}>{`:${
match instanceof CustomEmoji
? match.name
: match
}:`}</span>
</div>
{match instanceof CustomEmoji &&
match.parent.type == "Server" && (
<>
<Tooltip
content={
client.servers.get(
match.parent.id,
)?.name
}>
<Link
to={`/server/${match.parent.id}`}>
<ServerIcon
target={client.servers.get(
match.parent.id,
)}
size={20}
/>
</Link>
</Tooltip>
</>
)}
</button> </button>
))} ))}
{state.type === "user" && {state.type === "user" &&

View File

@ -67,7 +67,8 @@ export default function Embed({ embed }: Props) {
break; break;
} }
case "Twitch": case "Twitch":
case "Lightspeed": { case "Lightspeed":
case "Streamable": {
mw = 1280; mw = 1280;
mh = 720; mh = 720;
break; break;

View File

@ -92,6 +92,16 @@ export default function EmbedMedia({ embed, width, height }: Props) {
/> />
); );
} }
case "Streamable": {
return (
<iframe
src={`https://streamable.com/e/${embed.special.id}?loop=0`}
seamless
loading="lazy"
style={{ height }}
/>
);
}
default: { default: {
if (embed.video) { if (embed.video) {
const url = embed.video.url; const url = embed.video.url;

View File

@ -25,7 +25,7 @@ const Emoji = styled.img`
} }
`; `;
const RE_EMOJI = /:([a-zA-Z0-9_+]+):/g; const RE_EMOJI = /:([a-zA-Z0-9\-_]+):/g;
const RE_ULID = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/; const RE_ULID = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/;
export function RenderEmoji({ match }: CustomComponentProps) { export function RenderEmoji({ match }: CustomComponentProps) {

View File

@ -17,6 +17,7 @@ const ALLOWED_ORIGINS = [
"app.revolt.chat", "app.revolt.chat",
"nightly.revolt.chat", "nightly.revolt.chat",
"local.revolt.chat", "local.revolt.chat",
"rolt.chat",
]; ];
/** /**

View File

@ -59,10 +59,12 @@ export default class Draft implements Store, Persistent<Data> {
* @param channel Channel ID * @param channel Channel ID
*/ */
@computed has(channel: string) { @computed has(channel: string) {
return ( if (!this.drafts.has(channel)) return false;
this.drafts.has(channel) && // fetch the draft object
this.drafts.get(channel)!.content!.length > 0 const potentialDraft = this.drafts.get(channel)?.content;
); // if it doesn't have any content return false
if (!potentialDraft) return false;
return potentialDraft.length > 0;
} }
/** /**

View File

@ -340,11 +340,13 @@ function ListElement({
const save = useCallback(() => { const save = useCallback(() => {
setEditing(undefined); setEditing(undefined);
setTitle!(editing!); if (editing !== "") {
setTitle!(editing!);
}
}, [editing, setTitle]); }, [editing, setTitle]);
useEffect(() => { useEffect(() => {
if (!editing) return; if (editing === undefined) return;
function onClick(ev: MouseEvent) { function onClick(ev: MouseEvent) {
if ((ev.target as HTMLElement)?.id !== category.id) { if ((ev.target as HTMLElement)?.id !== category.id) {
@ -368,7 +370,7 @@ function ListElement({
<div className="inner"> <div className="inner">
<Row> <Row>
<KanbanListHeader {...provided.dragHandleProps}> <KanbanListHeader {...provided.dragHandleProps}>
{editing ? ( {editing !== undefined ? (
<input <input
value={editing} value={editing}
onChange={(e) => onChange={(e) =>

View File

@ -70,6 +70,10 @@ export const Roles = observer(({ server }: Props) => {
} }
`; `;
const DeleteRoleButton = styled(Button)`
margin: 16px 0;
`;
return ( return (
<PermissionsLayout <PermissionsLayout
server={server} server={server}
@ -266,12 +270,12 @@ export const Roles = observer(({ server }: Props) => {
<h1> <h1>
<Text id="app.settings.categories.danger_zone" /> <Text id="app.settings.categories.danger_zone" />
</h1> </h1>
<Button <DeleteRoleButton
palette="error" palette="error"
compact compact
onClick={deleteRole}> onClick={deleteRole}>
<Text id="app.settings.permissions.delete_role" /> <Text id="app.settings.permissions.delete_role" />
</Button> </DeleteRoleButton>
</> </>
)} )}
</div> </div>

View File

@ -8016,14 +8016,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"revolt-api@npm:0.5.5-4": "revolt-api@npm:0.5.7":
version: 0.5.5-4 version: 0.5.7
resolution: "revolt-api@npm:0.5.5-4" resolution: "revolt-api@npm:0.5.7"
dependencies: dependencies:
"@insertish/oapi": 0.1.18 "@insertish/oapi": 0.1.18
axios: ^0.26.1 axios: ^0.26.1
lodash.defaultsdeep: ^4.6.1 lodash.defaultsdeep: ^4.6.1
checksum: dfb374d58f1b8b5a6de2e7fa05e386b3df3ffb85a450a6894f23c6b9760af8bff0d198d8352063c33084f0dbc6ff84a234300efb0c62c7b27f709887402787f1 checksum: ce5d24f1f71c039789b71c33cce12a9bc1ee4709e2c37f88ac95caff193096ff4f5e90fd48ba0ed6f6bbb71d080cd410f8ba8a3998da8bd8083766a016b18306
languageName: node languageName: node
linkType: hard linkType: hard
@ -8040,7 +8040,7 @@ __metadata:
lodash.isequal: ^4.5.0 lodash.isequal: ^4.5.0
long: ^5.2.0 long: ^5.2.0
mobx: ^6.3.2 mobx: ^6.3.2
revolt-api: 0.5.5-4 revolt-api: 0.5.7
ulid: ^2.3.0 ulid: ^2.3.0
ws: ^8.2.2 ws: ^8.2.2
languageName: node languageName: node