├── manifest.json
├── constants.js
├── i18n
├── index.js
├── ru.json
├── pt-BR.json
├── ro.json
├── pl.json
├── it.json
├── tr.json
├── en-US.json
└── de.json
├── memberCountsStore
├── actions.js
└── store.js
├── styles.scss
├── README.md
├── components
├── GuildProfileIcon.jsx
├── GuildIcon.jsx
├── GuildProfileFeatureList.jsx
├── GuildProfileHeader.jsx
├── GuildRelationships.jsx
├── GuildBanner.jsx
├── GuildProfileModal.jsx
├── GuildInfoBase.jsx
└── Icons.jsx
├── index.js
└── LICENSE
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Guild Profile",
3 | "version": "2.1.2",
4 | "description": "Allows you to open a guild profile modal that gives you various information about the guild.",
5 | "author": "NurMarvin",
6 | "license": "OSL-3.0"
7 | }
8 |
--------------------------------------------------------------------------------
/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | module.exports = Object.freeze({
7 | FluxActions: {
8 | UPDATE_MEMBER_COUNTS: 'GUILD_PROFILE_UPDATE_MEMBER_COUNTS',
9 | },
10 | });
--------------------------------------------------------------------------------
/i18n/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | require('fs')
7 | .readdirSync(__dirname)
8 | .filter(file => file !== 'index.js')
9 | .forEach(filename => {
10 | const moduleName = filename.split('.')[0];
11 | exports[moduleName] = require(`${__dirname}/${filename}`);
12 | });
13 |
--------------------------------------------------------------------------------
/memberCountsStore/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const { FluxDispatcher } = require('powercord/webpack');
7 | const { FluxActions } = require('../constants');
8 |
9 | module.exports = {
10 | updateMemberCounts: async (memberCountsUpdate) => {
11 | FluxDispatcher.dispatch({
12 | type: FluxActions.UPDATE_MEMBER_COUNTS,
13 | ...memberCountsUpdate
14 | });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/styles.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | .guild-info-section {
7 | padding: 5px 10px;
8 | }
9 |
10 | .guild-profile {
11 | padding: 20px 10px;
12 | height: 100%;
13 | }
14 |
15 | .guild-icon-acronym {
16 | font-size: 2em;
17 | text-align: center;
18 | margin: auto;
19 | color: var(--text-normal);
20 | overflow: hidden;
21 | width: 100%;
22 | }
23 |
24 | .guild-profile-feature-badge {
25 | color: var(--interactive-normal);
26 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Guild Profile
2 |
3 | Open a guild profile modal that gives you various information about the guild.
4 |
5 | ## Support Server (NEW)
6 | Need help with the plugin? Feel free to drop into the [support server](https://nurmarv.in/support) and ask for help!
7 |
8 | ## Preview
9 |
10 | 
11 |
12 | ## Installation
13 |
14 | You're probably totally stoked to learn how to install this bad boy, right? \
15 | It's real simple! Just follow our certified installation guide and you're ready to use it:
16 |
17 | 1. Clone this repository into your Powercord plugins folder using `git clone https://github.com/NurMarvin/guild-profile.git`
18 | 2. Reload Discord using `Ctrl + R`
19 |
--------------------------------------------------------------------------------
/components/GuildProfileIcon.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const { React, getModule } = require('powercord/webpack');
7 | const { icon } = getModule(['menu', 'icon'], false);
8 |
9 | module.exports = React.memo((props) => (
10 |
25 | ));
26 |
--------------------------------------------------------------------------------
/i18n/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "GUILD_PROFILE": "Профиль Сервера",
3 | "GUILD_INFO": "Информация о Сервере",
4 | "LOADING": "Загрузка",
5 | "FRIENDS_IN_GUILD": "Друзья",
6 | "BLOCKED_USERS_IN_GUILD": "Заблокированные Пользователи",
7 | "NOT_IMPLEMENTED_YET": "Еще Не Реализовано",
8 | "JOINED_AT": "Зашёл",
9 | "CREATED_AT": "Создан",
10 | "COMMUNITY": "Публичный",
11 | "COMMERCE": "Каналы \"Магазины\"",
12 | "ENABLED_DISCOVERABLE_BEFORE": "Enabled Discoverable Before",
13 | "INVITE_SPLASH": "Фон Приглашения На сервер",
14 | "BANNER": "Баннер Сервера",
15 | "ANIMATED_ICON": "Анимированная Иконка Сервера",
16 | "DISCOVERABLE": "Видимый",
17 | "WELCOME_SCREEN_ENABLED": "Экран Приветствия Включен",
18 | "NEWS": "Канал Объявлений",
19 | "FEATURABLE": "Featurable",
20 | "VIP_REGIONS": "VIP Регионы"
21 | }
22 |
--------------------------------------------------------------------------------
/i18n/pt-BR.json:
--------------------------------------------------------------------------------
1 | {
2 | "GUILD_PROFILE": "Informações do servidor",
3 | "GUILD_INFO": "Informações do servidor",
4 | "LOADING": "Processando",
5 | "FRIENDS_IN_GUILD": "Amigos",
6 | "BLOCKED_USERS_IN_GUILD": "Usuários bloqueados",
7 | "NOT_IMPLEMENTED_YET": "Ainda não implementado",
8 | "JOINED_AT": "Data de sua entrada",
9 | "CREATED_AT": "Data de criação",
10 | "COMMUNITY": "Comunidade",
11 | "COMMERCE": "Contém canais de loja",
12 | "ENABLED_DISCOVERABLE_BEFORE": "Ativado Servidor público antes",
13 | "INVITE_SPLASH": "Plano de fundo de convite do servidor",
14 | "BANNER": "Banner do servidor",
15 | "ANIMATED_ICON": "Ícone animado do servidor",
16 | "DISCOVERABLE": "Servidor público",
17 | "WELCOME_SCREEN_ENABLED": "Tela de bem-vindo do servidor ativada",
18 | "NEWS": "Contém canais de anúncio",
19 | "FEATURABLE": "Pode ser inserido na home do Discover",
20 | "VIP_REGIONS": "Regiões VIP"
21 | }
22 |
--------------------------------------------------------------------------------
/memberCountsStore/store.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const { Flux, FluxDispatcher } = require('powercord/webpack');
7 | const { FluxActions } = require('../constants');
8 |
9 | const memberCounts = [];
10 |
11 | function handleMemberCountsUpdate(memberCountsUpdate) {
12 | memberCounts.push(memberCountsUpdate);
13 | }
14 |
15 | class MemberCountsStore extends Flux.Store {
16 | getStore() {
17 | return {
18 | memberCounts,
19 | };
20 | }
21 |
22 | getAllMemberCounts() {
23 | return memberCounts;
24 | }
25 |
26 | getMemberCounts(guildId) {
27 | return memberCounts.find(memberCounts => memberCounts.guildId === guildId);
28 | }
29 | }
30 |
31 | module.exports = new MemberCountsStore(FluxDispatcher, {
32 | [FluxActions.UPDATE_MEMBER_COUNTS]: (guildId, members, membersOnline) => handleMemberCountsUpdate(guildId, members, membersOnline),
33 | });
--------------------------------------------------------------------------------
/i18n/ro.json:
--------------------------------------------------------------------------------
1 | {
2 | "GUILD_PROFILE": "Profilul server-ului",
3 | "GUILD_INFO": "Informații despre server",
4 | "LOADING": "Se încarcă",
5 | "FRIENDS_IN_GUILD": "Prieteni",
6 | "BLOCKED_USERS_IN_GUILD": "Utilizatori blocați",
7 | "NOT_IMPLEMENTED_YET": "În lucru",
8 | "JOINED_AT": "Alaturat la",
9 | "CREATED_AT": "Creat la",
10 | "COMMUNITY": "Comunitate",
11 | "COMMERCE": "Canale de comerț",
12 | "ENABLED_DISCOVERABLE_BEFORE": "Descoperibil înainte",
13 | "INVITE_SPLASH": "Fundal de invite",
14 | "BANNER": "Banner-ul server-ului",
15 | "ANIMATED_ICON": "Imaginea animată a server-ului",
16 | "DISCOVERABLE": "Descoperibilă",
17 | "WELCOME_SCREEN_ENABLED": "Ecran de început",
18 | "NEWS": "Canale de anunțuri",
19 | "FEATURABLE": "Promovabil",
20 | "VIP_REGIONS": "Regiuni VIP",
21 | "MORE_EMOJI": "Mai multe emoji-uri",
22 | "RELAY_ENABLED": "Relay activat",
23 | "CLICK_TO_COPY_SERVER_ICON_URL": "Click pentru a copia imaginea server-ului",
24 | "NO_FRIENDS_IN_THIS_GUILD": "Nu există prieteni în acest server",
25 | "NO_BLOCKED_USERS_IN_THIS_GUILD": "Nu există utilizatori blocați în acest server",
26 | "GUILD_PREMIUM_SUBSCRIBER_COUNT": "Numărul de boosteri",
27 | "GUILD_PREMIUM_TIER": "Nivelul de boost al server-ului"
28 | }
--------------------------------------------------------------------------------
/i18n/pl.json:
--------------------------------------------------------------------------------
1 | {
2 | "GUILD_PROFILE": "Profil serwera",
3 | "GUILD_INFO": "Informacje o serwerze",
4 | "LOADING": "Ładowanie",
5 | "FRIENDS_IN_GUILD": "Znajomi",
6 | "BLOCKED_USERS_IN_GUILD": "Zablokowani użytkownicy",
7 | "NOT_IMPLEMENTED_YET": "Jeszcze niezaimplementowane",
8 | "JOINED_AT": "Data dołączenia",
9 | "CREATED_AT": "Data utworzenia",
10 | "COMMUNITY": "Społeczność",
11 | "COMMERCE": "Kanały sklepu",
12 | "ENABLED_DISCOVERABLE_BEFORE": "Wcześniej włączone odnajdowanie serwera",
13 | "INVITE_SPLASH": "Tło zaproszenia do serwera",
14 | "BANNER": "Banner serwera",
15 | "ANIMATED_ICON": "Animowana ikona serwera",
16 | "DISCOVERABLE": "Odnajdywalny",
17 | "WELCOME_SCREEN_ENABLED": "Włączony ekran powitalny",
18 | "NEWS": "Kanały ogłoszeń",
19 | "FEATURABLE": "Featurable",
20 | "VIP_REGIONS": "Regiony VIP",
21 | "MORE_EMOJI": "Więcej emoji",
22 | "RELAY_ENABLED": "Włączona retransmisja",
23 | "PREVIEW_ENABLED": "Włączony przegląd",
24 | "MEMBER_VERIFICATION_GATE_ENABLED": "Włączone sprawdzanie członków",
25 | "CLICK_TO_COPY_SERVER_ICON_URL": "Kliknij aby skopiować URL ikony serwera",
26 | "NO_FRIENDS_IN_THIS_GUILD": "Brak znajomych na tym serwerze",
27 | "NO_BLOCKED_USERS_IN_THIS_GUILD": "Brak zablokowanych użytkowników na tym serwerze",
28 | "GUILD_PREMIUM_SUBSCRIBER_COUNT": "Ilość osób ulepszających serwer",
29 | "GUILD_PREMIUM_TIER": "Poziom ulepszenia serwera",
30 | "PREFERRED_LOCALE": "Preferowane umiejscowienie",
31 | "NSFW": "Not Safe For Work (NSFW)/Zbanowane na iOS",
32 | "YES": "Tak",
33 | "NO": "Nie"
34 | }
35 |
--------------------------------------------------------------------------------
/i18n/it.json:
--------------------------------------------------------------------------------
1 | {
2 | "GUILD_PROFILE": "Profilo Server",
3 | "GUILD_INFO": "Informazioni Server",
4 | "LOADING": "Caricamento",
5 | "FRIENDS_IN_GUILD": "Amici",
6 | "BLOCKED_USERS_IN_GUILD": "Utenti Bloccati",
7 | "NOT_IMPLEMENTED_YET": "Non ancora implementato",
8 | "JOINED_AT": "Iscritto il",
9 | "CREATED_AT": "Creato il",
10 | "COMMUNITY": "Comunità",
11 | "COMMERCE": "Canali Negozio",
12 | "ENABLED_DISCOVERABLE_BEFORE": "Ha abilitato \"Discovery\" in precedenza",
13 | "INVITE_SPLASH": "Sfondo sulla schermata di invito",
14 | "BANNER": "Banner del Server",
15 | "ANIMATED_ICON": "Icona del server animata",
16 | "DISCOVERABLE": "Disponibile su \"Discovery\"",
17 | "WELCOME_SCREEN_ENABLED": "Schermata di benvenuto abilitata",
18 | "NEWS": "Canale Annunci",
19 | "FEATURABLE": "In Evidenza",
20 | "VIP_REGIONS": "Regioni VIP",
21 | "MORE_EMOJI": "Più Emoji",
22 | "RELAY_ENABLED": "Relay Abilitato",
23 | "PREVIEW_ENABLED": "Anteprima Abilitata",
24 | "MEMBER_VERIFICATION_GATE_ENABLED": "Verifica iniziale dei membri abilitata",
25 | "CLICK_TO_COPY_SERVER_ICON_URL": "Clicca per copiare l'URL dell'icona del server",
26 | "NO_FRIENDS_IN_THIS_GUILD": "Nessuno dei tuoi amici è in questo server",
27 | "NO_BLOCKED_USERS_IN_THIS_GUILD": "Non ci sono utenti bloccati in questo server",
28 | "GUILD_PREMIUM_SUBSCRIBER_COUNT": "Numero Boost nel Server",
29 | "GUILD_PREMIUM_TIER": "Livello Boost del Server",
30 | "PREFERRED_LOCALE": "Lingua Preferita",
31 | "NSFW": "Contenuti Espliciti (NSFW)/Bannato on iOS",
32 | "YES": "Si",
33 | "NO": "No"
34 | }
35 |
--------------------------------------------------------------------------------
/components/GuildIcon.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const { React, getModule } = require('powercord/webpack');
7 |
8 | module.exports = class GuildIcon extends React.PureComponent {
9 | constructor(props) {
10 | super(props);
11 |
12 | const { wrapper, avatar, avatarStack } = getModule(
13 | ['avatar', 'wrapper', 'avatarStack'],
14 | false
15 | );
16 |
17 | this.modules = {
18 | acronym: getModule(['childWrapper', 'acronym'], false).acronym,
19 | wrapper,
20 | avatarStack,
21 | realAvatar: avatar,
22 | ...getModule(['headerTop', 'avatar', 'badgeList'], false),
23 | ...getModule(['getGuildIconURL'], false),
24 | };
25 | }
26 |
27 | render() {
28 | const { guild } = this.props;
29 |
30 | return (
31 |
32 |
33 |
34 | {guild.icon ? (
35 |
})
41 | ) : (
42 |
47 | {guild.acronym}
48 |
49 | )}
50 |
51 |
52 |
53 | );
54 | }
55 | };
56 |
--------------------------------------------------------------------------------
/i18n/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "GUILD_PROFILE": "Sunucu Profili",
3 | "GUILD_INFO": "Sunucu Bilgisi",
4 | "LOADING": "Yükleniyor",
5 | "FRIENDS_IN_GUILD": "Arkadaşlar",
6 | "BLOCKED_USERS_IN_GUILD": "Yasaklanan Kullanıcılar",
7 | "NOT_IMPLEMENTED_YET": "Henüz uygulanmadı",
8 | "JOINED_AT": "Katıldığı Tarih",
9 | "CREATED_AT": "Oluşturulduğu Tarih",
10 | "COMMUNITY": "Topluluk",
11 | "COMMERCE": "Mağaza Kanalları",
12 | "ENABLED_DISCOVERABLE_BEFORE": "Daha Önce Keşfedilebilir Etkinleştirilmiş",
13 | "INVITE_SPLASH": "Sunucu Davet Arkaplanı",
14 | "BANNER": "Sunucu Afişi",
15 | "ANIMATED_ICON": "Hareketli Sunucu Fotoğrafı",
16 | "DISCOVERABLE": "Keşfedilebilir",
17 | "WELCOME_SCREEN_ENABLED": "Hoşgeldin Ekranı Etkin",
18 | "NEWS": "Duyuru Kanalları",
19 | "FEATURABLE": "Öne Çıkan",
20 | "VIP_REGIONS": "VIP Bölgeler",
21 | "MORE_EMOJI": "Daha Fazla Emoji",
22 | "RELAY_ENABLED": "Relay Etkin",
23 | "PREVIEW_ENABLED": "Önizleme Etkin",
24 | "MEMBER_VERIFICATION_GATE_ENABLED": "Üyelik Kapısı Etkin",
25 | "PRIVATE_THREADS": "Özel Konular",
26 | "THREE_DAY_THREAD_ARCHIVE": "Üç Günlük Konu Arşivi",
27 | "SEVEN_DAY_THREAD_ARCHIVE": "Yedi Günlük Konu Arşivi",
28 | "CLICK_TO_COPY_SERVER_ICON_URL": "Sunucu simgesinin URL'sini kopyalamak için tıkla",
29 | "NO_FRIENDS_IN_THIS_GUILD": "Bu sunucuda arkadaşın yok",
30 | "NO_BLOCKED_USERS_IN_THIS_GUILD": "Bu sunucuda engellenen kullanıcı yok",
31 | "NO_EXPERIMENTS_IN_THIS_GUILD": "Bu sunucuda etkin sunucu deneyimleri yok",
32 | "GUILD_PREMIUM_SUBSCRIBER_COUNT": "Sunucu Takviye Sayısı",
33 | "GUILD_PREMIUM_TIER": "Sunucu Takviye Seviyesi",
34 | "PREFERRED_LOCALE": "Tercih Edilen Yer",
35 | "NSFW": "İş İçin Güvenli Değil (NSFW) Düzeyi",
36 | "NSFW_LEVEL_DEFAULT": "Varsayılan",
37 | "NSFW_LEVEL_EXPLICIT": "Müstehcen",
38 | "NSFW_LEVEL_SAFE": "Güvenli",
39 | "NSFW_LEVEL_AGE_RESTRICTED": "Yaş Sınırlı",
40 | "ENABLED_EXPERIMENTS": "Deneyimler Etkin",
41 | "EXCLUSIVE_TO_SERVER_BANNER_FEATURE": "Sunucu afiş özelliğine sahip sunuculara özel",
42 | "NONE": "Yok"
43 | }
44 |
--------------------------------------------------------------------------------
/components/GuildProfileFeatureList.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const {
7 | React,
8 | getModule,
9 | i18n: { Messages },
10 | } = require("powercord/webpack");
11 |
12 | const { Tooltip, Clickable } = require("powercord/components");
13 |
14 | const Icons = require("./Icons");
15 |
16 | module.exports = class GuildProfileFeatureList extends React.PureComponent {
17 | constructor(props) {
18 | super(props);
19 |
20 | this.modules = {
21 | ...getModule(["container", "clickable", "profileBadge"], false),
22 | theme: getModule(["theme"], false).theme,
23 | };
24 | }
25 |
26 | render() {
27 | const { guild, className } = this.props;
28 |
29 | return (
30 |
35 | {Array.from(guild.features)
36 | .filter(
37 | (feature) => feature !== "VERIFIED" && feature !== "PARTNERED"
38 | )
39 | .map((feature) => {
40 | const Icon = Icons[feature] ?? Icons.UNKNOWN;
41 |
42 | return (
43 |
45 | window.open(
46 | `https://github.com/Delitefully/DiscordLists#guild-feature-glossary:~:.-,text=${feature}`,
47 | "_blank"
48 | )
49 | }
50 | className={this.modules.clickable}
51 | >
52 |
58 | 14
61 | ? this.modules.profileBadge18
62 | : this.modules.profileBadge24,
63 | this.modules.profileBadge,
64 | this.modules.desaturate,
65 | "guild-profile-feature-badge",
66 | ].join(" ")}
67 | />
68 |
69 |
70 | );
71 | })}
72 |
73 | );
74 | }
75 | };
76 |
--------------------------------------------------------------------------------
/i18n/en-US.json:
--------------------------------------------------------------------------------
1 | {
2 | "GUILD_PROFILE": "Guild Profile",
3 | "GUILD_INFO": "Server Info",
4 | "LOADING": "Loading",
5 | "FRIENDS_IN_GUILD": "Friends",
6 | "BLOCKED_USERS_IN_GUILD": "Blocked Users",
7 | "NOT_IMPLEMENTED_YET": "Not implemented yet",
8 | "JOINED_AT": "Joined at",
9 | "CREATED_AT": "Created at",
10 | "COMMUNITY": "Community",
11 | "COMMERCE": "Store Channels",
12 | "ENABLED_DISCOVERABLE_BEFORE": "Enabled Discoverable Before",
13 | "INVITE_SPLASH": "Server Invite Background",
14 | "BANNER": "Server Banner",
15 | "ANIMATED_ICON": "Animated Server Icon",
16 | "DISCOVERABLE": "Discoverable",
17 | "WELCOME_SCREEN_ENABLED": "Welcome Screen Enabled",
18 | "NEWS": "Announcement Channels",
19 | "FEATURABLE": "Featurable",
20 | "VIP_REGIONS": "VIP Regions",
21 | "MORE_EMOJI": "More Emoji",
22 | "RELAY_ENABLED": "Relay Enabled",
23 | "PREVIEW_ENABLED": "Preview Enabled",
24 | "MEMBER_VERIFICATION_GATE_ENABLED": "Membership Gating Enabled",
25 | "THREADS_ENABLED": "Threads Enabled",
26 | "PRIVATE_THREADS": "Private Threads",
27 | "THREE_DAY_THREAD_ARCHIVE": "Three Day Thread Archive",
28 | "SEVEN_DAY_THREAD_ARCHIVE": "Seven Day Thread Archive",
29 | "ROLE_ICONS": "Role Icons",
30 | "ANIMATED_BANNER": "Animated Server Banner",
31 | "MEMBER_PROFILES": "Member Profiles",
32 | "AUTO_MODERATION": "Auto Moderation",
33 | "TEXT_IN_VOICE_ENABLED": "Text in Voice Enabled",
34 | "ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE": "Role Subscriptions Available for Purchase",
35 | "EXPOSED_TO_ACTIVITIES_WTP_EXPERIMENT": "Exposed to Activities \"Willingness To Pay\" Experiment",
36 | "HAD_EARLY_ACTIVITIES_ACCESS": "Had Early Activities Access",
37 | "INVITES_DISABLED": "Invites Disabled",
38 | "ROLE_SUBSCRIPTIONS_ENABLED": "Role Subscriptions Enabled",
39 | "PREMIUM_TIER_3_OVERRIDE": "Server Boost Tier 3 Override",
40 | "BOT_DEVELOPER_EARLY_ACCESS": "Bot Developer Early Access",
41 | "INTERNAL_EMPLOYEE_ONLY": "Internal Employee Only",
42 | "CREATOR_MONETIZABLE": "Creator Monetizable",
43 | "CLICK_TO_COPY_SERVER_ICON_URL": "Click to copy server icon URL",
44 | "NO_FRIENDS_IN_THIS_GUILD": "No friends in this server",
45 | "NO_BLOCKED_USERS_IN_THIS_GUILD": "No blocked users in this server",
46 | "NO_EXPERIMENTS_IN_THIS_GUILD": "No enabled guild experiments in this server",
47 | "GUILD_PREMIUM_SUBSCRIBER_COUNT": "Server Booster Count",
48 | "GUILD_PREMIUM_TIER": "Server Boost Level",
49 | "PREFERRED_LOCALE": "Preferred Locale",
50 | "NSFW": "Not Safe For Work (NSFW) Level",
51 | "NSFW_LEVEL_DEFAULT": "Default",
52 | "NSFW_LEVEL_EXPLICIT": "Explicit",
53 | "NSFW_LEVEL_SAFE": "Safe",
54 | "NSFW_LEVEL_AGE_RESTRICTED": "Age Restricted",
55 | "ENABLED_EXPERIMENTS": "Enabled Experiments",
56 | "EXCLUSIVE_TO_SERVER_BANNER_FEATURE": "Exclusive to servers with the server banner feature",
57 | "NONE": "None"
58 | }
59 |
--------------------------------------------------------------------------------
/i18n/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "GUILD_PROFILE": "Serverprofil",
3 | "GUILD_INFO": "Serverinfo",
4 | "LOADING": "Lade",
5 | "FRIENDS_IN_GUILD": "Freunde",
6 | "BLOCKED_USERS_IN_GUILD": "Blockierte Nutzer",
7 | "NOT_IMPLEMENTED_YET": "Noch nicht implementiert",
8 | "JOINED_AT": "Server betreten",
9 | "CREATED_AT": "Server erstellt",
10 | "COMMUNITY": "Community",
11 | "COMMERCE": "Store-Kanäle",
12 | "ENABLED_DISCOVERABLE_BEFORE": "Hat \"Entdeckbar\" vorher schon mal aktiviert",
13 | "INVITE_SPLASH": "Hintergrund für Servereinladungen",
14 | "BANNER": "Server Banner",
15 | "ANIMATED_ICON": "Animiertes Servericon",
16 | "DISCOVERABLE": "Entdeckbar",
17 | "WELCOME_SCREEN_ENABLED": "Willkommensbildschirm aktiviert",
18 | "NEWS": "Ankündigungskanäle",
19 | "FEATURABLE": "Vorstellbar",
20 | "VIP_REGIONS": "VIP Regionen",
21 | "MORE_EMOJI": "Mehr Emojis",
22 | "RELAY_ENABLED": "Relay aktiviert",
23 | "PREVIEW_ENABLED": "Vorschau aktiviert",
24 | "MEMBER_VERIFICATION_GATE_ENABLED": "Mitgliedschaftsbeschränkungen aktiviert",
25 | "PRIVATE_THREADS": "Private Threads",
26 | "THREE_DAY_THREAD_ARCHIVE": "3-Tage Thread Archivierung",
27 | "SEVEN_DAY_THREAD_ARCHIVE": "7-Tage Thread Archivierung",
28 | "ROLE_ICONS": "Rollenicons",
29 | "ANIMATED_BANNER": "Animierter Serverbanner",
30 | "MEMBER_PROFILES": "Mitgliederprofile",
31 | "AUTO_MODERATION": "Auto-Moderation",
32 | "TEXT_IN_VOICE_ENABLED": "Text in Sprachkanälen aktiviert",
33 | "ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE": "Rollenabonnements zum Kauf verfügbar",
34 | "EXPOSED_TO_ACTIVITIES_WTP_EXPERIMENT": "Hat das \"Bereitschaft Zu Zahlen\" Aktitäts-Experiment",
35 | "HAD_EARLY_ACTIVITIES_ACCESS": "Hatte frühen Zugang zu Aktivitäten",
36 | "INVITES_DISABLED": "Einladungen deaktiviert",
37 | "ROLE_SUBSCRIPTIONS_ENABLED": "Rollenabonnements aktiviert",
38 | "PREMIUM_TIER_3_OVERRIDE": "Server-Boost Level 3 Überschreibung",
39 | "BOT_DEVELOPER_EARLY_ACCESS": "Früher Zugang für Bot-Entwickler",
40 | "INTERNAL_EMPLOYEE_ONLY": "Interner Server, nur für Mitarbeiter",
41 | "CREATOR_MONETIZABLE": "Monetarisierbar für Servereigentümer",
42 | "CLICK_TO_COPY_SERVER_ICON_URL": "Klicke um die Server Icon URL zu kopieren",
43 | "NO_FRIENDS_IN_THIS_GUILD": "Keine Freunde in diesem Server",
44 | "NO_BLOCKED_USERS_IN_THIS_GUILD": "Keine blockierten Nutzer in diesem Server",
45 | "GUILD_PREMIUM_SUBSCRIBER_COUNT": "Anzahl der Server Booster",
46 | "GUILD_PREMIUM_TIER": "Server Boost Level",
47 | "NSFW": "Nicht sicher für die Arbeit (NSFW) Level",
48 | "NSFW_LEVEL_DEFAULT": "Standard",
49 | "NSFW_LEVEL_EXPLICIT": "Explizit",
50 | "NSFW_LEVEL_SAFE": "Sicher",
51 | "NSFW_LEVEL_AGE_RESTRICTED": "Altersbeschränkt",
52 | "ENABLED_EXPERIMENTS": "Aktivierte Experimente",
53 | "EXCLUSIVE_TO_SERVER_BANNER_FEATURE": "Exklusiv für Server mit dem Server Banner Feature",
54 | "NONE": "Keine"
55 | }
56 |
--------------------------------------------------------------------------------
/components/GuildProfileHeader.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const {
7 | React,
8 | getModule,
9 | i18n: { Messages },
10 | getModuleByDisplayName,
11 | } = require('powercord/webpack');
12 |
13 | const { Text } = require('powercord/components');
14 | const AsyncComponent = require('powercord/components/AsyncComponent');
15 |
16 | const inviteModule = getModule((m) => m.displayName === 'InviteButton' && m.Header)
17 | const GuildBadge = AsyncComponent.from(getModuleByDisplayName('GuildBadge'));
18 | const InviteButton = AsyncComponent.from(inviteModule);
19 |
20 | inviteModule.then((Button) => {
21 | ['Data'].forEach((prop) => (InviteButton[prop] = Button[prop]));
22 | });
23 |
24 | const GuildBanner = require('./GuildBanner');
25 | const GuildIcon = require('./GuildIcon');
26 | const GuildProfileFeatureList = require('./GuildProfileFeatureList');
27 |
28 | module.exports = class GuildProfileHeader extends React.PureComponent {
29 | constructor(props) {
30 | super(props);
31 |
32 | this.modules = {
33 | ...getModule(['guildDetail'], false),
34 | ...getModule(['topSection'], false),
35 | ...getModule(['wrapper', 'pointer'], false),
36 | ...getModule(['guildIconContainer', 'guildBadge'], false),
37 | ...getModule(['headerTop', 'avatar', 'badgeList'], false),
38 | };
39 | }
40 | render() {
41 | const { guild, counts } = this.props;
42 |
43 | return (
44 |
87 | );
88 | }
89 | };
90 |
--------------------------------------------------------------------------------
/components/GuildRelationships.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const {
7 | React,
8 | getModule,
9 | i18n: { Messages },
10 | getModuleByDisplayName,
11 | } = require('powercord/webpack');
12 | const { AdvancedScrollerThin } = getModule(['AdvancedScrollerThin'], false);
13 | const { Clickable, Spinner } = require('powercord/components');
14 | const AsyncComponent = require('powercord/components/AsyncComponent');
15 | const DiscordTag = AsyncComponent.from(getModuleByDisplayName('DiscordTag'));
16 | const { default: Avatar } = getModule(['AnimatedAvatar'], false);
17 | const { close } = require('powercord/modal');
18 |
19 | const ContextMenu = getModule(['closeContextMenu'], false);
20 |
21 | const UserProfileModalActionCreators = getModule(
22 | ['openUserProfileModal'],
23 | false
24 | );
25 |
26 | class RelationshipRow extends React.PureComponent {
27 | constructor(props) {
28 | super(props);
29 |
30 | this.modules = {
31 | ...getModule(['listRow'], false),
32 | };
33 | }
34 |
35 | render() {
36 | const { user, status, onSelect, onContextMenu } = this.props;
37 |
38 | return (
39 | onSelect(user.id)}
42 | onContextMenu={() => onContextMenu(user.id)}
43 | >
44 |
50 |
55 |
56 | );
57 | }
58 | }
59 |
60 | module.exports = class Relationships extends React.PureComponent {
61 | constructor(props) {
62 | super(props);
63 |
64 | this.modules = {
65 | nelly: getModule(['flexWrapper', 'image'], false).image,
66 | ...getModule(['emptyIconFriends'], false),
67 | ...getModule(['scrollerBase', 'fade', 'thin'], false),
68 | };
69 | }
70 |
71 | handleSelect(userId) {
72 | close();
73 | UserProfileModalActionCreators.openUserProfileModal({ userId });
74 | }
75 |
76 | render() {
77 | const { relationships, section } = this.props;
78 |
79 | if (!relationships) {
80 | return (
81 |
82 |
83 |
84 | );
85 | } else if (relationships.length < 1) {
86 | return (
87 |
88 |
89 |
90 | {Messages[`NO_${section}_IN_THIS_GUILD`]}
91 |
92 |
93 | );
94 | }
95 | return (
96 |
100 | {relationships.map((relationship) => (
101 |
104 | ContextMenu.openContextMenu(event, () => {})
105 | }
106 | user={relationship}
107 | />
108 | ))}
109 |
110 | );
111 | }
112 | };
113 |
--------------------------------------------------------------------------------
/components/GuildBanner.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const {
7 | React,
8 | getModule,
9 | i18n: { Messages },
10 | } = require("powercord/webpack");
11 | const { Tooltip } = require("powercord/components");
12 |
13 | const { TextBadge } = getModule(["TextBadge"], false);
14 | const { Colors } = getModule(["Colors"], false);
15 |
16 | module.exports = class GuildBanner extends React.PureComponent {
17 | state = {
18 | color: null,
19 | hovered: false,
20 | };
21 |
22 | constructor(props) {
23 | super(props);
24 |
25 | this.modules = {
26 | ...getModule(["profileBannerPremium", "profileBanner"], false),
27 | getPaletteForAvatar: getModule(["getPaletteForAvatar"], false)
28 | .getPaletteForAvatar,
29 | getGuildIconURL: getModule(["getGuildIconURL"], false).getGuildIconURL,
30 | getGuildBannerURL: getModule(["getGuildBannerURL"], false)
31 | .getGuildBannerURL,
32 | ...getModule(["getBestMediaProxySize"], false),
33 | canUseWebp: getModule(["canUseWebp"], false).canUseWebp,
34 | isAnimatedIconHash: getModule(["isAnimatedIconHash"], false)
35 | .isAnimatedIconHash,
36 | Endpoints: getModule(["Endpoints"], false).Endpoints,
37 | };
38 | }
39 |
40 | async componentDidMount() {
41 | const { guild } = this.props;
42 |
43 | let color = Colors.BRAND;
44 |
45 | if (guild.icon) {
46 | const palette = await this.modules.getPaletteForAvatar(
47 | this.modules.getGuildIconURL(guild)
48 | );
49 | color = `rgb(${palette[0].join(", ")})`;
50 | }
51 |
52 | this.setState({ color });
53 | }
54 |
55 | render() {
56 | const { guild } = this.props;
57 | const { color } = this.state;
58 |
59 | if (!color) return null;
60 |
61 | const classes = [this.modules.banner];
62 | const style = {
63 | backgroundColor: color,
64 | };
65 |
66 | if (guild.banner) {
67 | classes.push(
68 | this.modules.profileBannerPremium,
69 | this.modules.bannerPremium
70 | );
71 |
72 | const { id, banner } = guild;
73 |
74 | if (banner == null) return null;
75 |
76 | const cdnHost = window.GLOBAL_ENV.CDN_HOST;
77 | const extension =
78 | this.state.hovered && this.modules.isAnimatedIconHash(banner)
79 | ? "gif"
80 | : this.modules.canUseWebp
81 | ? "webp"
82 | : "jpg";
83 | const bannerSize = this.modules.getBestMediaProxySize(
84 | 600 * this.modules.getDevicePixelRatio()
85 | );
86 | let url =
87 | (cdnHost
88 | ? `${location.protocol}//${cdnHost}/banners/${id}/${banner}.${extension}`
89 | : this.modules.Endpoints.GUILD_BANNER(id, banner, extension)) +
90 | `?size=${bannerSize}`;
91 |
92 | if (extension === "jpg") {
93 | url += "&quality=lossless";
94 | }
95 |
96 | style.backgroundImage = `url(${url})`;
97 | } else {
98 | classes.push(this.modules.profileBanner);
99 | }
100 |
101 | return (
102 | this.setState({ hovered: true })}
104 | onMouseLeave={() => this.setState({ hovered: false })}
105 | style={style}
106 | className={classes.join(" ")}
107 | >
108 |
112 | {guild.banner && (
113 |
123 |
127 |
131 |
132 | }
133 | />
134 | )}
135 |
136 |
137 | );
138 | }
139 | };
140 |
--------------------------------------------------------------------------------
/components/GuildProfileModal.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const {
7 | React,
8 | i18n: { Messages },
9 | Flux,
10 | getModule,
11 | } = require('powercord/webpack');
12 |
13 | const { ModalRoot } = getModule(['ModalRoot'], false);
14 |
15 | const GuildProfileHeader = require('./GuildProfileHeader');
16 | const GuildRelationships = require('./GuildRelationships');
17 | const GuildInfoBase = require('./GuildInfoBase');
18 |
19 | const { TabBar } = require('powercord/components');
20 |
21 | const GuildProfileSections = {
22 | GUILD_INFO: 'GUILD_INFO',
23 | FRIENDS: 'FRIENDS',
24 | BLOCKED_USERS: 'BLOCKED_USERS',
25 | };
26 |
27 | class GuildProfileTabBar extends React.PureComponent {
28 | constructor(props) {
29 | super(props);
30 |
31 | this.modules = {
32 | ...getModule(['topSection'], false),
33 | };
34 | }
35 | render() {
36 | const { setSection, section } = this.props;
37 |
38 | return (
39 |
40 |
46 |
50 | {Messages.GUILD_INFO}
51 |
52 |
56 | {Messages.FRIENDS_IN_GUILD}
57 |
58 |
62 | {Messages.BLOCKED_USERS_IN_GUILD}
63 |
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | class GuildProfileModal extends React.PureComponent {
71 | constructor(props) {
72 | super(props);
73 |
74 | this.modules = {
75 | ...getModule(['topSection'], false),
76 | };
77 |
78 | this.state = {
79 | section: GuildProfileSections.GUILD_INFO,
80 | };
81 | }
82 |
83 | async componentDidMount() {
84 | const { guild, getMemberCounts } = this.props;
85 | const memberData = await getMemberCounts(guild.id);
86 | this.setState({ counts: memberData });
87 | }
88 |
89 | render() {
90 | const { guild } = this.props;
91 | const { counts } = this.state;
92 |
93 | let section;
94 |
95 | switch (this.state.section) {
96 | case GuildProfileSections.GUILD_INFO:
97 | section = ;
98 | break;
99 | case GuildProfileSections.FRIENDS:
100 | section = (
101 |
105 | );
106 | break;
107 | case GuildProfileSections.BLOCKED_USERS:
108 | section = (
109 |
113 | );
114 | break;
115 | }
116 |
117 | return (
118 |
119 |
120 |
121 | this.setState({ section })}
123 | section={this.state.section}
124 | guild={guild}
125 | />
126 |
127 | {section}
128 |
129 | );
130 | }
131 | }
132 |
133 | module.exports = Flux.connectStoresAsync(
134 | [
135 | getModule(['getRelationships']),
136 | getModule(['getCurrentUser', 'getUser']),
137 | getModule(['isMember']),
138 | ],
139 | ([relationshipsStore, userStore, membersStore], compProps) => {
140 | // Its safe to assume if the module aboves were found that this one is also loaded
141 | const userFetcher = getModule(['getUser'], false);
142 | const relationships = relationshipsStore.getRelationships();
143 | const props = {
144 | friends: [],
145 | blocked: [],
146 | };
147 |
148 | for (const userId in relationships) {
149 | if (!membersStore.isMember(compProps.guild.id, userId)) {
150 | continue;
151 | }
152 |
153 | const relationshipType = relationships[userId];
154 | const user = userStore.getUser(userId);
155 | if (!user) {
156 | userFetcher.getUser(userId);
157 | continue;
158 | }
159 |
160 | if (relationshipType === 1) {
161 | props.friends.push(user);
162 | } else if (relationshipType === 2) {
163 | props.blocked.push(user);
164 | }
165 | }
166 | return props;
167 | }
168 | )(GuildProfileModal);
169 |
--------------------------------------------------------------------------------
/components/GuildInfoBase.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const {
7 | React,
8 | i18n: { Messages },
9 | getModule,
10 | getModuleByDisplayName,
11 | } = require('powercord/webpack');
12 |
13 | const { Flex, Text } = require('powercord/components');
14 | const AsyncComponent = require('powercord/components/AsyncComponent');
15 |
16 | const FormSection = AsyncComponent.from(getModuleByDisplayName('FormSection'));
17 | const Anchor = AsyncComponent.from(getModuleByDisplayName('Anchor'));
18 | const Mention = AsyncComponent.from(getModuleByDisplayName('Mention'));
19 |
20 | const { AdvancedScrollerThin } = getModule(['AdvancedScrollerThin'], false);
21 |
22 | const ContextMenu = getModule(['closeContextMenu'], false);
23 | const { close } = require('powercord/modal');
24 |
25 | const UserProfileModalActionCreators = getModule(
26 | ['openUserProfileModal'],
27 | false
28 | );
29 |
30 | const GuildExplicitContentFilterTypes = [
31 | 'EXPLICIT_CONTENT_FILTER_DISABLED',
32 | 'EXPLICIT_CONTENT_FILTER_MEDIUM',
33 | 'EXPLICIT_CONTENT_FILTER_HIGH',
34 | ];
35 |
36 | const GuildVerificationLevels = [
37 | 'VERIFICATION_LEVEL_NONE',
38 | 'VERIFICATION_LEVEL_LOW',
39 | 'VERIFICATION_LEVEL_MEDIUM',
40 | 'VERIFICATION_LEVEL_HIGH',
41 | 'VERIFICATION_LEVEL_VERY_HIGH',
42 | ];
43 |
44 | const NsfwLevels = [
45 | 'NSFW_LEVEL_DEFAULT',
46 | 'NSFW_LEVEL_EXPLICIT',
47 | 'NSFW_LEVEL_SAFE',
48 | 'NSFW_LEVEL_AGE_RESTRICTED',
49 | ];
50 |
51 | class Section extends React.PureComponent {
52 | constructor(props) {
53 | super(props);
54 |
55 | this.modules = {
56 | marginBottom8: getModule(['marginBottom8'], false).marginBottom8,
57 | };
58 | }
59 |
60 | render() {
61 | const { children, title } = this.props;
62 |
63 | if (!children) {
64 | return null;
65 | }
66 |
67 | return (
68 |
73 | {children}
74 |
75 | );
76 | }
77 | }
78 |
79 | module.exports = class GuildInfoBase extends React.PureComponent {
80 | constructor(props) {
81 | super(props);
82 |
83 | this.modules = {
84 | ...getModule(['topSection'], false),
85 | ...getModule(['infoScroller'], false),
86 | ...getModule(['emptyIconFriends', 'empty'], false),
87 | };
88 |
89 | this.state = {};
90 | }
91 |
92 | async componentDidMount() {
93 | const { getUser } = getModule(['getUser'], false);
94 | const { ownerId } = this.props.guild;
95 |
96 | const { getSerializedState } = await getModule(['getSerializedState']);
97 | const { getRegisteredExperiments } = await getModule([
98 | 'getRegisteredExperiments',
99 | ]);
100 | const { v3: murmurHash } = await getModule(['v3']);
101 |
102 | const { loadedGuildExperiments } = getSerializedState();
103 | const registeredExperiments = getRegisteredExperiments();
104 |
105 | const object = {};
106 |
107 | Object.keys(registeredExperiments).forEach(
108 | (experiment) => (object[murmurHash(experiment)] = experiment)
109 | );
110 | Object.entries(loadedGuildExperiments).forEach(
111 | ([key, value]) =>
112 | (loadedGuildExperiments[object[key]] = { ...value, hashKey: key })
113 | );
114 |
115 | const enabledExperiments = Object.keys(loadedGuildExperiments).filter(
116 | (k) => loadedGuildExperiments[k].hashKey != null && k != 'undefined'
117 | );
118 | const enabledGuildExperiments = {};
119 | const y = {};
120 | Object.keys(object).forEach((key) => {
121 | y[object[key]] = key;
122 | });
123 | enabledExperiments.forEach((k) => {
124 | enabledGuildExperiments[k] = loadedGuildExperiments[y[k]];
125 | });
126 |
127 | const { id } = this.props.guild;
128 | const experimentsEnabledForGuild = [];
129 |
130 | enabledExperiments.forEach((k) => {
131 | const d = enabledGuildExperiments[k];
132 | if (d.overrides[id] != undefined) {
133 | experimentsEnabledForGuild.push(k);
134 | }
135 | });
136 |
137 | this.setState({
138 | owner: await getUser(ownerId),
139 | experiments: experimentsEnabledForGuild,
140 | });
141 | }
142 |
143 | handleContextMenu(event) {
144 | ContextMenu.openContextMenu(event, () => {});
145 | }
146 |
147 | render() {
148 | const moment = getModule(['momentProperties'], false);
149 | const { extractTimestamp } = getModule(['extractTimestamp'], false);
150 | const { guild } = this.props;
151 | const {
152 | vanityURLCode,
153 | description,
154 | verificationLevel,
155 | explicitContentFilter,
156 | nsfwLevel,
157 | } = guild;
158 | const { owner, experiments } = this.state;
159 |
160 | const streamerMode = getModule(
161 | ['hidePersonalInformation'],
162 | false
163 | ).hidePersonalInformation;
164 |
165 | if (streamerMode) {
166 | return (
167 |
168 |
169 |
170 | {Messages.STREAMER_MODE_ENABLED}
171 |
172 |
173 | );
174 | }
175 |
176 | return (
177 |
181 |
182 |
183 | {owner ? (
184 | this.handleContextMenu(e)}
187 | onClick={() => {
188 | close();
189 | UserProfileModalActionCreators.openUserProfileModal({
190 | userId: owner.id,
191 | });
192 | }}
193 | >
194 | @{owner.username}#{owner.discriminator}
195 |
196 | ) : (
197 | `${Messages.LOADING}...`
198 | )}
199 |
200 |
201 | {description}
202 |
203 | {vanityURLCode && (
204 |
205 |
206 | discord.gg/{vanityURLCode}
207 |
208 |
209 | )}
210 |
211 | {moment(extractTimestamp(guild.id)).format('LLL')}
212 |
213 |
214 | {moment(guild.joinedAt).format('LLL')}
215 |
216 |
217 | {Messages[GuildVerificationLevels[verificationLevel]]}
218 |
219 |
220 | {Messages[GuildExplicitContentFilterTypes[explicitContentFilter]]}
221 |
222 |
223 | {guild.premiumSubscriberCount}
224 |
225 |
226 | {guild.premiumTier}
227 |
228 |
229 | {guild.preferredLocale}
230 |
231 |
232 | {Messages[NsfwLevels[nsfwLevel]]}
233 |
234 |
235 | {experiments && experiments.length > 0
236 | ? experiments.join(', ')
237 | : Messages.NONE}
238 |
239 |
240 |
241 | );
242 | }
243 | };
244 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 - 2021 NurMarvin (Marvin Witt)
3 | * Licensed under the Open Software License version 3.0
4 | */
5 |
6 | const { Plugin } = require("powercord/entities");
7 | const { inject, uninject } = require("powercord/injector");
8 | const {
9 | React,
10 | getModule,
11 | getModuleByDisplayName,
12 | FluxDispatcher,
13 | subscribe,
14 | i18n: { Messages },
15 | } = require("powercord/webpack");
16 | const { open } = require("powercord/modal");
17 | const { findInReactTree } = require("powercord/util");
18 | const i18n = require("./i18n");
19 |
20 | const GuildProfileModal = require("./components/GuildProfileModal");
21 | const GuildProfileIcon = require("./components/GuildProfileIcon");
22 |
23 | const memberCountsStore = require("./memberCountsStore/store");
24 | const memberCountsActions = require("./memberCountsStore/actions");
25 |
26 | const { getCurrentUser } = getModule(["getCurrentUser"], false);
27 |
28 | module.exports = class GuildProfile extends Plugin {
29 | async startPlugin() {
30 | powercord.api.i18n.loadAllStrings(i18n);
31 | this.loadStylesheet("styles.scss");
32 | this._injectContextMenu();
33 | this._injectMenu();
34 |
35 | this.handleMemberListUpdate = this.handleMemberListUpdate.bind(this);
36 |
37 | FluxDispatcher.subscribe(
38 | "GUILD_MEMBER_LIST_UPDATE",
39 | this.handleMemberListUpdate
40 | );
41 | }
42 |
43 | handleMemberListUpdate(memberListUpdate) {
44 | this.updateMemberCounts(memberListUpdate);
45 | }
46 |
47 | getMemberCounts(id) {
48 | return new Promise((resolve) => {
49 | const memberCounts = memberCountsStore.getMemberCounts(id);
50 |
51 | // If the member count is in the Flux store just send that data
52 | if (memberCounts) {
53 | resolve(memberCounts);
54 | return;
55 | }
56 |
57 | const { requestMembers } = getModule(["requestMembers"], false);
58 | requestMembers(id);
59 |
60 | const updateMemberCounts = (memberListUpdate) => {
61 | return this.updateMemberCounts(memberListUpdate);
62 | };
63 |
64 | function onReceived(memberListUpdate) {
65 | if (memberListUpdate.guildId === id) {
66 | resolve(updateMemberCounts(memberListUpdate));
67 | }
68 | }
69 |
70 | FluxDispatcher.subscribe("GUILD_MEMBER_LIST_UPDATE", onReceived);
71 | });
72 | }
73 |
74 | updateMemberCounts(memberListUpdate) {
75 | const { guildId, memberCount, groups } = memberListUpdate;
76 | const onlineCount = groups
77 | .map((group) => (group.id != "offline" ? group.count : 0))
78 | .reduce((a, b) => {
79 | return a + b;
80 | }, 0);
81 | const memberCounts = { guildId, memberCount, onlineCount };
82 |
83 | memberCountsActions.updateMemberCounts(memberCounts);
84 | return memberCounts;
85 | }
86 |
87 | async _injectContextMenu() {
88 | const Menu = await getModule(["MenuItem"]);
89 | const getMemberCounts = (guildId) => {
90 | return this.getMemberCounts(guildId);
91 | };
92 |
93 | subscribe(x => x.default?.displayName === "GuildContextMenuWrapper", GuildContextMenuWrapper => {
94 | inject("guild-profile-context-menu", GuildContextMenuWrapper, "default", ([{ guild }], res) => {
95 | const renderContextMenu = res.props.children.type;
96 | res.props.children.type = (props) => {
97 | const contextMenu = renderContextMenu(props);
98 | contextMenu.props.children.unshift(
99 | React.createElement(
100 | Menu.MenuGroup,
101 | null,
102 | React.createElement(Menu.MenuItem, {
103 | id: "guild-profile",
104 | label: Messages.GUILD_PROFILE,
105 | action: () =>
106 | this._openModalHandler(() =>
107 | React.createElement(GuildProfileModal, {
108 | guild,
109 | section: "GUILD_INFO",
110 | getMemberCounts,
111 | })
112 | ),
113 | })
114 | )
115 | );
116 | return contextMenu;
117 | };
118 | return res;
119 | })
120 | });
121 | }
122 |
123 | async _injectMenu() {
124 | const id = "guild-profile";
125 | const Menu = await getModule(["MenuItem"]);
126 | const { getGuild } = await getModule(["getGuild"]);
127 | const { getGuildId } = await getModule(["getLastSelectedGuildId"]);
128 |
129 | const getMemberCounts = (guildId) => {
130 | return this.getMemberCounts(guildId);
131 | };
132 |
133 | inject("guild-profile-menu", Menu, "default", ([{ children }], res) => {
134 | const menuId = res.props.children.props.id;
135 |
136 | if (menuId !== "guild-header-popout")
137 | return res;
138 |
139 | if (!findInReactTree(res, (c) => c.props && c.props.id == id)) {
140 | children.unshift(
141 | React.createElement(
142 | Menu.MenuGroup,
143 | null,
144 | React.createElement(Menu.MenuItem, {
145 | id,
146 | label: Messages.GUILD_PROFILE,
147 | icon: () => React.createElement(GuildProfileIcon),
148 | action: () =>
149 | this._openModalHandler(() =>
150 | React.createElement(GuildProfileModal, {
151 | guild: getGuild(getGuildId()),
152 | section: "GUILD_INFO",
153 | getMemberCounts,
154 | })
155 | ),
156 | })
157 | )
158 | );
159 | }
160 | return res;
161 | });
162 |
163 | Menu.default.displayName = "Menu";
164 | }
165 |
166 | _openModalHandler(element) {
167 | const { openUserProfileModal } = getModule(["openUserProfileModal"], false);
168 | const module = getModule(["openModalLazy"], false);
169 |
170 | if (getModuleByDisplayName("UserProfileModal", false) !== null) {
171 | open(element);
172 | return;
173 | }
174 |
175 | // So, the modules that are needed to render our modal are in another chunk, which is still not loaded.
176 | // We call a function that loads modules for UserProfileModal,
177 | // but the user will not see the final UserProfileModal -
178 | // since it will be replaced at the last stage with our element
179 |
180 | const { ModalRoot } = getModule(["ModalRoot"], false);
181 | const userId = getCurrentUser().id;
182 |
183 | inject(
184 | "guild-profile-open-modal-lazy",
185 | module,
186 | "openModalLazy",
187 | ([initLazyRender]) => {
188 | const warpInitLazyRender = () => {
189 | const lazyRender = initLazyRender();
190 |
191 | return new Promise(async (resolve) => {
192 | const render = await lazyRender;
193 |
194 | resolve((event) => {
195 | const res = render(event);
196 |
197 | if (res?.type?.displayName === "UserProfileModal") {
198 | const { props } = res;
199 | const { root } = getModule(["root", "body"], false);
200 |
201 | if (props.guildId === "@me" && props.user.id === userId) {
202 | if (props.transitionState === 3) {
203 | // = close modal
204 | uninject("guild-profile-open-modal-lazy");
205 | }
206 |
207 | return React.createElement(ModalRoot, {
208 | transitionState: props.transitionState,
209 | className: root,
210 | children: element(),
211 | });
212 | }
213 | }
214 | return res;
215 | });
216 | });
217 | };
218 |
219 | return [warpInitLazyRender];
220 | },
221 | true
222 | );
223 |
224 | openUserProfileModal({ userId });
225 | }
226 |
227 | pluginWillUnload() {
228 | uninject("guild-profile-context-menu");
229 | uninject("guild-profile-menu");
230 | uninject("guild-profile-open-modal-lazy");
231 | FluxDispatcher.unsubscribe(
232 | "GUILD_MEMBER_LIST_UPDATE",
233 | this.handleMemberListUpdate
234 | );
235 | }
236 | };
237 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Open Software License ("OSL") v. 3.0
2 |
3 | This Open Software License (the "License") applies to any original work of
4 | authorship (the "Original Work") whose owner (the "Licensor") has placed the
5 | following licensing notice adjacent to the copyright notice for the Original
6 | Work:
7 |
8 | Licensed under the Open Software License version 3.0
9 |
10 | 1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free,
11 | non-exclusive, sublicensable license, for the duration of the copyright, to do
12 | the following:
13 |
14 | a) to reproduce the Original Work in copies, either alone or as part of a
15 | collective work;
16 |
17 | b) to translate, adapt, alter, transform, modify, or arrange the Original
18 | Work, thereby creating derivative works ("Derivative Works") based upon the
19 | Original Work;
20 |
21 | c) to distribute or communicate copies of the Original Work and Derivative
22 | Works to the public, with the proviso that copies of Original Work or
23 | Derivative Works that You distribute or communicate shall be licensed under
24 | this Open Software License;
25 |
26 | d) to perform the Original Work publicly; and
27 |
28 | e) to display the Original Work publicly.
29 |
30 | 2) Grant of Patent License. Licensor grants You a worldwide, royalty-free,
31 | non-exclusive, sublicensable license, under patent claims owned or controlled
32 | by the Licensor that are embodied in the Original Work as furnished by the
33 | Licensor, for the duration of the patents, to make, use, sell, offer for sale,
34 | have made, and import the Original Work and Derivative Works.
35 |
36 | 3) Grant of Source Code License. The term "Source Code" means the preferred
37 | form of the Original Work for making modifications to it and all available
38 | documentation describing how to modify the Original Work. Licensor agrees to
39 | provide a machine-readable copy of the Source Code of the Original Work along
40 | with each copy of the Original Work that Licensor distributes. Licensor
41 | reserves the right to satisfy this obligation by placing a machine-readable
42 | copy of the Source Code in an information repository reasonably calculated to
43 | permit inexpensive and convenient access by You for as long as Licensor
44 | continues to distribute the Original Work.
45 |
46 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names
47 | of any contributors to the Original Work, nor any of their trademarks or
48 | service marks, may be used to endorse or promote products derived from this
49 | Original Work without express prior permission of the Licensor. Except as
50 | expressly stated herein, nothing in this License grants any license to
51 | Licensor's trademarks, copyrights, patents, trade secrets or any other
52 | intellectual property. No patent license is granted to make, use, sell, offer
53 | for sale, have made, or import embodiments of any patent claims other than the
54 | licensed claims defined in Section 2. No license is granted to the trademarks
55 | of Licensor even if such marks are included in the Original Work. Nothing in
56 | this License shall be interpreted to prohibit Licensor from licensing under
57 | terms different from this License any Original Work that Licensor otherwise
58 | would have a right to license.
59 |
60 | 5) External Deployment. The term "External Deployment" means the use,
61 | distribution, or communication of the Original Work or Derivative Works in any
62 | way such that the Original Work or Derivative Works may be used by anyone
63 | other than You, whether those works are distributed or communicated to those
64 | persons or made available as an application intended for use over a network.
65 | As an express condition for the grants of license hereunder, You must treat
66 | any External Deployment by You of the Original Work or a Derivative Work as a
67 | distribution under section 1(c).
68 |
69 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative
70 | Works that You create, all copyright, patent, or trademark notices from the
71 | Source Code of the Original Work, as well as any notices of licensing and any
72 | descriptive text identified therein as an "Attribution Notice." You must cause
73 | the Source Code for any Derivative Works that You create to carry a prominent
74 | Attribution Notice reasonably calculated to inform recipients that You have
75 | modified the Original Work.
76 |
77 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
78 | the copyright in and to the Original Work and the patent rights granted herein
79 | by Licensor are owned by the Licensor or are sublicensed to You under the
80 | terms of this License with the permission of the contributor(s) of those
81 | copyrights and patent rights. Except as expressly stated in the immediately
82 | preceding sentence, the Original Work is provided under this License on an "AS
83 | IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without
84 | limitation, the warranties of non-infringement, merchantability or fitness for
85 | a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK
86 | IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
87 | License. No license to the Original Work is granted by this License except
88 | under this disclaimer.
89 |
90 | 8) Limitation of Liability. Under no circumstances and under no legal theory,
91 | whether in tort (including negligence), contract, or otherwise, shall the
92 | Licensor be liable to anyone for any indirect, special, incidental, or
93 | consequential damages of any character arising as a result of this License or
94 | the use of the Original Work including, without limitation, damages for loss
95 | of goodwill, work stoppage, computer failure or malfunction, or any and all
96 | other commercial damages or losses. This limitation of liability shall not
97 | apply to the extent applicable law prohibits such limitation.
98 |
99 | 9) Acceptance and Termination. If, at any time, You expressly assented to this
100 | License, that assent indicates your clear and irrevocable acceptance of this
101 | License and all of its terms and conditions. If You distribute or communicate
102 | copies of the Original Work or a Derivative Work, You must make a reasonable
103 | effort under the circumstances to obtain the express assent of recipients to
104 | the terms of this License. This License conditions your rights to undertake
105 | the activities listed in Section 1, including your right to create Derivative
106 | Works based upon the Original Work, and doing so without honoring these terms
107 | and conditions is prohibited by copyright law and international treaty.
108 | Nothing in this License is intended to affect copyright exceptions and
109 | limitations (including "fair use" or "fair dealing"). This License shall
110 | terminate immediately and You may no longer exercise any of the rights granted
111 | to You by this License upon your failure to honor the conditions in Section
112 | 1(c).
113 |
114 | 10) Termination for Patent Action. This License shall terminate automatically
115 | and You may no longer exercise any of the rights granted to You by this
116 | License as of the date You commence an action, including a cross-claim or
117 | counterclaim, against Licensor or any licensee alleging that the Original Work
118 | infringes a patent. This termination provision shall not apply for an action
119 | alleging patent infringement by combinations of the Original Work with other
120 | software or hardware.
121 |
122 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
123 | License may be brought only in the courts of a jurisdiction wherein the
124 | Licensor resides or in which Licensor conducts its primary business, and under
125 | the laws of that jurisdiction excluding its conflict-of-law provisions. The
126 | application of the United Nations Convention on Contracts for the
127 | International Sale of Goods is expressly excluded. Any use of the Original
128 | Work outside the scope of this License or after its termination shall be
129 | subject to the requirements and penalties of copyright or patent law in the
130 | appropriate jurisdiction. This section shall survive the termination of this
131 | License.
132 |
133 | 12) Attorneys' Fees. In any action to enforce the terms of this License or
134 | seeking damages relating thereto, the prevailing party shall be entitled to
135 | recover its costs and expenses, including, without limitation, reasonable
136 | attorneys' fees and costs incurred in connection with such action, including
137 | any appeal of such action. This section shall survive the termination of this
138 | License.
139 |
140 | 13) Miscellaneous. If any provision of this License is held to be
141 | unenforceable, such provision shall be reformed only to the extent necessary
142 | to make it enforceable.
143 |
144 | 14) Definition of "You" in This License. "You" throughout this License,
145 | whether in upper or lower case, means an individual or a legal entity
146 | exercising rights under, and complying with all of the terms of, this License.
147 | For legal entities, "You" includes any entity that controls, is controlled by,
148 | or is under common control with you. For purposes of this definition,
149 | "control" means (i) the power, direct or indirect, to cause the direction or
150 | management of such entity, whether by contract or otherwise, or (ii) ownership
151 | of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
152 | ownership of such entity.
153 |
154 | 15) Right to Use. You may use the Original Work in all ways not otherwise
155 | restricted or conditioned by this License or by law, and Licensor promises not
156 | to interfere with or be responsible for such uses by You.
157 |
158 | 16) Modification of This License. This License is Copyright © 2005 Lawrence
159 | Rosen. Permission is granted to copy, distribute, or communicate this License
160 | without modification. Nothing in this License permits You to modify this
161 | License as applied to the Original Work or to Derivative Works. However, You
162 | may modify the text of this License and copy, distribute or communicate your
163 | modified version (the "Modified License") and apply it to other original works
164 | of authorship subject to the following conditions: (i) You may not indicate in
165 | any way that your Modified License is the "Open Software License" or "OSL" and
166 | you may not use those names in the name of your Modified License; (ii) You
167 | must replace the notice specified in the first paragraph above with the notice
168 | "Licensed under " or with a notice of your own
169 | that is not confusingly similar to the notice in this License; and (iii) You
170 | may not claim that your original works are open source software unless your
171 | Modified License has been approved by Open Source Initiative (OSI) and You
172 | comply with its license review and certification process.
173 |
--------------------------------------------------------------------------------
/components/Icons.jsx:
--------------------------------------------------------------------------------
1 | const { React } = require("powercord/webpack");
2 |
3 | module.exports = {
4 | AUTO_MODERATION: React.memo((props) => (
5 |
11 | )),
12 | ANIMATED_BANNER: React.memo((props) => (
13 |
25 | )),
26 | ANIMATED_ICON: React.memo((props) => (
27 |
35 | )),
36 | BANNER: React.memo((props) => (
37 |
43 | )),
44 | BOT_DEVELOPER_EARLY_ACCESS: React.memo((props) => (
45 |
51 | )),
52 | COMMERCE: React.memo((props) => (
53 |
59 | )),
60 | COMMUNITY: React.memo((props) => (
61 |
67 | )),
68 | CREATOR_MONETIZABLE: React.memo((props) => (
69 |
76 | )),
77 | DISCOVERABLE: React.memo((props) => (
78 |
91 | )),
92 | ENABLED_DISCOVERABLE_BEFORE: React.memo((props) => (
93 |
103 | )),
104 | EXPOSED_TO_ACTIVITIES_WTP_EXPERIMENT: React.memo((props) => (
105 |
129 | )),
130 | FEATURABLE: React.memo((props) => (
131 |
137 | )),
138 | GUILD_HOME_TEST: React.memo((props) => (
139 |
145 | )),
146 | HAD_EARLY_ACTIVITIES_ACCESS: React.memo((props) => (
147 |
167 | )),
168 | HAS_DIRECTORY_ENTRY: React.memo((props) => (
169 |
175 | )),
176 | INVITES_DISABLED: React.memo((props) => (
177 |
199 | )),
200 | INVITE_SPLASH: React.memo((props) => (
201 |
211 | )),
212 | INTERNAL_EMPLOYEE_ONLY: React.memo((props) => (
213 |
227 | )),
228 | MEMBER_PROFILES: React.memo((props) => (
229 |
235 | )),
236 | MEMBER_VERIFICATION_GATE_ENABLED: React.memo((props) => (
237 |
243 | )),
244 | NEWS: React.memo((props) => (
245 |
251 | )),
252 | NEW_THREAD_PERMISSIONS: React.memo((props) => (
253 |
263 | )),
264 | PREMIUM_TIER_3_OVERRIDE: React.memo((props) => (
265 |
275 | )),
276 | PREVIEW_ENABLED: React.memo((props) => (
277 |
287 | )),
288 | PRIVATE_THREADS: React.memo((props) => (
289 |
305 | )),
306 | RELAY_ENABLED: React.memo((props) => (
307 |
313 | )),
314 | ROLE_ICONS: React.memo((props) => (
315 |
325 | )),
326 | ROLE_SUBSCRIPTIONS_ENABLED: React.memo((props) => (
327 |
338 | )),
339 | ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE: React.memo((props) => (
340 |
346 | )),
347 | SEVEN_DAY_THREAD_ARCHIVE: React.memo((props) => (
348 |
362 | )),
363 | TEXT_IN_VOICE_ENABLED: React.memo((props) => (
364 |
376 | )),
377 | THREADS_ENABLED: React.memo((props) => (
378 |
388 | )),
389 | THREADS_ENABLED_TESTING: React.memo((props) => (
390 |
400 | )),
401 | THREE_DAY_THREAD_ARCHIVE: React.memo((props) => (
402 |
416 | )),
417 | VANITY_URL: React.memo((props) => (
418 |
431 | )),
432 | VIP_REGIONS: React.memo((props) => (
433 |
445 | )),
446 | WELCOME_SCREEN_ENABLED: React.memo((props) => (
447 |
453 | )),
454 |
455 | // Placeholder for unknown features
456 | UNKNOWN: React.memo((props) => (
457 |
463 | )),
464 | };
465 |
--------------------------------------------------------------------------------