Add Radial Status: colored ring around the avatar instead of the presence dot
This commit is contained in:
parent
36d7d2a621
commit
511dff142c
7 changed files with 206 additions and 2 deletions
|
|
@ -22,6 +22,7 @@ To install: open Discord with Revenge installed, go to **Settings → Plugins**,
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| Staff Tags | Rebuilt | OWNER/ADMIN/STAFF/MOD-style tags next to members, computed from real server permissions. Per-tag text, colors, gradients, and visibility. | `https://rp.jarviscli.dev/staff-tags/install/` |
|
| Staff Tags | Rebuilt | OWNER/ADMIN/STAFF/MOD-style tags next to members, computed from real server permissions. Per-tag text, colors, gradients, and visibility. | `https://rp.jarviscli.dev/staff-tags/install/` |
|
||||||
| Custom User Tags | New | Create your own tags (text + color) and assign them to specific users, independent of server permissions. Long-press a name to manage one, or add by user ID in settings. | `https://rp.jarviscli.dev/custom-user-tags/install/` |
|
| Custom User Tags | New | Create your own tags (text + color) and assign them to specific users, independent of server permissions. Long-press a name to manage one, or add by user ID in settings. | `https://rp.jarviscli.dev/custom-user-tags/install/` |
|
||||||
|
| Radial Status | Revived | Replaces the small presence dot on an avatar with a colored ring instead - one color per status, adjustable size and thickness. Fork of [AngelW0lf's Radial Status](https://github.com/Angelix1/MP/tree/master/angel/radialstatus), BSD-3-Clause - dropped a phone-home mechanism that could silently uninstall the plugin for specific user IDs, and replaced non-functional size/thickness controls with real ones. | `https://rp.jarviscli.dev/radial-status/install/` |
|
||||||
| RoleColorEverywhere | Revived | A member's top role color shown in mentions, the typing indicator, voice channel names, member list role headers, and optionally message text. | `https://rp.jarviscli.dev/role-color-everywhere/install/` |
|
| RoleColorEverywhere | Revived | A member's top role color shown in mentions, the typing indicator, voice channel names, member list role headers, and optionally message text. | `https://rp.jarviscli.dev/role-color-everywhere/install/` |
|
||||||
| PronounDB | Revived | Shows a user's pronouns in their profile, if they've set them at pronoundb.org (nothing to configure in the plugin itself). | `https://rp.jarviscli.dev/pronoun-db/install/` |
|
| PronounDB | Revived | Shows a user's pronouns in their profile, if they've set them at pronoundb.org (nothing to configure in the plugin itself). | `https://rp.jarviscli.dev/pronoun-db/install/` |
|
||||||
| FakeProfileThemesAndEffects | Revived | Profile theming and profile effects without Nitro. Fork of [shipwr3ckd's FPTE](https://github.com/shipwr3ckd/FPTE) - fixed a crash on every profile-editor open, confirmed via on-device diagnostics. | `https://rp.jarviscli.dev/fake-profile-themes-and-effects/install/` |
|
| FakeProfileThemesAndEffects | Revived | Profile theming and profile effects without Nitro. Fork of [shipwr3ckd's FPTE](https://github.com/shipwr3ckd/FPTE) - fixed a crash on every profile-editor open, confirmed via on-device diagnostics. | `https://rp.jarviscli.dev/fake-profile-themes-and-effects/install/` |
|
||||||
|
|
@ -52,8 +53,8 @@ To install: open Discord with Revenge installed, go to **Settings → Plugins**,
|
||||||
it didn't exist anywhere in the Revenge/Vendetta plugin ecosystem before.
|
it didn't exist anywhere in the Revenge/Vendetta plugin ecosystem before.
|
||||||
|
|
||||||
**Licensing note:** everything in this repo is original work except `plugins/view-raw` (GPLv3, forked from
|
**Licensing note:** everything in this repo is original work except `plugins/view-raw` (GPLv3, forked from
|
||||||
bwlok's ViewRaw) and `plugins/rose-utils` (CC-BY 4.0, forked from nexpid's NexxUtils) - the rest of the repo
|
bwlok's ViewRaw), `plugins/rose-utils` (CC-BY 4.0, forked from nexpid's NexxUtils), and `plugins/radial-status`
|
||||||
isn't under either of those licenses.
|
(BSD-3-Clause, forked from AngelW0lf's Radial Status) - the rest of the repo isn't under any of those licenses.
|
||||||
|
|
||||||
## Why some of these broke before, and what's different now
|
## Why some of these broke before, and what's different now
|
||||||
|
|
||||||
|
|
|
||||||
28
plugins/radial-status/LICENSE
Normal file
28
plugins/radial-status/LICENSE
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2024, Angel
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
9
plugins/radial-status/manifest.json
Normal file
9
plugins/radial-status/manifest.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"name": "Radial Status",
|
||||||
|
"description": "Replaces the small presence dot on avatars with a colored ring instead - one color per status (online/idle/DND), fully configurable, plus size and thickness controls.",
|
||||||
|
"authors": [
|
||||||
|
{ "name": "everestmcarthur" },
|
||||||
|
{ "name": "AngelW0lf" }
|
||||||
|
],
|
||||||
|
"main": "src/index.ts"
|
||||||
|
}
|
||||||
25
plugins/radial-status/src/index.ts
Normal file
25
plugins/radial-status/src/index.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { logger } from "@vendetta";
|
||||||
|
import { storage } from "@vendetta/plugin";
|
||||||
|
import { applyPatches } from "@shared/lib/patcher";
|
||||||
|
import patchRing from "./patches/ringPatch";
|
||||||
|
import Settings from "./ui/Settings";
|
||||||
|
|
||||||
|
let unpatchAll: () => void = () => {};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
onLoad: () => {
|
||||||
|
storage.colors ??= {
|
||||||
|
online: "#23A55A",
|
||||||
|
idle: "#F0B232",
|
||||||
|
dnd: "#F23F42"
|
||||||
|
};
|
||||||
|
storage.ringMult ??= 1.3;
|
||||||
|
storage.ringThickness ??= 2.5;
|
||||||
|
|
||||||
|
unpatchAll = applyPatches("Radial Status", logger, {
|
||||||
|
ring: patchRing
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onUnload: () => unpatchAll(),
|
||||||
|
settings: Settings
|
||||||
|
};
|
||||||
64
plugins/radial-status/src/patches/ringPatch.ts
Normal file
64
plugins/radial-status/src/patches/ringPatch.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import { General } from "@vendetta/ui/components";
|
||||||
|
import { before } from "@vendetta/patcher";
|
||||||
|
import { storage } from "@vendetta/plugin";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forked from AngelW0lf's Radial Status (BSD-3-Clause, see LICENSE). The upstream original matched
|
||||||
|
* one hardcoded wrapper size (exactly 32x32, borderRadius 16) - generalized here to any square,
|
||||||
|
* fully-circular wrapper (borderRadius === width / 2) instead, since avatar sizes differ across
|
||||||
|
* member lists, DM lists, and profile popouts, and the two structural checks right below (a `user`
|
||||||
|
* prop with a string id, a `status` prop) already confirm this is the right element regardless of
|
||||||
|
* its exact pixel size.
|
||||||
|
*
|
||||||
|
* Not independently re-verified against a live decompiled build the way the rest of this repo's
|
||||||
|
* fixes are - this is a straight-line adaptation of upstream's own (previously working, per its
|
||||||
|
* changelog) logic. Every assumption below is guarded, so a shape mismatch just means the ring
|
||||||
|
* doesn't draw for that avatar, not a crash.
|
||||||
|
*/
|
||||||
|
export default function patchRing(): () => void {
|
||||||
|
if (!General?.View) return () => {};
|
||||||
|
|
||||||
|
return before("render", General.View, (args: any[]) => {
|
||||||
|
try {
|
||||||
|
const [wrapper] = args;
|
||||||
|
if (!wrapper || !Array.isArray(wrapper.style)) return;
|
||||||
|
|
||||||
|
const circleIdx = wrapper.style.findIndex(
|
||||||
|
(s: any) => s && typeof s.width === "number" && s.width === s.height && s.borderRadius === s.width / 2
|
||||||
|
);
|
||||||
|
if (circleIdx === -1) return;
|
||||||
|
|
||||||
|
const userProps = wrapper.children?.[1]?.props;
|
||||||
|
const presenceProps = wrapper.children?.[3]?.props;
|
||||||
|
if (!userProps || typeof userProps.user?.id !== "string") return;
|
||||||
|
if (!presenceProps || typeof presenceProps.status !== "string") return;
|
||||||
|
|
||||||
|
const colors = storage.colors ?? {};
|
||||||
|
const color = colors[presenceProps.status as string];
|
||||||
|
if (!color) return; // no color configured for this status - leave the native dot alone
|
||||||
|
|
||||||
|
const baseSize = wrapper.style[circleIdx].width;
|
||||||
|
const mult = storage.ringMult ?? 1.3;
|
||||||
|
const thickness = storage.ringThickness ?? 2.5;
|
||||||
|
|
||||||
|
presenceProps.size = 0;
|
||||||
|
presenceProps.isMobileOnline = false;
|
||||||
|
if (presenceProps.style) presenceProps.style.display = "none";
|
||||||
|
if (userProps.cutout?.nativeCutouts?.[0]) userProps.cutout.nativeCutouts[0].size = 0;
|
||||||
|
|
||||||
|
wrapper.style[circleIdx] = {
|
||||||
|
width: baseSize * mult,
|
||||||
|
height: baseSize * mult,
|
||||||
|
borderRadius: (baseSize / 2) * mult,
|
||||||
|
overflow: "hidden"
|
||||||
|
};
|
||||||
|
wrapper.style.push({
|
||||||
|
borderWidth: thickness * mult,
|
||||||
|
borderColor: color,
|
||||||
|
borderStyle: "solid"
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
// Leave the native presence dot alone.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
61
plugins/radial-status/src/ui/Settings.tsx
Normal file
61
plugins/radial-status/src/ui/Settings.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { React } from "@vendetta/metro/common";
|
||||||
|
import { useProxy } from "@vendetta/storage";
|
||||||
|
import { storage } from "@vendetta/plugin";
|
||||||
|
import { Forms } from "@vendetta/ui/components";
|
||||||
|
import SettingsScaffold from "@shared/ui/SettingsScaffold";
|
||||||
|
import ColorInput from "@shared/ui/ColorInput";
|
||||||
|
import NoteBox from "@shared/ui/NoteBox";
|
||||||
|
|
||||||
|
const { FormSection, FormInput } = Forms;
|
||||||
|
|
||||||
|
const STATUSES: { key: string; label: string; defaultColor: string }[] = [
|
||||||
|
{ key: "online", label: "Online", defaultColor: "#23A55A" },
|
||||||
|
{ key: "idle", label: "Idle", defaultColor: "#F0B232" },
|
||||||
|
{ key: "dnd", label: "Do Not Disturb", defaultColor: "#F23F42" }
|
||||||
|
];
|
||||||
|
|
||||||
|
function numberInput(title: string, key: "ringMult" | "ringThickness", placeholder: string) {
|
||||||
|
return (
|
||||||
|
<FormInput
|
||||||
|
title={title}
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={String(storage[key] ?? placeholder)}
|
||||||
|
onChange={(v: string) => {
|
||||||
|
const n = parseFloat(v);
|
||||||
|
if (!Number.isNaN(n) && n > 0) storage[key] = n;
|
||||||
|
}}
|
||||||
|
keyboardType="numeric"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Settings() {
|
||||||
|
useProxy(storage);
|
||||||
|
storage.colors ??= {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsScaffold>
|
||||||
|
<NoteBox>
|
||||||
|
Replaces the small presence dot on an avatar with a colored ring instead, per status
|
||||||
|
below. Leave a status blank to keep showing the normal dot for it.
|
||||||
|
</NoteBox>
|
||||||
|
<FormSection title="Ring colors">
|
||||||
|
{STATUSES.map(({ key, label, defaultColor }) => (
|
||||||
|
<ColorInput
|
||||||
|
key={key}
|
||||||
|
title={label}
|
||||||
|
value={storage.colors[key]}
|
||||||
|
placeholder={defaultColor}
|
||||||
|
onChange={(v: string) => {
|
||||||
|
storage.colors = { ...storage.colors, [key]: v };
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</FormSection>
|
||||||
|
<FormSection title="Ring size">
|
||||||
|
{numberInput("Size multiplier", "ringMult", "1.3")}
|
||||||
|
{numberInput("Ring thickness", "ringThickness", "2.5")}
|
||||||
|
</FormSection>
|
||||||
|
</SettingsScaffold>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,20 @@
|
||||||
{
|
{
|
||||||
|
"radial-status": {
|
||||||
|
"category": "Moderation & Appearance",
|
||||||
|
"status": "revived",
|
||||||
|
"accent": "#23A55A",
|
||||||
|
"tagline": "A colored ring around the avatar instead of a presence dot - similar to Platform Indicator, but for status.",
|
||||||
|
"note": "Fork of AngelW0lf's Radial Status (BSD-3-Clause, see LICENSE). Upstream had a phone-home mechanism - it fetched a remote blacklist on every load and could silently uninstall itself for specific user IDs the original author chose - dropped entirely here, no network requests at all. Upstream also matched one hardcoded wrapper size (32x32 only); generalized here to any fully-circular avatar wrapper, since avatar sizes differ across member lists, DM lists, and profile popouts. Upstream's settings screen had commented-out, non-functional size/thickness inputs and an unfinished joke placeholder button - both removed, with real working size and thickness controls in their place.",
|
||||||
|
"howItWorks": "Hides the small colored dot Discord shows on an avatar for online/idle/DND, and draws a colored ring around the whole avatar instead - similar in spirit to a platform indicator, but for presence status. Each status's color is configurable, along with the ring's size multiplier and thickness. A status left with no color configured just keeps showing the normal dot.",
|
||||||
|
"features": [
|
||||||
|
"Colored ring around the avatar instead of the small presence dot",
|
||||||
|
"Independent color per status: online, idle, do not disturb",
|
||||||
|
"Adjustable ring size multiplier and thickness",
|
||||||
|
"No network requests - upstream's remote blacklist/kill-switch mechanism was removed entirely"
|
||||||
|
],
|
||||||
|
"commands": [],
|
||||||
|
"limitations": "The wrapper-shape match this depends on hasn't been independently re-verified against a live decompiled build the way the rest of this repo's fixes are - it's a direct adaptation of upstream's own previously-working logic, generalized and guarded so a mismatch just means no ring is drawn rather than a crash."
|
||||||
|
},
|
||||||
"custom-user-tags": {
|
"custom-user-tags": {
|
||||||
"category": "Moderation & Appearance",
|
"category": "Moderation & Appearance",
|
||||||
"status": "new",
|
"status": "new",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue