Skip to content

Commit 9d6fdf8

Browse files
authored
feat: Role gradient colours (#10986)
* feat: backport role gradient colours * chore: add deprecations
1 parent cafe58b commit 9d6fdf8

File tree

5 files changed

+173
-12
lines changed

5 files changed

+173
-12
lines changed

packages/discord.js/src/managers/GuildMemberRoleManager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class GuildMemberRoleManager extends DataManager {
6565
* @readonly
6666
*/
6767
get color() {
68-
const coloredRoles = this.cache.filter(role => role.color);
68+
const coloredRoles = this.cache.filter(role => role.colors.primaryColor);
6969
if (!coloredRoles.size) return null;
7070
return coloredRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev));
7171
}

packages/discord.js/src/managers/RoleManager.js

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const PermissionsBitField = require('../util/PermissionsBitField');
1212
const { setPosition, resolveColor } = require('../util/Util');
1313

1414
let cacheWarningEmitted = false;
15+
let deprecationEmittedForCreate = false;
16+
let deprecationEmittedForEdit = false;
1517

1618
/**
1719
* Manages API methods for roles and stores their cache.
@@ -112,11 +114,24 @@ class RoleManager extends CachedManager {
112114
* @returns {?Snowflake}
113115
*/
114116

117+
/**
118+
* @typedef {Object} RoleColorsResolvable
119+
* @property {ColorResolvable} primaryColor The primary color of the role
120+
* @property {ColorResolvable} [secondaryColor] The secondary color of the role.
121+
* This will make the role a gradient between the other provided colors
122+
* @property {ColorResolvable} [tertiaryColor] The tertiary color of the role.
123+
* When sending `tertiaryColor` the API enforces the role color to be a holographic style
124+
* with values of `primaryColor = 11127295`, `secondaryColor = 16759788`, and `tertiaryColor = 16761760`.
125+
* These values are available as a constant: `Constants.HolographicStyle`
126+
*/
127+
115128
/**
116129
* Options used to create a new role.
117130
* @typedef {Object} RoleCreateOptions
118131
* @property {string} [name] The name of the new role
119132
* @property {ColorResolvable} [color] The data to create the role with
133+
* <warn>This property is deprecated. Use `colors` instead.</warn>
134+
* @property {RoleColorsResolvable} [colors] The colors to create the role with
120135
* @property {boolean} [hoist] Whether or not the new role should be hoisted
121136
* @property {PermissionResolvable} [permissions] The permissions for the new role
122137
* @property {number} [position] The position of the new role
@@ -142,26 +157,61 @@ class RoleManager extends CachedManager {
142157
* // Create a new role with data and a reason
143158
* guild.roles.create({
144159
* name: 'Super Cool Blue People',
145-
* color: Colors.Blue,
146160
* reason: 'we needed a role for Super Cool People',
161+
* colors: {
162+
* primaryColor: Colors.Blue,
163+
* },
164+
* })
165+
* .then(console.log)
166+
* .catch(console.error);
167+
* @example
168+
* // Create a role with holographic colors
169+
* guild.roles.create({
170+
* name: 'Holographic Role',
171+
* reason: 'Creating a role with holographic effect',
172+
* colors: {
173+
* primaryColor: Constants.HolographicStyle.Primary,
174+
* secondaryColor: Constants.HolographicStyle.Secondary,
175+
* tertiaryColor: Constants.HolographicStyle.Tertiary,
176+
* },
147177
* })
148178
* .then(console.log)
149179
* .catch(console.error);
150180
*/
151181
async create(options = {}) {
152-
let { name, color, hoist, permissions, position, mentionable, reason, icon, unicodeEmoji } = options;
153-
color &&= resolveColor(color);
182+
let { permissions, icon } = options;
183+
const { name, color, hoist, position, mentionable, reason, unicodeEmoji } = options;
154184
if (permissions !== undefined) permissions = new PermissionsBitField(permissions);
155185
if (icon) {
156186
const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL();
157187
icon = guildEmojiURL ? await resolveImage(guildEmojiURL) : await resolveImage(icon);
158188
if (typeof icon !== 'string') icon = undefined;
159189
}
160190

191+
let colors = options.colors && {
192+
primary_color: resolveColor(options.colors.primaryColor),
193+
secondary_color: options.colors.secondaryColor && resolveColor(options.colors.secondaryColor),
194+
tertiary_color: options.colors.tertiaryColor && resolveColor(options.colors.tertiaryColor),
195+
};
196+
197+
if (color !== undefined) {
198+
if (!deprecationEmittedForCreate) {
199+
process.emitWarning(`Passing "color" to RoleManager#create() is deprecated. Use "colors" instead.`);
200+
}
201+
202+
deprecationEmittedForCreate = true;
203+
204+
colors = {
205+
primary_color: resolveColor(color),
206+
secondary_color: null,
207+
tertiary_color: null,
208+
};
209+
}
210+
161211
const data = await this.client.rest.post(Routes.guildRoles(this.guild.id), {
162212
body: {
163213
name,
164-
color,
214+
colors,
165215
hoist,
166216
permissions,
167217
mentionable,
@@ -210,9 +260,29 @@ class RoleManager extends CachedManager {
210260
if (typeof icon !== 'string') icon = undefined;
211261
}
212262

263+
let colors = options.colors && {
264+
primary_color: resolveColor(options.colors.primaryColor),
265+
secondary_color: options.colors.secondaryColor && resolveColor(options.colors.secondaryColor),
266+
tertiary_color: options.colors.tertiaryColor && resolveColor(options.colors.tertiaryColor),
267+
};
268+
269+
if (options.color !== undefined) {
270+
if (!deprecationEmittedForEdit) {
271+
process.emitWarning(`Passing "color" to RoleManager#edit() is deprecated. Use "colors" instead.`);
272+
}
273+
274+
deprecationEmittedForEdit = true;
275+
276+
colors = {
277+
primary_color: resolveColor(options.color),
278+
secondary_color: null,
279+
tertiary_color: null,
280+
};
281+
}
282+
213283
const body = {
214284
name: options.name,
215-
color: options.color === undefined ? undefined : resolveColor(options.color),
285+
colors,
216286
hoist: options.hoist,
217287
permissions: options.permissions === undefined ? undefined : new PermissionsBitField(options.permissions),
218288
mentionable: options.mentionable,

packages/discord.js/src/structures/Role.js

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,37 @@ class Role extends Base {
5454
if ('color' in data) {
5555
/**
5656
* The base 10 color of the role
57+
*
5758
* @type {number}
59+
* @deprecated Use {@link Role#colors} instead.
5860
*/
5961
this.color = data.color;
6062
}
6163

64+
/**
65+
* @typedef {Object} RoleColors
66+
* @property {number} primaryColor The primary color of the role
67+
* @property {?number} secondaryColor The secondary color of the role.
68+
* This will make the role a gradient between the other provided colors
69+
* @property {?number} tertiaryColor The tertiary color of the role.
70+
* When sending `tertiaryColor` the API enforces the role color to be a holographic style
71+
* with values of `primaryColor = 11127295`, `secondaryColor = 16759788`, and `tertiaryColor = 16761760`.
72+
* These values are available as a constant: `Constants.HolographicStyle`
73+
*/
74+
75+
if ('colors' in data) {
76+
/**
77+
* The colors of the role
78+
*
79+
* @type {RoleColors}
80+
*/
81+
this.colors = {
82+
primaryColor: data.colors.primary_color,
83+
secondaryColor: data.colors.secondary_color,
84+
tertiaryColor: data.colors.tertiary_color,
85+
};
86+
}
87+
6288
if ('hoist' in data) {
6389
/**
6490
* If true, users that are part of this role will appear in a separate category in the users list
@@ -231,6 +257,8 @@ class Role extends Base {
231257
* @typedef {Object} RoleData
232258
* @property {string} [name] The name of the role
233259
* @property {ColorResolvable} [color] The color of the role, either a hex string or a base 10 number
260+
* <warn>This property is deprecated. Use `colors` instead.</warn>
261+
* @property {RoleColorsResolvable} [colors] The colors of the role
234262
* @property {boolean} [hoist] Whether or not the role should be hoisted
235263
* @property {number} [position] The position of the role
236264
* @property {PermissionResolvable} [permissions] The permissions of the role
@@ -286,17 +314,39 @@ class Role extends Base {
286314

287315
/**
288316
* Sets a new color for the role.
317+
*
289318
* @param {ColorResolvable} color The color of the role
290319
* @param {string} [reason] Reason for changing the role's color
291320
* @returns {Promise<Role>}
321+
* @deprecated Use {@link Role#setColors} instead.
322+
*/
323+
async setColor(color, reason) {
324+
return this.edit({ color, reason });
325+
}
326+
327+
/**
328+
* Sets new colors for the role.
329+
*
330+
* @param {RoleColorsResolvable} colors The colors of the role
331+
* @param {string} [reason] Reason for changing the role's colors
332+
* @returns {Promise<Role>}
333+
* @example
334+
* // Set the colors of a role
335+
* role.setColors({ primaryColor: '#FF0000', secondaryColor: '#00FF00', tertiaryColor: '#0000FF' })
336+
* .then(updated => console.log(`Set colors of role to ${updated.colors}`))
337+
* .catch(console.error);
292338
* @example
293-
* // Set the color of a role
294-
* role.setColor('#FF0000')
295-
* .then(updated => console.log(`Set color of role to ${updated.color}`))
339+
* // Set holographic colors using constants
340+
* role.setColors({
341+
* primaryColor: Constants.HolographicStyle.Primary,
342+
* secondaryColor: Constants.HolographicStyle.Secondary,
343+
* tertiaryColor: Constants.HolographicStyle.Tertiary,
344+
* })
345+
* .then(updated => console.log(`Set holographic colors for role ${updated.name}`))
296346
* .catch(console.error);
297347
*/
298-
setColor(color, reason) {
299-
return this.edit({ color, reason });
348+
async setColors(colors, reason) {
349+
return this.edit({ colors, reason });
300350
}
301351

302352
/**
@@ -434,7 +484,9 @@ class Role extends Base {
434484
role &&
435485
this.id === role.id &&
436486
this.name === role.name &&
437-
this.color === role.color &&
487+
this.colors.primaryColor === role.colors.primaryColor &&
488+
this.colors.secondaryColor === role.colors.secondaryColor &&
489+
this.colors.tertiaryColor === role.colors.tertiaryColor &&
438490
this.hoist === role.hoist &&
439491
this.position === role.position &&
440492
this.permissions.bitfield === role.permissions.bitfield &&

packages/discord.js/src/util/Constants.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,21 @@ exports.StickerFormatExtensionMap = {
254254
[StickerFormatType.GIF]: ImageFormat.GIF,
255255
};
256256

257+
/**
258+
* Holographic color values for role styling.
259+
* When using `tertiaryColor`, the API enforces these specific values for holographic effect.
260+
*
261+
* @typedef {Object} HolographicStyle
262+
* @property {number} Primary 11127295 (0xA9FFFF)
263+
* @property {number} Secondary 16759788 (0xFFCCCC)
264+
* @property {number} Tertiary 16761760 (0xFFE0A0)
265+
*/
266+
exports.HolographicStyle = {
267+
Primary: 11_127_295,
268+
Secondary: 16_759_788,
269+
Tertiary: 16_761_760,
270+
};
271+
257272
/**
258273
* @typedef {Object} Constants Constants that can be used in an enum or object-like way.
259274
* @property {number} MaxBulkDeletableMessageAge Max bulk deletable message age
@@ -264,4 +279,5 @@ exports.StickerFormatExtensionMap = {
264279
* @property {VoiceBasedChannelTypes} VoiceBasedChannelTypes The types of channels that are voice-based
265280
* @property {SelectMenuTypes} SelectMenuTypes The types of components that are select menus.
266281
* @property {Object} StickerFormatExtensionMap A mapping between sticker formats and their respective image formats.
282+
* @property {HolographicStyle} HolographicStyle Holographic color values for role styling.
267283
*/

packages/discord.js/typings/index.d.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3080,9 +3080,23 @@ export class RichPresenceAssets {
30803080
public smallImageURL(options?: ImageURLOptions): string | null;
30813081
}
30823082

3083+
export interface RoleColors {
3084+
primaryColor: number;
3085+
secondaryColor: number | null;
3086+
tertiaryColor: number | null;
3087+
}
3088+
3089+
export interface RoleColorsResolvable {
3090+
primaryColor: ColorResolvable;
3091+
secondaryColor?: ColorResolvable;
3092+
tertiaryColor?: ColorResolvable;
3093+
}
3094+
30833095
export class Role extends Base {
30843096
private constructor(client: Client<true>, data: RawRoleData, guild: Guild);
3097+
/** @deprecated Use {@link Role.colors} instead. */
30853098
public color: number;
3099+
public colors: RoleColors;
30863100
public get createdAt(): Date;
30873101
public get createdTimestamp(): number;
30883102
public get editable(): boolean;
@@ -3110,7 +3124,9 @@ export class Role extends Base {
31103124
channel: NonThreadGuildBasedChannel | Snowflake,
31113125
checkAdmin?: boolean,
31123126
): Readonly<PermissionsBitField>;
3127+
/** @deprecated Use {@link Role.setColors} instead. */
31133128
public setColor(color: ColorResolvable, reason?: string): Promise<Role>;
3129+
public setColors(colors: RoleColorsResolvable, reason?: string): Promise<Role>;
31143130
public setHoist(hoist?: boolean, reason?: string): Promise<Role>;
31153131
public setMentionable(mentionable?: boolean, reason?: string): Promise<Role>;
31163132
public setName(name: string, reason?: string): Promise<Role>;
@@ -4262,6 +4278,11 @@ export type DeletableMessageType =
42624278
| MessageType.UserJoin;
42634279

42644280
export const Constants: {
4281+
HolographicStyle: {
4282+
Primary: 11_127_295;
4283+
Secondary: 16_759_788;
4284+
Tertiary: 16_761_760;
4285+
};
42654286
MaxBulkDeletableMessageAge: 1_209_600_000;
42664287
SweeperKeys: SweeperKey[];
42674288
NonSystemMessageTypes: NonSystemMessageType[];
@@ -7372,7 +7393,9 @@ export interface ResolvedOverwriteOptions {
73727393

73737394
export interface RoleData {
73747395
name?: string;
7396+
/** @deprecated Use {@link RoleData.colors} instead. */
73757397
color?: ColorResolvable;
7398+
colors?: RoleColorsResolvable;
73767399
hoist?: boolean;
73777400
position?: number;
73787401
permissions?: PermissionResolvable;

0 commit comments

Comments
 (0)