Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement default values for auto-populated selections #108

Merged
merged 5 commits into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Disqord;

/// <summary>
/// Represents a default value of a selection message component.
/// </summary>
public interface IDefaultSelectionValue: IIdentifiableEntity
{
/// <summary>
/// Gets the type of this default value.
/// </summary>
DefaultSelectionValueType Type { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ public interface ISelectionComponent : IComponent, ICustomIdentifiableEntity
/// </summary>
string? Placeholder { get; }

/// <summary>
/// Gets the default values that auto-populate this selection component.
/// </summary>
/// <remarks>
/// This is only valid for <see cref="SelectionComponentType.User"/>,
/// <see cref="SelectionComponentType.Role"/>, or <see cref="SelectionComponentType.Channel"/> selections.
/// </remarks>
IReadOnlyList<IDefaultSelectionValue> DefaultValues { get; }

/// <summary>
/// Gets the minimum amount of options that must be selected at once of this selection component.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public virtual ComponentJsonModel ToModel()
model.Type = (ComponentType) selectionComponent.Type;
model.ChannelTypes = Optional.Convert(selectionComponent.ChannelTypes, channelTypes => channelTypes?.ToArray())!;
model.Placeholder = selectionComponent.Placeholder;
model.DefaultValues = Optional.Convert(selectionComponent.DefaultValues, defaultValues => defaultValues.Select(defaultValue => defaultValue.ToModel()).ToArray());
model.MinValues = selectionComponent.MinimumSelectedOptions;
model.MaxValues = selectionComponent.MaximumSelectedOptions;
model.Disabled = selectionComponent.IsDisabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@ public static TComponent WithPlaceholder<TComponent>(this TComponent selection,
return selection;
}

public static TComponent AddDefaultValue<TComponent>(this TComponent selection, LocalDefaultSelectionValue defaultValue)
where TComponent : LocalSelectionComponent
{
if (selection.DefaultValues.Add(defaultValue, out var list))
selection.DefaultValues = new(list);

return selection;
}

public static TComponent WithDefaultValues<TComponent>(this TComponent selection, IEnumerable<LocalDefaultSelectionValue> defaultValues)
where TComponent : LocalSelectionComponent
{
Guard.IsNotNull(defaultValues);

if (selection.DefaultValues.With(defaultValues, out var list))
selection.DefaultValues = new(list);

return selection;
}

public static TComponent WithDefaultValues<TComponent>(this TComponent selection, params LocalDefaultSelectionValue[] defaultValues)
where TComponent : LocalSelectionComponent
{
return selection.WithDefaultValues(defaultValues as IEnumerable<LocalDefaultSelectionValue>);
}

public static TComponent WithMinimumSelectedOptions<TComponent>(this TComponent selection, int minimumSelectedOptions)
where TComponent : LocalSelectionComponent
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Disqord.Models;

namespace Disqord;

public class LocalDefaultSelectionValue : IDefaultSelectionValue, ILocalConstruct<LocalDefaultSelectionValue>, IJsonConvertible<DefaultValueJsonModel>
{
public static LocalDefaultSelectionValue User(Snowflake id)
{
return new(id, DefaultSelectionValueType.User);
}

public static LocalDefaultSelectionValue Role(Snowflake id)
{
return new(id, DefaultSelectionValueType.Role);
}

public static LocalDefaultSelectionValue Channel(Snowflake id)
{
return new(id, DefaultSelectionValueType.Channel);
}

/// <inheritdoc/>
public Snowflake Id { get; set; }

/// <inheritdoc/>
public DefaultSelectionValueType Type { get; set; }

/// <summary>
/// Instantiates a new <see cref="LocalDefaultSelectionValue"/>.
/// </summary>
public LocalDefaultSelectionValue()
{ }

/// <summary>
/// Instantiates a new <see cref="LocalDefaultSelectionValue"/>.
/// </summary>
/// <param name="id"> The id of the entity. </param>
/// <param name="type"> The type of the entity. </param>
public LocalDefaultSelectionValue(Snowflake id, DefaultSelectionValueType type)
{
Id = id;
Type = type;
}

/// <summary>
/// Instantiates a new <see cref="LocalDefaultSelectionValue"/> with the properties copied from another instance.
/// </summary>
/// <param name="other"> The other instance to copy properties from. </param>
protected LocalDefaultSelectionValue(LocalDefaultSelectionValue other)
{
Id = other.Id;
Type = other.Type;
}

/// <inheritdoc/>
public LocalDefaultSelectionValue Clone()
{
return new(this);
}

public DefaultValueJsonModel ToModel()
{
return new DefaultValueJsonModel
{
Id = Id,
Type = Type
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ public class LocalSelectionComponent : LocalComponent, ILocalCustomIdentifiableE
/// </summary>
public Optional<string> Placeholder { get; set; }

/// <summary>
/// Gets or sets the default values of this selection.
/// </summary>
/// <remarks>
/// This is only valid for <see cref="SelectionComponentType.User"/>,
/// <see cref="SelectionComponentType.Role"/>, or <see cref="SelectionComponentType.Channel"/> selections.
/// </remarks>
public Optional<IList<LocalDefaultSelectionValue>> DefaultValues { get; set; }

/// <summary>
/// Gets or sets the minimum amount of options of this selection.
/// </summary>
Expand Down Expand Up @@ -69,6 +78,7 @@ protected LocalSelectionComponent(LocalSelectionComponent other)
Type = other.Type;
ChannelTypes = other.ChannelTypes.Clone();
Placeholder = other.Placeholder;
DefaultValues = other.DefaultValues.DeepClone();
MinimumSelectedOptions = other.MinimumSelectedOptions;
MaximumSelectedOptions = other.MaximumSelectedOptions;
Options = other.Options.DeepClone();
Expand Down Expand Up @@ -112,6 +122,19 @@ public static LocalSelectionComponent CreateFrom(ISelectionComponent selectionCo

selection.Options = localOptions;
}
else
{
var defaultValues = selectionComponent.DefaultValues;
var defaultValueCount = defaultValues.Count;
var localDefaultValues = new List<LocalDefaultSelectionValue>(defaultValueCount);
for (var i = 0; i < defaultValueCount; i++)
{
var defaultValue = defaultValues[i];
localDefaultValues.Add(new(defaultValue.Id, defaultValue.Type));
}

selection.DefaultValues = localDefaultValues;
}

return selection;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Disqord.Models;

namespace Disqord;

/// <inheritdoc cref="IDefaultSelectionValue"/>
public class TransientDefaultSelectionValue : TransientClientEntity<DefaultValueJsonModel>, IDefaultSelectionValue
{
/// <inheritdoc/>
public Snowflake Id => Model.Id;

/// <inheritdoc/>
public DefaultSelectionValueType Type => Model.Type;

public TransientDefaultSelectionValue(IClient client, DefaultValueJsonModel model)
: base(client, model)
{ }
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public IReadOnlyList<ChannelType> ChannelTypes
/// <inheritdoc/>
public string? Placeholder => Model.Placeholder.GetValueOrDefault();

/// <inheritdoc/>
public IReadOnlyList<IDefaultSelectionValue> DefaultValues => _defaultValues ??= Model.DefaultValues.Value.ToReadOnlyList(Client, (model, client) => new TransientDefaultSelectionValue(client, model));

private IReadOnlyList<IDefaultSelectionValue>? _defaultValues;

/// <inheritdoc/>
public int MinimumSelectedOptions => Model.MinValues.Value;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Runtime.Serialization;
using Disqord.Serialization.Json;

namespace Disqord;

/// <summary>
/// Represents the type of default selection value.
/// </summary>
[StringEnum]
public enum DefaultSelectionValueType
{
[EnumMember(Value = "user")]
User,

[EnumMember(Value = "role")]
Role,

[EnumMember(Value = "channel")]
Channel,
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Disqord.Serialization.Json;
using Qommon;

Expand Down Expand Up @@ -39,6 +40,9 @@ public class ComponentJsonModel : JsonModel
[JsonProperty("placeholder")]
public Optional<string> Placeholder;

[JsonProperty("default_values")]
public Optional<DefaultValueJsonModel[]> DefaultValues;

[JsonProperty("min_values")]
public Optional<int> MinValues;

Expand Down Expand Up @@ -137,6 +141,22 @@ protected override void OnValidate()
if (MinValues.HasValue && MaxValues.HasValue)
Guard.IsLessThanOrEqualTo(MinValues.Value, MaxValues.Value);

OptionalGuard.CheckValue(DefaultValues, defaultValues =>
{
Guard.IsBetweenOrEqualTo(defaultValues.Length, MinValues.GetValueOrDefault(Discord.Limits.Component.Selection.MinMinimumSelectedOptions), MaxValues.GetValueOrDefault(Discord.Limits.Component.Selection.MaxMaximumSelectedOptions));

Predicate<DefaultValueJsonModel> predicate = Type switch
{
ComponentType.UserSelection => value => value.Type is DefaultSelectionValueType.User,
ComponentType.RoleSelection => value => value.Type is DefaultSelectionValueType.Role,
ComponentType.MentionableSelection => value => value.Type is DefaultSelectionValueType.User or DefaultSelectionValueType.Role,
ComponentType.ChannelSelection => value => value.Type is DefaultSelectionValueType.Channel,
_ => value => true
};

Guard.IsTrue(Array.TrueForAll(defaultValues, predicate), message: "The types of default selection values must match the type of the component.");
});

break;
}
case ComponentType.TextInput:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Disqord.Serialization.Json;

namespace Disqord.Models;

public class DefaultValueJsonModel : JsonModel
{
[JsonProperty("id")]
public Snowflake Id;

[JsonProperty("type")]
public DefaultSelectionValueType Type;
}
Loading