2020-04-23 22:01:48 +02:00
import { Config } from "data" ;
2019-05-30 17:44:05 +02:00
import WebpackModules from "./webpackmodules" ;
2019-06-23 06:11:50 +02:00
import DiscordModules from "./discordmodules" ;
import Utilities from "./utilities" ;
import Patcher from "./patcher" ;
2019-05-30 07:06:17 +02:00
import BDLogo from "../ui/icons/bdlogo" ;
2021-07-08 01:55:56 +02:00
import Logger from "common/logger" ;
2019-05-28 23:27:25 +02:00
2020-07-16 07:42:56 +02:00
const React = DiscordModules . React ;
const Tooltip = WebpackModules . getByDisplayName ( "Tooltip" ) ;
2021-07-08 01:55:56 +02:00
const MutedStore = WebpackModules . getByProps ( "isMuted" ) ;
const Anchor = WebpackModules . getByDisplayName ( "Anchor" ) ;
const Developers = [
/* Zerebos#7790 */
"249746236008169473"
] ;
const DeveloperBadge = function DeveloperBadge ( { type , size = 16 } ) {
return React . createElement ( Tooltip , { color : "primary" , position : "top" , text : "BetterDiscord Developer" } ,
2021-07-09 08:05:58 +02:00
props => React . createElement ( Anchor , Object . assign ( { className : ` bd- ${ type } -badge ` , href : "https://github.com/BetterDiscord/BetterDiscord" , title : "BetterDiscord" , target : "_blank" } , props ) ,
2021-07-08 01:55:56 +02:00
React . createElement ( BDLogo , { size , className : "bd-logo" } )
)
) ;
} ;
2020-07-16 07:42:56 +02:00
2019-06-24 21:47:24 +02:00
export default new class ComponentPatcher {
2021-07-08 01:55:56 +02:00
warn ( ... message ) { return Logger . warn ( "ComponentPatcher" , ... message ) ; }
error ( ... message ) { return Logger . error ( "ComponentPatcher" , ... message ) ; }
debug ( ... message ) { return Logger . debug ( "ComponentPatcher" , ... message ) ; }
2019-05-28 20:19:48 +02:00
initialize ( ) {
2019-06-23 06:11:50 +02:00
Utilities . suppressErrors ( this . patchSocial . bind ( this ) , "BD Social Patch" ) ( ) ;
2021-07-08 01:55:56 +02:00
2019-06-23 06:11:50 +02:00
Utilities . suppressErrors ( this . patchGuildPills . bind ( this ) , "BD Guild Pills Patch" ) ( ) ;
Utilities . suppressErrors ( this . patchGuildListItems . bind ( this ) , "BD Guild List Items Patch" ) ( ) ;
2021-07-08 01:55:56 +02:00
/ *
2019-06-23 06:11:50 +02:00
Utilities . suppressErrors ( this . patchGuildSeparator . bind ( this ) , "BD Guild Separator Patch" ) ( ) ;
2020-12-12 21:33:32 +01:00
* /
2020-07-16 07:42:56 +02:00
Utilities . suppressErrors ( this . patchMessageHeader . bind ( this ) , "BD Message Header Patch" ) ( ) ;
Utilities . suppressErrors ( this . patchMemberList . bind ( this ) , "BD Member List Patch" ) ( ) ;
2021-07-08 01:55:56 +02:00
Utilities . suppressErrors ( this . patchProfile . bind ( this ) , "BD Profile Badges Patch" ) ( ) ;
2019-05-28 20:19:48 +02:00
}
2019-05-30 07:06:17 +02:00
patchSocial ( ) {
if ( this . socialPatch ) return ;
2019-06-23 06:11:50 +02:00
const TabBar = WebpackModules . getByDisplayName ( "TabBar" ) ;
2020-04-23 22:01:48 +02:00
if ( ! TabBar ) return ;
this . socialPatch = Patcher . after ( "ComponentPatcher" , TabBar . prototype , "render" , ( thisObject , args , returnValue ) => {
2019-06-23 06:11:50 +02:00
const children = returnValue . props . children ;
2020-04-23 22:01:48 +02:00
if ( ! children || ! children . length || children . length < 3 ) return ;
if ( children [ children . length - 3 ] . type . displayName !== "Separator" ) return ;
if ( ! children [ children . length - 2 ] . type . toString ( ) . includes ( "socialLinks" ) ) return ;
if ( Anchor ) {
const original = children [ children . length - 2 ] . type ;
const newOne = function ( ) {
const returnVal = original ( ... arguments ) ;
returnVal . props . children . push (
2021-04-19 03:48:41 +02:00
DiscordModules . React . createElement ( Anchor , { className : "bd-social-link" , href : "https://twitter.com/_BetterDiscord_" , title : "BetterDiscord" , target : "_blank" } ,
2020-07-16 07:42:56 +02:00
DiscordModules . React . createElement ( BDLogo , { size : "16px" , className : "bd-social-logo" } )
)
2020-04-23 22:01:48 +02:00
) ;
return returnVal ;
} ;
children [ children . length - 2 ] . type = newOne ;
}
2021-04-05 07:43:30 +02:00
const additional = DiscordModules . React . createElement ( "div" , { className : "colorMuted-HdFt4q size12-3cLvbJ" } , ` BetterDiscord ${ Config . version } ` ) ;
2020-04-23 22:01:48 +02:00
const originalVersions = children [ children . length - 1 ] . type ;
children [ children . length - 1 ] . type = function ( ) {
const returnVal = originalVersions ( ... arguments ) ;
returnVal . props . children . splice ( 1 , 0 , additional ) ;
2019-05-30 07:06:17 +02:00
return returnVal ;
} ;
2019-06-23 06:11:50 +02:00
} ) ;
2019-05-28 20:19:48 +02:00
}
2021-04-05 07:43:30 +02:00
2021-07-08 01:55:56 +02:00
isGuildMuted ( guildId ) {
if ( ! MutedStore || typeof ( MutedStore . isMuted ) !== "function" ) return false ;
return MutedStore . isMuted ( guildId ) ;
}
/ * *
* @ updated 07.07 . 2021
* /
async patchGuildListItems ( ) {
2019-05-28 20:19:48 +02:00
if ( this . guildListItemsPatch ) return ;
2021-07-08 01:55:56 +02:00
const guildClasses = WebpackModules . getByProps ( "downloadProgressCircle" , "guilds" ) ;
if ( ! guildClasses ) return this . warn ( "Failed to get guilds classes!" ) ;
const start = Date . now ( ) ;
const guilds = await new Promise ( ( resolve ) => Utilities . onAdded ( ` . ${ guildClasses . guilds } ` , resolve ) ) ;
if ( ! guilds ) return this . error ( "Cannot find guilds component." ) ;
const reactInstance = Utilities . getReactInstance ( guilds ) ;
if ( ! reactInstance ) return this . error ( "Failed to get Guilds instance." ) ;
const GuildComponent = await new Promise ( ( resolve ) => {
let tries = 0 ;
const searchForGuild = function ( ) {
tries ++ ;
const guild = Utilities . findInTree ( reactInstance , e => e ? . type ? . displayName === "Guild" , { walkable : [ "child" , "sibling" ] } ) ;
if ( guild ) { resolve ( guild ) ; }
else if ( tries < 10 ) { setTimeout ( searchForGuild , 300 ) ; }
else { resolve ( null ) ; }
} ;
searchForGuild ( ) ;
} ) ;
if ( ! GuildComponent || typeof ( GuildComponent . type ) !== "function" ) return this . error ( "Failed to get Guild component." ) ;
this . debug ( ` Found Guild component in ${ Date . now ( ) - start } ms ` ) ;
const Guild = GuildComponent . type ;
this . guildListItemsPatch = Patcher . after ( "ComponentPatcher" , Guild . prototype , "render" , ( thisObject , _ , returnValue ) => {
2020-02-28 01:00:12 +01:00
if ( ! returnValue || ! thisObject ) return ;
2019-06-23 06:11:50 +02:00
const guildData = thisObject . props ;
2019-05-28 20:19:48 +02:00
returnValue . props . className += " bd-guild" ;
if ( guildData . unread ) returnValue . props . className += " bd-unread" ;
if ( guildData . selected ) returnValue . props . className += " bd-selected" ;
if ( guildData . audio ) returnValue . props . className += " bd-audio" ;
if ( guildData . video ) returnValue . props . className += " bd-video" ;
if ( guildData . badge ) returnValue . props . className += " bd-badge" ;
if ( guildData . animatable ) returnValue . props . className += " bd-animatable" ;
2021-07-08 01:55:56 +02:00
if ( guildData . unavailable ) returnValue . props . className += " bd-unavailable" ;
if ( guildData . screenshare ) returnValue . props . className += " bd-screenshare" ;
if ( guildData . liveStage ) returnValue . props . className += " bd-live-stage" ;
if ( this . isGuildMuted ( guildData . guild . id ) ) returnValue . props . className += " bd-muted" ;
2019-05-28 20:19:48 +02:00
return returnValue ;
2019-06-23 06:11:50 +02:00
} ) ;
2019-05-28 20:19:48 +02:00
2021-07-08 01:55:56 +02:00
if ( reactInstance . forceUpdate ) reactInstance . forceUpdate ( ) ;
}
2019-05-28 20:19:48 +02:00
patchGuildPills ( ) {
if ( this . guildPillPatch ) return ;
2019-06-23 06:11:50 +02:00
const guildPill = WebpackModules . getModule ( m => m . default && ! m . default . displayName && m . default . toString && m . default . toString ( ) . includes ( "translate3d" ) ) ;
2019-05-28 20:19:48 +02:00
if ( ! guildPill ) return ;
2020-07-16 07:42:56 +02:00
this . guildPillPatch = Patcher . after ( "ComponentPatcher" , guildPill , "default" , ( _ , args , returnValue ) => {
2019-06-23 06:11:50 +02:00
const props = args [ 0 ] ;
if ( props . unread ) returnValue . props . className += " bd-unread" ;
if ( props . selected ) returnValue . props . className += " bd-selected" ;
if ( props . hovered ) returnValue . props . className += " bd-hovered" ;
return returnValue ;
} ) ;
2019-05-28 20:19:48 +02:00
}
2021-07-08 01:55:56 +02:00
/ *
2019-05-28 20:19:48 +02:00
patchGuildSeparator ( ) {
if ( this . guildSeparatorPatch ) return ;
2019-06-23 06:11:50 +02:00
const Guilds = WebpackModules . getByDisplayName ( "Guilds" ) ;
const guildComponents = WebpackModules . getByProps ( "renderListItem" ) ;
2019-05-28 20:19:48 +02:00
if ( ! guildComponents || ! Guilds ) return ;
const GuildSeparator = function ( ) {
2020-07-25 10:22:57 +02:00
const returnValue = guildComponents . Separator ( ... arguments ) ; // eslint-disable-line new-cap
2019-05-28 20:19:48 +02:00
returnValue . props . className += " bd-guild-separator" ;
return returnValue ;
} ;
2020-07-16 07:42:56 +02:00
this . guildSeparatorPatch = Patcher . after ( "ComponentPatcher" , Guilds . prototype , "render" , ( _ , _ _ , returnValue ) => {
2019-06-23 06:11:50 +02:00
const Separator = Utilities . findInReactTree ( returnValue , m => m . type && ! m . type . displayName && typeof ( m . type ) == "function" && Utilities . isEmpty ( m . props ) ) ;
if ( ! Separator ) return ;
Separator . type = GuildSeparator ;
} ) ;
2020-12-12 21:33:32 +01:00
} * /
2019-05-28 20:19:48 +02:00
2021-07-08 01:55:56 +02:00
/ * *
* @ updated 07.07 . 2021
* /
2020-07-16 07:42:56 +02:00
patchMessageHeader ( ) {
if ( this . messageHeaderPatch ) return ;
2021-07-08 01:55:56 +02:00
const MessageTimestamp = WebpackModules . getModule ( m => m ? . default ? . toString ( ) . indexOf ( "showTimestampOnHover" ) > - 1 ) ;
this . messageHeaderPatch = Patcher . after ( "ComponentPatcher" , MessageTimestamp , "default" , ( _ , [ { message } ] , returnValue ) => {
const userId = Utilities . getNestedProp ( message , "author.id" ) ;
if ( Developers . indexOf ( userId ) < 0 ) return ;
const children = Utilities . getNestedProp ( returnValue , "props.children.1.props.children" ) ;
2020-07-16 07:42:56 +02:00
if ( ! Array . isArray ( children ) ) return ;
2021-07-08 01:55:56 +02:00
children . splice ( 2 , 0 ,
React . createElement ( DeveloperBadge , {
type : "chat"
} )
2020-07-16 07:42:56 +02:00
) ;
} ) ;
}
2021-07-08 01:55:56 +02:00
/ * *
* @ updated 07.07 . 2021
* /
2020-07-16 07:42:56 +02:00
patchMemberList ( ) {
if ( this . memberListPatch ) return ;
const MemberListItem = WebpackModules . findByDisplayName ( "MemberListItem" ) ;
2021-07-08 01:55:56 +02:00
if ( ! MemberListItem ? . prototype ? . renderDecorators ) return ;
2020-07-16 07:42:56 +02:00
this . memberListPatch = Patcher . after ( "ComponentPatcher" , MemberListItem . prototype , "renderDecorators" , ( thisObject , args , returnValue ) => {
const user = Utilities . getNestedProp ( thisObject , "props.user" ) ;
const children = Utilities . getNestedProp ( returnValue , "props.children" ) ;
2021-07-08 01:55:56 +02:00
if ( ! children || Developers . indexOf ( user . id ) < 0 ) return ;
2020-07-16 07:42:56 +02:00
if ( ! Array . isArray ( children ) ) return ;
children . push (
2021-07-08 01:55:56 +02:00
React . createElement ( DeveloperBadge , {
type : "member"
} )
) ;
} ) ;
}
/ * *
* @ updated 07.07 . 2021
* /
patchProfile ( ) {
if ( this . profilePatch ) return ;
const UserProfileBadgeList = WebpackModules . getModule ( m => m ? . default ? . displayName === "UserProfileBadgeList" ) ;
this . profilePatch = Patcher . after ( "ComponentPatcher" , UserProfileBadgeList , "default" , ( _ , [ { user } ] , res ) => {
if ( Developers . indexOf ( user ? . id ) < 0 ) return ;
const children = Utilities . getNestedProp ( res , "props.children" ) ;
if ( ! Array . isArray ( children ) ) return ;
children . unshift (
React . createElement ( DeveloperBadge , {
type : "profile" ,
size : 18
} )
2020-07-16 07:42:56 +02:00
) ;
} ) ;
}
2020-11-07 07:03:29 +01:00
} ;
2020-11-19 18:48:51 +01:00
// as part of utility classes, i would like a way to distinguish channel types from the .content-3at_AU element. other than that, can't think of anything
// Tropical's notes
2021-04-05 07:43:30 +02:00
/ *
2020-12-12 21:33:32 +01:00
html [ maximized | bd | stable | canary | ptb ]
2020-11-19 18:48:51 +01:00
. iconWrapper - 2 OrFZ1 [ type ]
. sidebar - 2 K8pFh [ guild - id ]
2020-12-12 21:33:32 +01:00
. wrapper - 2 jXpOf [ voice | text | announcement | store | private | nsfw | rules ]
. chat - 3 bRxxu [ channnel - name | guild - id ]
2020-11-19 18:48:51 +01:00
. listItem - 2 P _4kh [ type | state ]
. privateChannels - 1 nO12o [ library - hidden ]
. member - 3 - YXUe [ user - id ]
2020-12-12 21:33:32 +01:00
. message - 2 qnXI6 [ type | author - id | group - end | message - content ]
2020-11-19 18:48:51 +01:00
. wrapper - 3 t9DeA [ user - id | status ]
2020-12-12 21:33:32 +01:00
. userPopout - 3 XzG _A [ user - id ]
. root - SR8cQa [ user - id ]
2020-11-19 18:48:51 +01:00
. contentRegion - 3 nDuYy [ settings - page ]
. item - PXvHYJ [ settings - page ]
2020-12-12 21:33:32 +01:00
. wrapper - 35 wsBm [ valid | expired | joined ]
2020-11-19 18:48:51 +01:00
* /