mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 17:58:01 +00:00
Draft Ranking on ImageView
- add an option to show draft card rankings
This commit is contained in:
@@ -58,10 +58,10 @@ public class FDeckViewer extends FDialog {
|
||||
private FDeckViewer(final Deck deck0) {
|
||||
this.deck = deck0;
|
||||
this.setTitle(deck.getName());
|
||||
this.cardManager = new CardManager(null, false, false) {
|
||||
this.cardManager = new CardManager(null, false, false, false) {
|
||||
@Override //show hovered card in Image View in dialog instead of main Detail/Picture panes
|
||||
protected ImageView<PaperCard> createImageView(final ItemManagerModel<PaperCard> model0) {
|
||||
return new ImageView<PaperCard>(this, model0) {
|
||||
return new ImageView<PaperCard>(this, model0, false) {
|
||||
@Override
|
||||
protected void showHoveredItem(PaperCard item) {
|
||||
final CardView card = CardView.getCardForUi(item);
|
||||
|
||||
@@ -34,8 +34,8 @@ public class CardManager extends ItemManager<PaperCard> {
|
||||
|
||||
private boolean QuestMode;
|
||||
|
||||
public CardManager(final CDetailPicture cDetailPicture, final boolean wantUnique0, final boolean qm) {
|
||||
super(PaperCard.class, cDetailPicture, wantUnique0);
|
||||
public CardManager(final CDetailPicture cDetailPicture, final boolean wantUnique0, final boolean qm, boolean sr) {
|
||||
super(PaperCard.class, cDetailPicture, wantUnique0, sr);
|
||||
QuestMode = qm;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public final class DeckManager extends ItemManager<DeckProxy> implements IHasGam
|
||||
* @param gt
|
||||
*/
|
||||
public DeckManager(final GameType gt, final CDetailPicture cDetailPicture) {
|
||||
super(DeckProxy.class, cDetailPicture, true);
|
||||
super(DeckProxy.class, cDetailPicture, true, false);
|
||||
this.gameType = gt;
|
||||
|
||||
this.addSelectionListener(new ListSelectionListener() {
|
||||
|
||||
@@ -73,6 +73,7 @@ public abstract class ItemManager<T extends InventoryItem> extends JPanel implem
|
||||
private boolean alwaysNonUnique = false;
|
||||
private boolean allowMultipleSelections = false;
|
||||
private boolean hideFilters = false;
|
||||
private boolean showRanking = false;
|
||||
private UiCommand itemActivateCommand;
|
||||
private ContextMenuBuilder contextMenuBuilder;
|
||||
private final Class<T> genericType;
|
||||
@@ -125,7 +126,8 @@ public abstract class ItemManager<T extends InventoryItem> extends JPanel implem
|
||||
/**
|
||||
* ItemManager Constructor
|
||||
*/
|
||||
protected ItemManager(final Class<T> genericType0, final CDetailPicture cDetailPicture, final boolean wantUnique0) {
|
||||
protected ItemManager(final Class<T> genericType0, final CDetailPicture cDetailPicture, final boolean wantUnique0, final boolean showRanking) {
|
||||
this.showRanking = showRanking;
|
||||
this.cDetailPicture = cDetailPicture;
|
||||
this.genericType = genericType0;
|
||||
this.wantUnique = wantUnique0;
|
||||
@@ -143,7 +145,7 @@ public abstract class ItemManager<T extends InventoryItem> extends JPanel implem
|
||||
}
|
||||
|
||||
protected ImageView<T> createImageView(final ItemManagerModel<T> model0) {
|
||||
return new ImageView<>(this, model0);
|
||||
return new ImageView<>(this, model0, this.showRanking);
|
||||
}
|
||||
|
||||
public final CDetailPicture getCDetailPicture() {
|
||||
|
||||
@@ -17,7 +17,7 @@ public final class SpellShopManager extends ItemManager<InventoryItem> {
|
||||
private final boolean wantUnique02;
|
||||
|
||||
public SpellShopManager(final CDetailPicture cDetailPicture, final boolean wantUnique0) {
|
||||
super(InventoryItem.class, cDetailPicture, wantUnique0);
|
||||
super(InventoryItem.class, cDetailPicture, wantUnique0, false);
|
||||
cDetailPicture2 = cDetailPicture;
|
||||
wantUnique02 = wantUnique0;
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public final class SpellShopManager extends ItemManager<InventoryItem> {
|
||||
|
||||
@Override
|
||||
protected void buildAddFilterMenu(JMenu menu) {
|
||||
CardManager CM = new CardManager(cDetailPicture2, wantUnique02, true);
|
||||
CardManager CM = new CardManager(cDetailPicture2, wantUnique02, true, false);
|
||||
CM.buildAddFilterMenu(menu, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import forge.screens.match.controllers.CDetailPicture;
|
||||
|
||||
public class TokenManager extends ItemManager<PaperToken> {
|
||||
public TokenManager(final CDetailPicture cDetailPicture, final boolean wantUnique0) {
|
||||
super(PaperToken.class, cDetailPicture, wantUnique0);
|
||||
super(PaperToken.class, cDetailPicture, wantUnique0, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.deck.DeckProxy;
|
||||
import forge.deck.io.DeckPreferences;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
import forge.gamemodes.limited.CardRanker;
|
||||
import forge.gui.framework.ILocalRepaint;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.InventoryItem;
|
||||
@@ -145,7 +146,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
||||
private final FComboBoxWrapper<Object> cbPileByOptions = new FComboBoxWrapper<>();
|
||||
private final FComboBoxWrapper<Integer> cbColumnCount = new FComboBoxWrapper<>();
|
||||
|
||||
public ImageView(final ItemManager<T> itemManager0, final ItemManagerModel<T> model0) {
|
||||
public ImageView(final ItemManager<T> itemManager0, final ItemManagerModel<T> model0, final boolean showRanking) {
|
||||
super(itemManager0, model0);
|
||||
|
||||
SItemManagerUtil.populateImageViewOptions(itemManager0, cbGroupByOptions, cbPileByOptions);
|
||||
@@ -192,6 +193,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
||||
|
||||
//setup display
|
||||
display = new CardViewDisplay();
|
||||
display.setShowRanking(showRanking);
|
||||
display.addMouseListener(new FMouseAdapter() {
|
||||
@Override
|
||||
public void onLeftMouseDown(MouseEvent e) {
|
||||
@@ -1000,11 +1002,16 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private class CardViewDisplay extends JPanel implements ILocalRepaint {
|
||||
boolean showRanking = false;
|
||||
private CardViewDisplay() {
|
||||
setOpaque(false);
|
||||
setFocusable(true);
|
||||
}
|
||||
|
||||
public void setShowRanking(boolean showRanking) {
|
||||
this.showRanking = showRanking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repaintSelf() {
|
||||
repaint(getVisibleRect());
|
||||
@@ -1223,6 +1230,32 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
||||
}
|
||||
CardPanel.drawFoilEffect(g, card, bounds.x, bounds.y, bounds.width, bounds.height, borderSize);
|
||||
}
|
||||
//draw draft ranking
|
||||
if (showRanking && FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_OVERLAY_DRAFT_RANKING)) {
|
||||
double score = CardRanker.getRawScore((PaperCard) item);
|
||||
int draftRank = score <= 0 ? 0 : score > 99 ? 99 : (int) Math.round(CardRanker.getRawScore((PaperCard) item));
|
||||
String value = String.valueOf(draftRank);
|
||||
g.setColor(Color.white);
|
||||
Shape clip = g.getClip();
|
||||
g.setClip(bounds);
|
||||
int scale = (int)(g.getFontMetrics().getHeight()*3.5f);
|
||||
int h = (int)(g.getFontMetrics().getHeight()/3.5f);
|
||||
int w = g.getFontMetrics().stringWidth(value);
|
||||
int x = (int)(bounds.x+bounds.width/2);
|
||||
int y = (int)(bounds.y+bounds.height/2);
|
||||
if (draftRank >= 90)
|
||||
FSkin.drawImage(g, FSkin.getImage(FSkinProp.IMG_DRAFTRANK_S), x-scale/2, y-h-scale/2, scale, scale);
|
||||
else if (draftRank >= 80 && draftRank <= 89)
|
||||
FSkin.drawImage(g, FSkin.getImage(FSkinProp.IMG_DRAFTRANK_A), x-scale/2, y-h-scale/2, scale, scale);
|
||||
else if (draftRank >= 60 && draftRank <= 79)
|
||||
FSkin.drawImage(g, FSkin.getImage(FSkinProp.IMG_DRAFTRANK_B), x-scale/2, y-h-scale/2, scale, scale);
|
||||
else if (draftRank >= 25 && draftRank <= 59)
|
||||
FSkin.drawImage(g, FSkin.getImage(FSkinProp.IMG_DRAFTRANK_C), x-scale/2, y-h-scale/2, scale, scale);
|
||||
else
|
||||
FSkin.drawImage(g, FSkin.getImage(FSkinProp.IMG_DRAFTRANK_D), x-scale/2, y-h-scale/2, scale, scale);
|
||||
g.drawString(value, x-w/2, y);
|
||||
g.setClip(clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ public final class CEditorCommander extends CDeckEditor<Deck> {
|
||||
customCards.getAllCardsNoAlt()), PaperCard.class);
|
||||
}
|
||||
|
||||
CardManager catalogManager = new CardManager(getCDetailPicture(), true, false);
|
||||
CardManager deckManager = new CardManager(getCDetailPicture(), false, false);
|
||||
CardManager catalogManager = new CardManager(getCDetailPicture(), true, false, false);
|
||||
CardManager deckManager = new CardManager(getCDetailPicture(), false, false, false);
|
||||
deckManager.setAlwaysNonUnique(true);
|
||||
|
||||
catalogManager.setCaption("Catalog");
|
||||
|
||||
@@ -131,8 +131,8 @@ public final class CEditorConstructed extends CDeckEditor<Deck> {
|
||||
default:
|
||||
}
|
||||
|
||||
catalogManager = new CardManager(getCDetailPicture(), wantUnique, false);
|
||||
deckManager = new CardManager(getCDetailPicture(), false, false);
|
||||
catalogManager = new CardManager(getCDetailPicture(), wantUnique, false, false);
|
||||
deckManager = new CardManager(getCDetailPicture(), false, false, false);
|
||||
deckManager.setAlwaysNonUnique(true);
|
||||
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
@@ -76,8 +76,8 @@ public class CEditorDraftingProcess extends ACEditorBase<PaperCard, DeckGroup> {
|
||||
public CEditorDraftingProcess(final CDetailPicture cDetailPicture0) {
|
||||
super(FScreen.DRAFTING_PROCESS, cDetailPicture0, GameType.Draft);
|
||||
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, false);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false);
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, false, true);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false, true);
|
||||
|
||||
//hide filters and options panel so more of pack is visible by default
|
||||
catalogManager.setHideViewOptions(1, true);
|
||||
|
||||
@@ -85,8 +85,8 @@ public final class CEditorLimited extends CDeckEditor<DeckGroup> {
|
||||
public CEditorLimited(final IStorage<DeckGroup> deckMap0, final FScreen screen0, final CDetailPicture cDetailPicture0) {
|
||||
super(screen0, cDetailPicture0, GameType.Sealed);
|
||||
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, false);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false);
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, false, FScreen.DECK_EDITOR_DRAFT.equals(screen0));
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false, FScreen.DECK_EDITOR_DRAFT.equals(screen0));
|
||||
|
||||
catalogManager.setCaption("Sideboard");
|
||||
|
||||
|
||||
@@ -123,8 +123,8 @@ public final class CEditorQuest extends CDeckEditor<Deck> {
|
||||
|
||||
this.questData = questData0;
|
||||
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, true);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, true);
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, true, false);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, true, false);
|
||||
|
||||
catalogManager.setCaption("Quest Inventory");
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ public class CEditorQuestDraftingProcess extends ACEditorBase<PaperCard, DeckGro
|
||||
public CEditorQuestDraftingProcess(final CDetailPicture cDetailPicture0) {
|
||||
super(FScreen.DRAFTING_PROCESS, cDetailPicture0, GameType.QuestDraft);
|
||||
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, false);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false);
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, false, true);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false, true);
|
||||
|
||||
//hide filters and options panel so more of pack is visible by default
|
||||
catalogManager.setHideViewOptions(1, true);
|
||||
|
||||
@@ -101,8 +101,8 @@ public final class CEditorQuestLimited extends CDeckEditor<DeckGroup> {
|
||||
|
||||
this.questData = questData0;
|
||||
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, true);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, true);
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, true, false);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, true, false);
|
||||
|
||||
catalogManager.setCaption("Sideboard");
|
||||
|
||||
|
||||
@@ -70,8 +70,8 @@ public final class CEditorVariant extends CDeckEditor<Deck> {
|
||||
this.cardPoolCondition = poolCondition;
|
||||
this.sectionMode = deckSection0;
|
||||
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, true, false);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false);
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, true, false, false);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false, false);
|
||||
deckManager.setAlwaysNonUnique(true);
|
||||
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
@@ -79,8 +79,8 @@ public class CEditorWinstonProcess extends ACEditorBase<PaperCard, DeckGroup> {
|
||||
public CEditorWinstonProcess(final CDetailPicture cDetailPicture0) {
|
||||
super(FScreen.DRAFTING_PROCESS, cDetailPicture0, GameType.Draft);
|
||||
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, false);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false);
|
||||
final CardManager catalogManager = new CardManager(cDetailPicture0, false, false, true);
|
||||
final CardManager deckManager = new CardManager(cDetailPicture0, false, false, true);
|
||||
|
||||
//hide filters and options panel so more of pack is visible by default
|
||||
catalogManager.setHideViewOptions(1, true);
|
||||
|
||||
@@ -169,6 +169,7 @@ public enum CSubmenuPreferences implements ICDoc {
|
||||
lstControls.add(Pair.of(view.getCbLoadCardsLazily(), FPref.LOAD_CARD_SCRIPTS_LAZILY));
|
||||
lstControls.add(Pair.of(view.getCbLoadHistoricFormats(), FPref.LOAD_HISTORIC_FORMATS));
|
||||
lstControls.add(Pair.of(view.getCbSmartCardArtSelectionOpt(), FPref.UI_SMART_CARD_ART));
|
||||
lstControls.add(Pair.of(view.getCbShowDraftRanking(), FPref.UI_OVERLAY_DRAFT_RANKING));
|
||||
|
||||
|
||||
for(final Pair<JCheckBox, FPref> kv : lstControls) {
|
||||
|
||||
@@ -118,6 +118,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
private final JCheckBox cbUseExperimentalNetworkStream = new OptionsCheckBox(localizer.getMessage("lblExperimentalNetworkCompatibility"));
|
||||
private final JCheckBox cbCardArtCoreExpansionsOnlyOpt = new OptionsCheckBox(localizer.getMessage("lblPrefArtExpansionOnly"));
|
||||
private final JCheckBox cbSmartCardArtSelectionOpt = new OptionsCheckBox(localizer.getMessage("lblSmartCardArtOpt"));
|
||||
private final JCheckBox cbShowDraftRanking = new OptionsCheckBox(localizer.getMessage("lblShowDraftRankingOverlay"));
|
||||
|
||||
private final Map<FPref, KeyboardShortcutField> shortcutFields = new HashMap<>();
|
||||
|
||||
@@ -299,6 +300,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlSmartCardArtOpt")), "w 80%!, h 22px!, gap 28px 0 0 0, span 2 1");
|
||||
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlSmartCardArtOptNote")), descriptionConstraints);
|
||||
|
||||
//Draft Ranking Overlay
|
||||
pnlPrefs.add(cbShowDraftRanking, titleConstraints);
|
||||
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlShowDraftRankingOverlay")), descriptionConstraints);
|
||||
|
||||
// Advanced
|
||||
pnlPrefs.add(new SectionLabel(localizer.getMessage("AdvancedSettings")), sectionConstraints);
|
||||
|
||||
@@ -819,6 +824,9 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
/** @return {@link javax.swing.JCheckBox} */
|
||||
public JCheckBox getCbSmartCardArtSelectionOpt() { return cbSmartCardArtSelectionOpt; }
|
||||
|
||||
/** @return {@link javax.swing.JCheckBox} */
|
||||
public JCheckBox getCbShowDraftRanking() { return cbShowDraftRanking; }
|
||||
|
||||
/** @return {@link javax.swing.JCheckBox} */
|
||||
public JCheckBox getCbEnforceDeckLegality() {
|
||||
return cbEnforceDeckLegality;
|
||||
|
||||
@@ -39,7 +39,7 @@ public enum VWorkshopCatalog implements IVDoc<CWorkshopCatalog> {
|
||||
|
||||
//========== Constructor
|
||||
VWorkshopCatalog() {
|
||||
this.cardManager = new CardManager(cDetailPicture, true, false);
|
||||
this.cardManager = new CardManager(cDetailPicture, true, false, false);
|
||||
this.cardManager.setCaption(localizer.getMessage("lblCatalog"));
|
||||
final Iterable<PaperCard> allCards = Iterables.concat(FModel.getMagicDb().getCommonCards().getAllCardsNoAlt(), FModel.getMagicDb().getVariantCards().getAllCards());
|
||||
this.cardManager.setPool(ItemPool.createFrom(allCards, PaperCard.class), true);
|
||||
|
||||
@@ -1108,7 +1108,8 @@ public class FSkin {
|
||||
private static String preferredDir;
|
||||
private static String preferredName;
|
||||
private static BufferedImage bimDefaultSprite, bimFavIcon, bimPreferredSprite, bimFoils, bimQuestDraftDeck, bimOldFoils,
|
||||
bimDefaultAvatars, bimPreferredAvatars, bimTrophies, bimAbilities, bimManaIcons, bimPhyrexian, bimDefaultSleeve, bimDefaultSleeve2, bimDefaultDeckbox, bimPrefferedSetLogo, bimDefaultWatermark;
|
||||
bimDefaultAvatars, bimPreferredAvatars, bimTrophies, bimAbilities, bimManaIcons, bimPhyrexian, bimDefaultSleeve,
|
||||
bimDefaultSleeve2, bimDefaultDeckbox, bimPrefferedSetLogo, bimDefaultWatermark, bimDefaultDraftRank;
|
||||
private static int x0, y0, w0, h0, newW, newH, preferredW, preferredH;
|
||||
private static int[] tempCoords;
|
||||
private static int defaultFontSize = 12;
|
||||
@@ -1250,6 +1251,7 @@ public class FSkin {
|
||||
final File f16 = new File(preferredDir + ForgeConstants.SPRITE_SETLOGO_FILE);
|
||||
final File f17 = new File(defaultDir + ForgeConstants.SPRITE_WATERMARK_FILE);
|
||||
final File f18 = new File(defaultDir +ForgeConstants.SPRITE_PHYREXIAN_FILE);
|
||||
final File f19 = new File(defaultDir + ForgeConstants.SPRITE_DRAFTRANKS_FILE);
|
||||
|
||||
try {
|
||||
int p = 0;
|
||||
@@ -1275,6 +1277,8 @@ public class FSkin {
|
||||
FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p);
|
||||
bimDefaultDeckbox = ImageIO.read(f14);
|
||||
FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p);
|
||||
bimDefaultDraftRank = ImageIO.read(f19);
|
||||
FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p);
|
||||
bimPrefferedSetLogo = f16.exists() ? ImageIO.read(f16) : ImageIO.read(f15);
|
||||
FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p);
|
||||
bimDefaultWatermark = ImageIO.read(f17);
|
||||
@@ -1346,6 +1350,9 @@ public class FSkin {
|
||||
case DECKBOX:
|
||||
setImage(prop, bimDefaultDeckbox);
|
||||
break;
|
||||
case DRAFTRANKS:
|
||||
setImage(prop, bimDefaultDraftRank);
|
||||
break;
|
||||
case SETLOGO:
|
||||
setImage(prop, bimPrefferedSetLogo);
|
||||
break;
|
||||
@@ -1375,6 +1382,7 @@ public class FSkin {
|
||||
bimDefaultSleeve.flush();
|
||||
bimDefaultSleeve2.flush();
|
||||
bimDefaultDeckbox.flush();
|
||||
bimDefaultDraftRank.flush();
|
||||
bimPrefferedSetLogo.flush();
|
||||
bimDefaultWatermark.flush();
|
||||
bimQuestDraftDeck.flush();
|
||||
@@ -1393,6 +1401,7 @@ public class FSkin {
|
||||
bimDefaultSleeve = null;
|
||||
bimDefaultSleeve2 = null;
|
||||
bimDefaultDeckbox = null;
|
||||
bimDefaultDraftRank = null;
|
||||
bimPrefferedSetLogo = null;
|
||||
bimDefaultWatermark = null;
|
||||
bimPreferredAvatars = null;
|
||||
|
||||
Reference in New Issue
Block a user