3 Commits

Author SHA1 Message Date
izzy
57e8c0e274 chore: sign post new app & fix logo 2026-06-23 09:55:47 +01:00
izzy
38f95897ca ci: brand assets
Signed-off-by: izzy <me@insrt.uk>
2026-06-23 09:06:08 +01:00
izzy
4cb8b370fe ci: build to registry
Signed-off-by: izzy <me@insrt.uk>
2026-06-23 08:51:37 +01:00
31 changed files with 412 additions and 492 deletions

1
.envrc
View File

@@ -1 +0,0 @@
use flake

66
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
name: Bug report
description: File a bug report
title: "bug: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: what-happened
attributes:
label: What happened?
description: What did you expect to happen?
validations:
required: true
- type: dropdown
id: branch
attributes:
label: Branch
description: What branch of Revolt are you using?
options:
- Production (app.revolt.chat)
- Nightly (nightly.revolt.chat)
validations:
required: true
- type: textarea
id: commit-hash
attributes:
label: Commit hash
description: What is your commit hash? You can find this at the bottom of Settings, next to the branch name.
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: What browsers are you seeing the problem on?
multiple: true
options:
- Firefox
- Chrome
- Safari
- Microsoft Edge
- Other (please specify in the "What happened" form)
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. (To get this, press `CTRL`- or `CMD`-`SHIFT`-`I` and navigate to the "Console" tab.)
render: shell
- type: checkboxes
id: desktop
attributes:
label: Desktop
description: Is this bug specific to [the desktop client](https://github.com/revoltchat/desktop)? (If not, leave this unchecked.)
options:
- label: Yes, this bug is specific to Revolt Desktop and is *not* an issue with Revolt Desktop itself.
required: false
- type: checkboxes
id: pwa
attributes:
label: PWA
description: Is this bug specific to the PWA (i.e. "installing" the web app on iOS or Android)? (If not, leave this unchecked.)
options:
- label: Yes, this bug is specific to the PWA.
required: false

7
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
contact_links:
- name: Lounge Chat
url: https://rvlt.gg/Testers
about: Ask questions and discuss with others.
- name: Discussions
url: https://github.com/orgs/revoltchat/discussions
about: For larger feature requests and general question & answer.

24
.github/ISSUE_TEMPLATE/feature.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Feature request
description: Make a feature request
title: "feature request: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Before you start, a lot of bigger features may be better suited as [API issues](https://github.com/revoltchat/delta/issues/new) or [centralised discussions](https://github.com/revoltchat/revolt/discussions/new).
- type: textarea
id: your-idea
attributes:
label: What do you want to see?
description: Describe your idea in as much detail as possible - if applicable, screenshots/mockups are really useful.
validations:
required: true
- type: checkboxes
id: pwa
attributes:
label: PWA
description: Is this feature request specific to the PWA (i.e. "installing" the web app on iOS or Android)? (If not, leave this unchecked.)
options:
- label: Yes, this feature request is specific to the PWA.
required: false

7
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,7 @@
## Please make sure to check the following tasks before opening and submitting a PR
* [ ] I understand and have followed the [contribution guide](https://developers.revolt.chat/contrib.html)
* [ ] I have tested my changes locally and they are working as intended
* [ ] These changes do not have any notable side effects on other Revolt projects
* [ ] (optional) I have opened a pull request on [the translation repository](https://github.com/revoltchat/translations)
* [ ] I have included screenshots to demonstrate my changes

View File

@@ -3,53 +3,71 @@ name: Docker
on: on:
push: push:
branches: branches:
- "handmade" - "master"
tags:
- "*"
paths-ignore:
- ".github/**"
- "!.github/workflows/docker.yml"
- "!.github/workflows/preview_*.yml"
- ".vscode/**"
- ".gitignore"
- ".gitlab-ci.yml"
- "LICENSE"
- "README"
pull_request: pull_request:
branches: branches:
- "handmade" - "master"
release: paths-ignore:
types: - ".github/**"
- published - "!.github/workflows/docker.yml"
- "!.github/workflows/preview_*.yml"
- ".vscode/**"
- ".gitignore"
- ".gitlab-ci.yml"
- "LICENSE"
- "README"
workflow_dispatch:
jobs: jobs:
publish: publish:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: if: github.event_name != 'pull_request'
contents: read
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: "recursive" submodules: "recursive"
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Resolve registry host
id: registry
run: echo "host=${GITHUB_SERVER_URL#*://}" >> "$GITHUB_OUTPUT"
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
with: with:
images: ${{ steps.registry.outputs.host }}/${{ github.repository_owner }}/handmade-revolt-web-client images: revoltchat/client, ghcr.io/revoltchat/client
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest
env: env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
- name: Login to registry - name: Login to DockerHub
uses: docker/login-action@v3 uses: docker/login-action@v1
if: github.event_name == 'release' if: github.event_name != 'pull_request'
with: with:
registry: ${{ steps.registry.outputs.host }} username: ${{ secrets.DOCKERHUB_USERNAME }}
username: ${{ secrets.PACKAGE_PUBLISH_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }}
password: ${{ secrets.PACKAGE_PUBLISH_TOKEN }} - name: Login to Github Container Registry
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and publish - name: Build and publish
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name != 'pull_request' }}
platforms: linux/amd64 platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
annotations: ${{ steps.meta.outputs.annotations }} annotations: ${{ steps.meta.outputs.annotations }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}

44
.github/workflows/harbor.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: Publish to Harbor
on:
push:
branches:
- "master"
workflow_dispatch:
jobs:
legacy-frontend:
name: Build and push to Harbor
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: "recursive"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Harbor
uses: docker/login-action@v3
with:
registry: registry.stoatinternal.com
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
registry.stoatinternal.com/stoat/legacy-frontend:${{ github.sha }}
registry.stoatinternal.com/stoat/legacy-frontend:latest
build-args: |
REVOLT_PUBLIC_URL=https://api.stoat.chat/
REVOLT_SAAS=https://git.stoatinternal.com/brand/assets.git
REVOLT_SAAS_BRANCH=for-legacy-web
cache-from: type=gha
cache-to: type=gha,mode=max

54
.github/workflows/triage_issue.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Add Issue to Board
on:
issues:
types: [opened]
jobs:
track_issue:
runs-on: ubuntu-latest
steps:
- name: Get project data
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
run: |
gh api graphql -f query='
query {
organization(login: "revoltchat"){
projectV2(number: 3) {
id
fields(first:20) {
nodes {
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
}
}
}
}
}' > project_data.json
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.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.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
- name: Add issue to project
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
ISSUE_ID: ${{ github.event.issue.node_id }}
run: |
item_id="$( gh api graphql -f query='
mutation($project:ID!, $issue:ID!) {
addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) {
item {
id
}
}
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectV2ItemById.item.id')"
echo 'ITEM_ID='$item_id >> $GITHUB_ENV

79
.github/workflows/triage_pr.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: Add PR to Board
on:
pull_request_target:
types: [opened, synchronize, ready_for_review, review_requested]
jobs:
track_pr:
runs-on: ubuntu-latest
steps:
- name: Get project data
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
run: |
gh api graphql -f query='
query {
organization(login: "revoltchat"){
projectV2(number: 5) {
id
fields(first:20) {
nodes {
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
}
}
}
}
}' > project_data.json
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.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.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="🆕 Untriaged") |.id' project_data.json) >> $GITHUB_ENV
- name: Add PR to project
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
PR_ID: ${{ github.event.pull_request.node_id }}
run: |
item_id="$( gh api graphql -f query='
mutation($project:ID!, $pr:ID!) {
addProjectV2ItemById(input: {projectId: $project, contentId: $pr}) {
item {
id
}
}
}' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectV2ItemById.item.id')"
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
- name: Set fields
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
run: |
gh api graphql -f query='
mutation (
$project: ID!
$item: ID!
$status_field: ID!
$status_value: String!
) {
set_status: updateProjectV2ItemFieldValue(input: {
projectId: $project
itemId: $item
fieldId: $status_field
value: {
singleSelectOptionId: $status_value
}
}) {
projectV2Item {
id
}
}
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent

2
.gitignore vendored
View File

@@ -15,5 +15,3 @@ public/assets_*
!public/assets_default !public/assets_default
.vscode/chrome_data .vscode/chrome_data
.direnv

View File

@@ -8,11 +8,21 @@ COPY .env.build .env
RUN yarn install --frozen-lockfile RUN yarn install --frozen-lockfile
RUN yarn build:deps RUN yarn build:deps
# RUN yarn typecheck # lol no # RUN yarn typecheck # lol no
ARG REVOLT_SAAS
ARG REVOLT_SAAS_BRANCH
ENV REVOLT_SAAS=$REVOLT_SAAS
ENV REVOLT_SAAS_BRANCH=$REVOLT_SAAS_BRANCH
RUN yarn build:highmem RUN yarn build:highmem
RUN yarn workspaces focus --production --all RUN yarn workspaces focus --production --all
FROM node:24-alpine FROM node:24-alpine
WORKDIR /usr/src/app WORKDIR /usr/src/app
ARG REVOLT_PUBLIC_URL=https://api.stoat.chat/
ENV REVOLT_PUBLIC_URL=$REVOLT_PUBLIC_URL
COPY docker/package.json docker/yarn.lock . COPY docker/package.json docker/yarn.lock .
RUN yarn install --frozen-lockfile RUN yarn install --frozen-lockfile
COPY --from=builder --exclude=package.json --exclude=yarn.lock --exclude=.yarn* --exclude=.git --exclude=external --exclude=node_modules /usr/src/app . COPY --from=builder --exclude=package.json --exclude=yarn.lock --exclude=.yarn* --exclude=.git --exclude=external --exclude=node_modules /usr/src/app .

View File

@@ -1,5 +1,8 @@
# Handmade Revolt # Deprecation Notice
Fork of Revolt (now Stoat chat) maintained by the handmade cities community.
This project is deprecated, however it still may receive maintenance updates.
PRs for small fixes are more than welcome.
## Deploying a new release ## Deploying a new release

64
flake.lock generated
View File

@@ -1,64 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1770169770,
"narHash": "sha256-awR8qIwJxJJiOmcEGgP2KUqYmHG4v/z8XpL9z8FnT1A=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "aa290c9891fa4ebe88f8889e59633d20cc06a5f2",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"systems": "systems"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,28 +0,0 @@
{
description = "Node+yarn dev shell flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
systems.url = "github:nix-systems/default";
flake-utils = {
url = "github:numtide/flake-utils";
inputs.systems.follows = "systems";
};
};
outputs =
{ nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShells.default = pkgs.mkShell {
packages = with pkgs; [
nodejs_24
yarn
];
};
}
);
}

View File

@@ -1,11 +1,11 @@
<!DOCTYPE html> <!doctype html>
<html lang="en" background="#191919"> <html lang="en" background="#191919">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<!--App Title--> <!--App Title-->
<title>Revolt</title> <title>Stoat (Legacy)</title>
<meta name="apple-mobile-web-app-title" content="Revolt" /> <meta name="apple-mobile-web-app-title" content="Stoat (Legacy)" />
<!--App Scaling--> <!--App Scaling-->
<meta <meta

View File

@@ -16,11 +16,6 @@ import { IconButton } from "@revoltchat/ui";
import { emojiDictionary } from "../../../../assets/emojis"; import { emojiDictionary } from "../../../../assets/emojis";
import { useClient } from "../../../../controllers/client/ClientController"; import { useClient } from "../../../../controllers/client/ClientController";
import { modalController } from "../../../../controllers/modals/ModalController";
import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice";
import Tooltip from "../../Tooltip";
import UserIcon from "../../user/UserIcon";
import { Username } from "../../user/UserShort";
import { RenderEmoji } from "../../../markdown/plugins/emoji"; import { RenderEmoji } from "../../../markdown/plugins/emoji";
import { HackAlertThisFileWillBeReplaced } from "../MessageBox"; import { HackAlertThisFileWillBeReplaced } from "../MessageBox";
@@ -90,38 +85,6 @@ const Reaction = styled.div<{ active: boolean }>`
`} `}
`; `;
/**
* Tooltip content for reaction users
*/
const TooltipUserList = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
padding: 4px 0;
max-width: 200px;
`;
const TooltipUserRow = styled.div`
display: flex;
align-items: center;
gap: 6px;
font-size: 0.9em;
padding: 4px 6px;
margin: -4px -6px;
border-radius: var(--border-radius);
cursor: pointer;
&:hover {
background: var(--secondary-background);
}
`;
const TooltipMoreText = styled.div`
font-size: 0.85em;
color: var(--secondary-foreground);
padding-top: 2px;
`;
/** /**
* Render reactions on a message * Render reactions on a message
*/ */
@@ -135,97 +98,16 @@ export const Reactions = observer(({ message }: Props) => {
const Entry = useCallback( const Entry = useCallback(
observer(({ id, user_ids }: { id: string; user_ids?: Set<string> }) => { observer(({ id, user_ids }: { id: string; user_ids?: Set<string> }) => {
const active = user_ids?.has(client.user!._id) || false; const active = user_ids?.has(client.user!._id) || false;
const userIds = user_ids ? Array.from(user_ids) : [];
const longPressTimer = useRef<number | null>(null);
const didLongPress = useRef(false);
const handleClick = () => {
if (didLongPress.current) {
didLongPress.current = false;
return;
}
active ? message.unreact(id) : message.react(id);
};
const openModal = () => {
modalController.push({
type: "reaction_users",
emoji: id,
userIds,
});
};
const handleTouchStart = (e: TouchEvent) => {
e.preventDefault();
e.stopPropagation();
didLongPress.current = false;
longPressTimer.current = window.setTimeout(() => {
didLongPress.current = true;
openModal();
}, 500);
};
const handleTouchEnd = (e: TouchEvent) => {
e.stopPropagation();
if (longPressTimer.current) {
clearTimeout(longPressTimer.current);
longPressTimer.current = null;
// Short tap - trigger manually since preventDefault blocks click
if (!didLongPress.current) {
active ? message.unreact(id) : message.react(id);
}
}
didLongPress.current = false;
};
const openUserProfile = (userId: string) => {
modalController.push({ type: "user_profile", user_id: userId });
};
// Build tooltip content showing up to 10 users
const tooltipContent = (
<TooltipUserList>
{userIds.slice(0, 10).map((userId) => {
const user = client.users.get(userId);
return ( return (
<TooltipUserRow
key={userId}
onClick={() => openUserProfile(userId)}>
<UserIcon target={user} size={18} />
<Username user={user} />
</TooltipUserRow>
);
})}
{userIds.length > 10 && (
<TooltipMoreText>
and {userIds.length - 10} more...
</TooltipMoreText>
)}
</TooltipUserList>
);
const reactionElement = (
<Reaction <Reaction
active={active} active={active}
onClick={handleClick} onClick={() =>
onTouchStart={isTouchscreenDevice ? handleTouchStart : undefined} active ? message.unreact(id) : message.react(id)
onTouchEnd={isTouchscreenDevice ? handleTouchEnd : undefined} }>
onTouchCancel={isTouchscreenDevice ? handleTouchEnd : undefined}>
<RenderEmoji match={id} /> {user_ids?.size || 0} <RenderEmoji match={id} /> {user_ids?.size || 0}
</Reaction> </Reaction>
); );
// Desktop: show tooltip on hover
// Mobile: long-press opens modal, handled by touch events
if (!isTouchscreenDevice && userIds.length > 0) {
return (
<Tooltip content={tooltipContent} delay={300} interactive>
{reactionElement}
</Tooltip>
);
}
return reactionElement;
}), }),
[], [],
); );

View File

@@ -119,7 +119,7 @@ export default function UserBadges({ badges, uid }: Props) {
}} }}
onClick={() => { onClick={() => {
window.open( window.open(
"https://wiki.revolt.chat/notes/project/financial-support/", "https://ko-fi.com/stoatchat",
"_blank", "_blank",
); );
}} }}

View File

@@ -121,7 +121,7 @@ export default function MemberList({
Offline users have temporarily been disabled for Offline users have temporarily been disabled for
larger servers - see{" "} larger servers - see{" "}
<a <a
href="https://github.com/revoltchat/backend/issues/178" href="https://github.com/stoatchat/stoatchat/issues/178"
target="_blank" target="_blank"
rel="noreferrer"> rel="noreferrer">
issue #178 issue #178

View File

@@ -78,6 +78,7 @@ export type Theme = Overrides & {
css?: string; css?: string;
monospaceFont?: MonospaceFonts; monospaceFont?: MonospaceFonts;
"min-opacity"?: number; "min-opacity"?: number;
"logo-filter"?: string;
}; };
export type ComputedVariables = Theme & { export type ComputedVariables = Theme & {
@@ -288,6 +289,7 @@ export const PRESETS: Record<string, Theme> = {
"status-busy": "#F84848", "status-busy": "#F84848",
"status-streaming": "#977EFF", "status-streaming": "#977EFF",
"status-invisible": "#A5A5A5", "status-invisible": "#A5A5A5",
"logo-filter": "unset",
}, },
dark: { dark: {
accent: "#FD6671", accent: "#FD6671",
@@ -316,6 +318,7 @@ export const PRESETS: Record<string, Theme> = {
"status-busy": "#F84848", "status-busy": "#F84848",
"status-streaming": "#977EFF", "status-streaming": "#977EFF",
"status-invisible": "#A5A5A5", "status-invisible": "#A5A5A5",
"logo-filter": "invert(1)",
}, },
}; };

View File

@@ -28,7 +28,6 @@ type Transition =
| "SUCCESS" | "SUCCESS"
| "DISCONNECT" | "DISCONNECT"
| "RETRY" | "RETRY"
| "RETRY_FAILED"
| "LOGOUT" | "LOGOUT"
| "ONLINE" | "ONLINE"
| "OFFLINE"; | "OFFLINE";
@@ -41,7 +40,6 @@ export default class Session {
state: State = window.navigator.onLine ? "Ready" : "Offline"; state: State = window.navigator.onLine ? "Ready" : "Offline";
user_id: string | null = null; user_id: string | null = null;
client: Client | null = null; client: Client | null = null;
retryAttempts: number = 0;
/** /**
* Create a new Session * Create a new Session
@@ -91,11 +89,9 @@ export default class Session {
* Called when the client signals it has disconnected * Called when the client signals it has disconnected
*/ */
private onDropped() { private onDropped() {
if (this.state === "Connecting") { this.emit({
this.emit({ action: "RETRY_FAILED" }); action: "DISCONNECT",
} else { });
this.emit({ action: "DISCONNECT" });
}
} }
/** /**
@@ -215,7 +211,6 @@ export default class Session {
// Ready successfully received // Ready successfully received
case "SUCCESS": { case "SUCCESS": {
this.assert("Connecting"); this.assert("Connecting");
this.retryAttempts = 0;
this.state = "Online"; this.state = "Online";
break; break;
} }
@@ -244,18 +239,6 @@ export default class Session {
this.state = "Connecting"; this.state = "Connecting";
break; break;
} }
// Reconnect attempt failed, schedule another with backoff
case "RETRY_FAILED": {
this.assert("Connecting");
this.retryAttempts++;
const delay = Math.min(500 * Math.pow(2, this.retryAttempts), 16000);
setTimeout(() => {
if (this.state === "Connecting") {
this.client!.websocket.connect();
}
}, delay);
break;
}
// User instructed logout // User instructed logout
case "LOGOUT": { case "LOGOUT": {
this.assert("Connecting", "Online", "Disconnected"); this.assert("Connecting", "Online", "Disconnected");

View File

@@ -32,8 +32,6 @@ import CreateRole from "./components/CreateRole";
import CreateServer from "./components/CreateServer"; import CreateServer from "./components/CreateServer";
import CustomStatus from "./components/CustomStatus"; import CustomStatus from "./components/CustomStatus";
import DeleteMessage from "./components/DeleteMessage"; import DeleteMessage from "./components/DeleteMessage";
import ReactMessage from "./components/ReactMessage";
import ReactionUsers from "./components/ReactionUsers";
import Error from "./components/Error"; import Error from "./components/Error";
import ImageViewer from "./components/ImageViewer"; import ImageViewer from "./components/ImageViewer";
import KickMember from "./components/KickMember"; import KickMember from "./components/KickMember";
@@ -277,8 +275,6 @@ export const modalController = new ModalControllerExtended({
create_bot: CreateBot, create_bot: CreateBot,
custom_status: CustomStatus, custom_status: CustomStatus,
delete_message: DeleteMessage, delete_message: DeleteMessage,
react_message: ReactMessage,
reaction_users: ReactionUsers,
error: Error, error: Error,
image_viewer: ImageViewer, image_viewer: ImageViewer,
kick_member: KickMember, kick_member: KickMember,

View File

@@ -1,46 +0,0 @@
import styled from "styled-components";
import { Modal } from "@revoltchat/ui";
import { ModalProps } from "../types"
import { Message } from "revolt.js";
import { emojiDictionary } from "../../../assets/emojis"
import { HackAlertThisFileWillBeReplaced } from "../../../components/common/messaging/MessageBox"
const PickerContainer = styled.div`
max-height: 420px;
max-width: 370px;
overflow: hidden;
> div {
position: unset;
}
`
export default function ReactMessage({
target: message,
onClose,
...props
}: ModalProps<"react_message">) {
return (
<Modal
{...props}
padding={false}
maxWidth="370px"
>
<PickerContainer>
<HackAlertThisFileWillBeReplaced
onSelect={(emoji) =>{
message.react(
emojiDictionary[
emoji as keyof typeof emojiDictionary
] ?? emoji,
);
onClose();
}}
onClose={onClose}
/>
</PickerContainer>
</Modal>
)
}

View File

@@ -1,92 +0,0 @@
import styled from "styled-components";
import { Modal } from "@revoltchat/ui";
import UserIcon from "../../../components/common/user/UserIcon";
import { Username } from "../../../components/common/user/UserShort";
import { RenderEmoji } from "../../../components/markdown/plugins/emoji";
import { useClient } from "../../client/ClientController";
import { modalController } from "../ModalController";
import { ModalProps } from "../types";
const List = styled.div`
max-width: 100%;
max-height: 360px;
overflow-y: auto;
`;
const UserRow = styled.div`
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
cursor: pointer;
border-radius: var(--border-radius);
&:hover {
background: var(--secondary-background);
}
`;
const Header = styled.div`
display: flex;
align-items: center;
gap: 8px;
font-size: 1.1em;
img {
width: 1.5em;
height: 1.5em;
object-fit: contain;
}
`;
export default function ReactionUsers({
emoji,
userIds,
onClose,
...props
}: ModalProps<"reaction_users">) {
const client = useClient();
const openProfile = (userId: string) => {
onClose();
modalController.push({ type: "user_profile", user_id: userId });
};
// Modal must be nonDismissable, it conflicts with the message context menu otherwise.
return (
<Modal
{...props}
nonDismissable
title={
<Header>
<RenderEmoji match={emoji} />
<span>{userIds.length}</span>
</Header>
}
actions={[
{
onClick: () => {
onClose();
return true;
},
children: "Close",
},
]}>
<List>
{userIds.map((userId) => {
const user = client.users.get(userId);
return (
<UserRow
key={userId}
onClick={() => openProfile(userId)}>
<UserIcon target={user} size={32} />
<Username user={user} />
</UserRow>
);
})}
</List>
</Modal>
);
}

View File

@@ -153,15 +153,6 @@ export type Modal = {
type: "delete_message"; type: "delete_message";
target: Message; target: Message;
} }
| {
type: "react_message",
target: Message;
}
| {
type: "reaction_users";
emoji: string;
userIds: string[];
}
| { | {
type: "kick_member"; type: "kick_member";
member: Member; member: Member;

View File

@@ -65,7 +65,6 @@ type Action =
| { action: "quote_message"; content: string } | { action: "quote_message"; content: string }
| { action: "edit_message"; id: string } | { action: "edit_message"; id: string }
| { action: "delete_message"; target: Message } | { action: "delete_message"; target: Message }
| { action: "react_message"; target: Message }
| { action: "open_file"; attachment: API.File } | { action: "open_file"; attachment: API.File }
| { action: "save_file"; attachment: API.File } | { action: "save_file"; attachment: API.File }
| { action: "copy_file_link"; attachment: API.File } | { action: "copy_file_link"; attachment: API.File }
@@ -403,13 +402,6 @@ export default function ContextMenus() {
}); });
break; break;
case "react_message":
modalController.push({
type: "react_message",
target: data.target,
});
break;
case "leave_group": case "leave_group":
case "close_dm": case "close_dm":
case "delete_channel": case "delete_channel":
@@ -516,8 +508,6 @@ export default function ContextMenus() {
"Open in Admin Panel" "Open in Admin Panel"
) : locale === "admin_system" ? ( ) : locale === "admin_system" ? (
"Open User in Admin Panel" "Open User in Admin Panel"
) : locale === "react_message" ? (
"React"
) : ( ) : (
<Text <Text
id={`app.context_menu.${ id={`app.context_menu.${
@@ -843,16 +833,6 @@ export default function ContextMenus() {
}); });
} }
if (message.channel?.havePermission("React")) {
generateAction(
{
action: "react_message",
target: message,
},
"react_message",
);
}
if (message.author_id !== userId) { if (message.author_id !== userId) {
generateAction( generateAction(
{ {

View File

@@ -107,7 +107,10 @@ export default observer(() => {
<h3> <h3>
<Text id="app.special.modals.onboarding.welcome" /> <Text id="app.special.modals.onboarding.welcome" />
<br /> <br />
<img src={wideSVG} /> <img
src={wideSVG}
style={{ filter: "var(--logo-filter)" }}
/>
</h3> </h3>
<div className={styles.actions}> <div className={styles.actions}>
<a <a
@@ -164,18 +167,24 @@ export default observer(() => {
</Link> </Link>
)} )}
<Link to="/settings/feedback"> <a
href={
typeof window.native === "object"
? "https://stoat.chat/download"
: "https://stoat.chat/app"
}
target="_blank">
<CategoryButton <CategoryButton
action="chevron" action="chevron"
icon={<Megaphone size={32} />} icon={<Megaphone size={32} />}
description={ description="Revolt is now Stoat and has a brand new app.">
<Text id="app.home.feedback_desc" /> {typeof window.native === "object"
}> ? "Download new app"
<Text id="app.home.feedback" /> : "Switch to new app"}
</CategoryButton> </CategoryButton>
</Link> </a>
<a <a
href="https://wiki.revolt.chat/notes/project/financial-support/" href="https://ko-fi.com/stoatchat"
target="_blank" target="_blank"
rel="noreferrer"> rel="noreferrer">
<CategoryButton <CategoryButton

View File

@@ -41,7 +41,9 @@ export default observer(() => {
href={action.href} href={action.href}
target="_blank" target="_blank"
rel="noreferrer"> rel="noreferrer">
<div className="button">{action.text}</div>{" "} <div className="button">
{action.text}
</div>{" "}
</a> </a>
) : null, ) : null,
)} )}
@@ -59,7 +61,11 @@ export default observer(() => {
<div className={styles.nav}> <div className={styles.nav}>
<a className={styles.logo}> <a className={styles.logo}>
{!("native" in window) && ( {!("native" in window) && (
<img src={wideSVG} draggable={false} /> <img
src={wideSVG}
style={{ filter: "var(--logo-filter)" }}
draggable={false}
/>
)} )}
</a> </a>
<LocaleSelector /> <LocaleSelector />

View File

@@ -141,6 +141,10 @@
color: goldenrod !important; color: goldenrod !important;
} }
.newApp {
color: green !important;
}
.logOut, .logOut,
.deleteServer { .deleteServer {
color: var(--error) !important; color: var(--error) !important;

View File

@@ -207,11 +207,6 @@ export default observer(() => {
icon: <Bot size={20} />, icon: <Bot size={20} />,
title: <Text id="app.settings.pages.bots.title" />, title: <Text id="app.settings.pages.bots.title" />,
}, },
{
id: "feedback",
icon: <Megaphone size={20} />,
title: <Text id="app.settings.pages.feedback.title" />,
},
]} ]}
children={ children={
<Switch> <Switch>
@@ -263,16 +258,23 @@ export default observer(() => {
category="pages" category="pages"
custom={ custom={
<> <>
<ButtonItem {typeof window.native === "object" ? (
compact
onClick={() =>
modalController.push({ type: "changelog" })
}>
<ListUl size={20} />
<Text id="app.special.modals.changelogs.title" />
</ButtonItem>
<a <a
href="https://github.com/revoltchat" className={styles.newApp}
href="https://stoat.chat/download"
target="_blank">
<ButtonItem compact>Download new app</ButtonItem>
</a>
) : (
<a
className={styles.newApp}
href="https://stoat.chat/app"
target="_blank">
<ButtonItem compact>Switch to new app</ButtonItem>
</a>
)}
<a
href="https://github.com/stoatchat"
target="_blank" target="_blank"
rel="noreferrer"> rel="noreferrer">
<ButtonItem compact> <ButtonItem compact>
@@ -281,7 +283,7 @@ export default observer(() => {
</ButtonItem> </ButtonItem>
</a> </a>
<a <a
href="https://wiki.revolt.chat/notes/project/financial-support/" href="https://ko-fi.com/stoatchat"
target="_blank" target="_blank"
rel="noreferrer"> rel="noreferrer">
<ButtonItem className={styles.donate} compact> <ButtonItem className={styles.donate} compact>
@@ -309,7 +311,7 @@ export default observer(() => {
<a <a
href={ href={
GIT_BRANCH !== "DETACHED" GIT_BRANCH !== "DETACHED"
? `https://github.com/revoltchat/revite/tree/${GIT_BRANCH}` ? `https://github.com/stoatchat/for-legacy-web/tree/${GIT_BRANCH}`
: undefined : undefined
} }
target="_blank" target="_blank"

View File

@@ -33,9 +33,8 @@ export function Audio() {
const askOrGetPermission = async () => { const askOrGetPermission = async () => {
try { try {
const result = await navigator.mediaDevices.getUserMedia( const result =
constraints, await navigator.mediaDevices.getUserMedia(constraints);
);
setMediaStream(result); setMediaStream(result);
} catch (err) { } catch (err) {
@@ -96,28 +95,14 @@ export function Audio() {
<div className={styles.audio}> <div className={styles.audio}>
<Tip palette="warning"> <Tip palette="warning">
<span> <span>
We are currently{" "} Legacy voice is no longer supported,{" "}
<a <a
style={{ color: "inherit", fontWeight: "600" }} style={{ color: "inherit", fontWeight: "600" }}
href="https://github.com/revoltchat/frontend/issues/14" href="https://stoat.chat/app"
target="_blank" target="_blank">
rel="noreferrer"> use the new app
rebuilding the client </a>
</a>{" "} .
and{" "}
<a
style={{ color: "inherit", fontWeight: "600" }}
href="https://trello.com/c/Ay6KdiOV/1-voice-overhaul-and-video-calling"
target="_blank"
rel="noreferrer">
the voice server
</a>{" "}
from scratch.
<br />
<br />
The old voice should work in most cases, but it may
inexplicably not connect in some scenarios and / or
exhibit weird behaviour.
</span> </span>
</Tip> </Tip>

View File

@@ -1,7 +1,7 @@
/* eslint-disable */ /* eslint-disable */
// Strings needs to be explictly stated here as they can cause type issues elsewhere. // Strings needs to be explictly stated here as they can cause type issues elsewhere.
export const REPO_URL: string = "https://github.com/revoltchat/revite/commit"; export const REPO_URL: string = "https://github.com/stoatchat/for-legacy-web/commit";
export const GIT_REVISION: string = "__GIT_REVISION__"; export const GIT_REVISION: string = "__GIT_REVISION__";
export const GIT_BRANCH: string = "__GIT_BRANCH__"; export const GIT_BRANCH: string = "__GIT_BRANCH__";