Skip to content

Commit

Permalink
Implement default values for auto-populated selections (Quahu#108)
Browse files Browse the repository at this point in the history
* Implement default values for selections

* Added validation for default selection values

* Fix check

* Update validation check

* Add case to handle warning
  • Loading branch information
AnotherZane committed Jul 28, 2024
1 parent 9b2a2c1 commit 39562c2
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 0 deletions.
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;
}

0 comments on commit 39562c2

Please sign in to comment.