Editor UI panel for the SuperShot tool that manages Discord webhook channels and sharing options. It builds a UI to list, add, remove, favorite, and configure webhook channels, choose message mode per channel, save settings, and trigger capture/post actions.
using System;
using Sandbox;
namespace Editor.SuperShot;
public sealed class SharePanel : Widget
{
readonly SuperShotWindow _window;
Widget _channels;
public SharePanel( SuperShotWindow window ) : base( null )
{
_window = window;
Name = "Discord";
WindowTitle = "Discord";
SetWindowIcon( "forum" );
Layout = Layout.Column();
Layout.Margin = 8;
Layout.Spacing = 8;
Build();
}
void Build()
{
SuperShotUI.AddBanner( Layout, "Discord", "Post the current shot to your channels. Posting always saves a local copy too.", "forum" );
var scroll = new ScrollArea( this );
scroll.Canvas = new Widget( scroll );
scroll.Canvas.Layout = Layout.Column();
scroll.Canvas.Layout.Spacing = 8;
var body = scroll.Canvas.Layout;
var channelsCard = SuperShotUI.AddCard( body, "Discord Channels", "forum" );
channelsCard.Body.Add( new Label( "Each channel is a Discord webhook. Star a channel to pin it to the Discord menu for one-click posting." ) );
_channels = new Widget( null );
_channels.Layout = Layout.Column();
_channels.Layout.Spacing = 6;
channelsCard.Body.Add( _channels );
RebuildChannels();
var addRow = channelsCard.Body.AddRow();
addRow.Spacing = 4;
addRow.Add( new Button( "Add Channel", "add" ) { Tint = new Color( 0.27f, 0.58f, 0.38f ), Clicked = AddChannel } );
addRow.AddStretchCell();
var msgSo = _window.Settings.Share.Message.GetSerialized();
msgSo.OnPropertyChanged += _ => _window.Settings.Save();
SuperShotUI.AddSection( body, "Shared Message Format", "edit_note",
SuperShotUI.SheetWidget( msgSo ), cookie: "supershot.share.message" );
body.AddStretchCell();
Layout.Add( scroll, 1 );
var shareRow = Layout.AddRow();
shareRow.Spacing = 4;
shareRow.Add( new Button.Primary( "Save + Post to All", "send" ) { Clicked = SaveAndPostAll } );
shareRow.Add( new Button( "Post to All", "share" ) { Clicked = () => _ = _window.PostCurrentToAll() } );
var utilRow = Layout.AddRow();
utilRow.Spacing = 4;
utilRow.Add( new Button( "Copy Path", "content_copy" ) { ToolTip = "Save the shot and copy its file path", Clicked = CopyPath } );
utilRow.Add( new Button( "Upload to s&box Art Page", "image" ) { ToolTip = "Save the shot, copy its path, and open Steam's s&box artwork upload page", Clicked = _window.UploadToArtPage } );
}
static bool IsChannelBasic( SerializedProperty p )
{
return p.Name is nameof( DiscordWebhookConfig.Name )
or nameof( DiscordWebhookConfig.Enabled );
}
static string MessageModeLabel( WebhookMessageMode mode ) => mode switch
{
WebhookMessageMode.Custom => "Message: Custom",
WebhookMessageMode.None => "Message: None (image only)",
_ => "Message: Shared format",
};
static string MessageModeIcon( WebhookMessageMode mode ) => mode switch
{
WebhookMessageMode.Custom => "edit",
WebhookMessageMode.None => "hide_image",
_ => "forum",
};
void RebuildChannels()
{
_channels.Layout.Clear( true );
var list = _window.Settings.Share.Webhooks;
if ( list.Count == 0 )
{
_channels.Layout.Add( new Label( "No channels yet. Click \"Add Channel\" and paste a Discord webhook URL." ) );
return;
}
foreach ( var webhook in list.ToArray() )
_channels.Layout.Add( BuildChannelCard( webhook ) );
}
Widget BuildChannelCard( DiscordWebhookConfig webhook )
{
var card = new Card();
var header = card.Body.AddRow();
header.Spacing = 6;
header.Add( new IconLabel( "tag" ) );
var title = new Label.Subtitle( string.IsNullOrWhiteSpace( webhook.Name ) ? "Unnamed channel" : webhook.Name );
header.Add( title );
header.AddStretchCell();
var star = new Button( "Favorite", webhook.Favorite ? "star" : "star_outline" )
{
ToolTip = webhook.Favorite ? "Unpin from the quick-post menus" : "Pin to the quick-post menus",
Clicked = () =>
{
webhook.Favorite = !webhook.Favorite;
_window.Settings.Save();
RebuildChannels();
}
};
if ( webhook.Favorite )
star.Tint = new Color( 0.95f, 0.75f, 0.2f );
header.Add( star );
var so = webhook.GetSerialized();
// Only persist on edit. Don't rebuild or touch other widgets here - doing so collapses the URL
// expander and pulls focus out of the field being typed in. The header refreshes on the next rebuild.
so.OnPropertyChanged += _ => _window.Settings.Save();
card.Body.Add( SuperShotUI.SheetWidget( so, IsChannelBasic ) );
SuperShotUI.AddSection( card.Body, "Webhook URL", "link",
SuperShotUI.SheetWidget( so, p => p.Name == nameof( DiscordWebhookConfig.Url ) ),
defaultOpen: !webhook.IsConfigured );
var msgRow = card.Body.AddRow();
msgRow.Spacing = 4;
msgRow.Add( new Button( MessageModeLabel( webhook.MessageMode ), MessageModeIcon( webhook.MessageMode ) )
{
ToolTip = "Click to cycle what this channel posts:\n"
+ "Shared = the Shared Message Format below\n"
+ "Custom = this channel's own message/embed\n"
+ "None = the image only, no text or embed",
Clicked = () =>
{
webhook.MessageMode = (WebhookMessageMode)(((int)webhook.MessageMode + 1) % 3);
_window.Settings.Save();
RebuildChannels();
}
} );
msgRow.AddStretchCell();
if ( webhook.MessageMode == WebhookMessageMode.Custom )
{
var msgSo = webhook.CustomMessage.GetSerialized();
msgSo.OnPropertyChanged += _ => _window.Settings.Save();
card.Body.Add( SuperShotUI.SheetWidget( msgSo ) );
}
var actions = card.Body.AddRow();
actions.Spacing = 4;
actions.Add( new Button.Primary( "Capture + Post", "photo_camera" )
{
ToolTip = "Take a fresh capture, save it locally, and post it to this channel",
Clicked = () => _ = SuperShotService.CaptureAndPostTo( webhook )
} );
actions.AddStretchCell();
actions.Add( new Button.Danger( "Remove", "delete" ) { Clicked = () => RemoveChannel( webhook ) } );
return card;
}
void AddChannel()
{
_window.Settings.Share.Webhooks.Add( new DiscordWebhookConfig() );
_window.Settings.Save();
RebuildChannels();
}
void RemoveChannel( DiscordWebhookConfig webhook )
{
_window.Settings.Share.Webhooks.Remove( webhook );
_window.Settings.Save();
RebuildChannels();
}
void SaveAndPostAll()
{
_window.SaveCurrent();
_ = _window.PostCurrentToAll();
}
void CopyPath()
{
var path = _window.SaveCurrent();
SuperShotService.CopyPathToClipboard( path );
}
}