Added signed up options for role reminder
Added option to opt out of reminders
This commit is contained in:
parent
64ce169094
commit
c88bf5b133
18 changed files with 911 additions and 99 deletions
|
@ -54,6 +54,16 @@ namespace DiscordBot.CommandHandlers
|
|||
await _httpService.SignOff(signOff);
|
||||
await component.RespondAsync("Signed Off", ephemeral: true);
|
||||
break;
|
||||
case Constants.ComponentIds.OPT_OUT_BUTTON:
|
||||
if(await _httpService.ReminderOptOut(component.User.Id))
|
||||
{
|
||||
await component.RespondAsync("You opted out of the raid reminders.");
|
||||
}
|
||||
else
|
||||
{
|
||||
await component.RespondAsync("Opting out failed, please try again later or change the setting on the website.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
public const string BACKUP_BUTTON = "backupButton";
|
||||
public const string FLEX_BUTTON = "flexButton";
|
||||
public const string SIGN_OFF_BUTTON = "signOffButton";
|
||||
public const string OPT_OUT_BUTTON = "optOutButton";
|
||||
|
||||
public const string ROLE_SELECT_DROP_DOWN = "roleSelectDropDown";
|
||||
public const string ROLE_SELECT_EXTERNAL_DROP_DOWN = "roleSelectExternalDropDown";
|
||||
|
|
|
@ -95,5 +95,12 @@ namespace DiscordBot.Controllers
|
|||
{
|
||||
await DiscordBot.CommandHandlers.HandlerFunctions.RenameUser(_client, user.userId, user.Name, user.Account, user.ServerIds);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("[action]/{userId}")]
|
||||
public async Task SendReminderOptOutMessage(ulong userId)
|
||||
{
|
||||
await ReminderSubscriptionMessage.sendMessage(_client, userId);
|
||||
}
|
||||
}
|
||||
}
|
32
DiscordBot/Messages/ReminderSubscriptionMessage.cs
Normal file
32
DiscordBot/Messages/ReminderSubscriptionMessage.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace DiscordBot.Messages
|
||||
{
|
||||
public class ReminderSubscriptionMessage
|
||||
{
|
||||
public static async Task sendMessage(DiscordSocketClient client, ulong userId)
|
||||
{
|
||||
var builder = new ComponentBuilder()
|
||||
.WithButton("Opt Out", $"{Constants.ComponentIds.OPT_OUT_BUTTON}", ButtonStyle.Danger);
|
||||
|
||||
string message = "Hi, I'm Raid-o-Tron. \n"
|
||||
+ "I will send you reminders for raids you have signed up for.\n"
|
||||
+ "The reminders will look like\n"
|
||||
+ "> Testraid: The raid starts in 30 minutes. \n"
|
||||
+ "You can opt out of the reminders here or change it any time at https://lieb.games \n"
|
||||
+ " ------------------------------------------- \n"
|
||||
+ "Hi, ich bin Raid-o-Tron. \n"
|
||||
+ "Ich werde dir Erinnerungen für Raid an denen du dich angemeldet hast schicken.\n"
|
||||
+ "Die Erinnerungen werden so aussehen:\n"
|
||||
+ "> Testraid: The raid starts in 30 minutes. \n"
|
||||
+ "Du kannst dich von den Erinnerungen hier abmelden oder deine Einstellungen jederzeit auf https://lieb.games ändern.";
|
||||
|
||||
var user = await client.GetUserAsync(userId);
|
||||
if(user != null)
|
||||
{
|
||||
await user.SendMessageAsync(message, components: builder.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -242,5 +242,20 @@ namespace DiscordBot.Services
|
|||
}
|
||||
return new Tuple<bool, string>(true, string.Empty);
|
||||
}
|
||||
|
||||
public async Task<bool> ReminderOptOut(ulong userId)
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient(Constants.HTTP_CLIENT_NAME);
|
||||
|
||||
var httpResponseMessage = await httpClient.GetAsync($"DiscordBot/ReminderOptOut/{userId}");
|
||||
if (httpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
using var contentStream =
|
||||
await httpResponseMessage.Content.ReadAsStreamAsync();
|
||||
|
||||
return await JsonSerializer.DeserializeAsync<bool>(contentStream, _serializerOptions);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -250,5 +250,12 @@ namespace Lieb.Controllers
|
|||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("[action]/{userId}")]
|
||||
public async Task<ActionResult> ReminderOptOut(ulong userId)
|
||||
{
|
||||
return Ok(await _userService.ReminderOptOut(userId));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -160,16 +160,62 @@ namespace Lieb.Data
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> SendMessageToRaidUsers(string message, Raid raid)
|
||||
{
|
||||
if(raid == null) return false;
|
||||
|
||||
HashSet<ulong> userIds = new HashSet<ulong>();
|
||||
foreach(RaidSignUp signUp in raid.SignUps)
|
||||
{
|
||||
if(signUp.LiebUserId.HasValue)
|
||||
{
|
||||
userIds.Add(signUp.LiebUserId.Value);
|
||||
}
|
||||
}
|
||||
return await SendMessageToUsers(message, raid.Title, userIds);
|
||||
}
|
||||
|
||||
public async Task SendGroupReminder(RaidReminder reminder, Raid raid)
|
||||
{
|
||||
using var context = _contextFactory.CreateDbContext();
|
||||
HashSet<ulong> groupMembers = context.LiebUsers.Where(u => u.RoleAssignments.Where(r => r.LiebRole.LiebRoleId == reminder.RoleId).Any()).Select(u => u.Id).ToHashSet();
|
||||
HashSet<ulong> userIds;
|
||||
switch(reminder.RoleType)
|
||||
{
|
||||
case RaidReminder.RoleReminderType.All:
|
||||
userIds = groupMembers;
|
||||
break;
|
||||
case RaidReminder.RoleReminderType.SignedUp:
|
||||
userIds = groupMembers.Where(m => raid.SignUps.Where(s => s.LiebUserId == m).Any()).ToHashSet();
|
||||
break;
|
||||
case RaidReminder.RoleReminderType.NotSignedUp:
|
||||
userIds = groupMembers.Where(m => !raid.SignUps.Where(s => s.LiebUserId == m).Any()).ToHashSet();
|
||||
break;
|
||||
default:
|
||||
userIds = new HashSet<ulong>();
|
||||
break;
|
||||
}
|
||||
if (await SendMessageToUsers(reminder.Message, raid.Title, userIds))
|
||||
{
|
||||
reminder.Sent = true;
|
||||
context.Update(reminder);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> SendMessageToUsers(string message, string raidTitle, HashSet<ulong> userIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = _contextFactory.CreateDbContext();
|
||||
HashSet<ulong> userIdsToSendTo = context.LiebUsers
|
||||
.Where(u => u.ReminderSubscription == userIds.Contains(u.Id))
|
||||
.Select(u => u.Id)
|
||||
.ToHashSet();
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient(Constants.HttpClientName);
|
||||
|
||||
if(raid == null) return false;
|
||||
|
||||
ApiUserReminder apiReminder = ConvertUserReminder(message, raid);
|
||||
ApiUserReminder apiReminder = CreateUserReminder(message, raidTitle, userIds);
|
||||
|
||||
var raidItemJson = new StringContent(
|
||||
JsonSerializer.Serialize(apiReminder),
|
||||
|
@ -184,21 +230,12 @@ namespace Lieb.Data
|
|||
return false;
|
||||
}
|
||||
|
||||
public static ApiUserReminder ConvertUserReminder(string message, Raid raid)
|
||||
public static ApiUserReminder CreateUserReminder(string message, string raidTitle, HashSet<ulong> userIds)
|
||||
{
|
||||
ApiUserReminder apiReminder = new ApiUserReminder()
|
||||
{
|
||||
Message = $"{raid.Title}: {message}"
|
||||
Message = $"{raidTitle}: {message}"
|
||||
};
|
||||
apiReminder.UserIds = new List<ulong>();
|
||||
HashSet<ulong> userIds = new HashSet<ulong>();
|
||||
foreach(RaidSignUp signUp in raid.SignUps)
|
||||
{
|
||||
if(signUp.LiebUserId.HasValue)
|
||||
{
|
||||
userIds.Add(signUp.LiebUserId.Value);
|
||||
}
|
||||
}
|
||||
apiReminder.UserIds = userIds.ToList();
|
||||
return apiReminder;
|
||||
}
|
||||
|
@ -249,51 +286,6 @@ namespace Lieb.Data
|
|||
|
||||
#endregion ChannelReminder
|
||||
|
||||
#region GroupReminder
|
||||
public async Task SendGroupReminder(RaidReminder reminder, string raidTitle)
|
||||
{
|
||||
using var context = _contextFactory.CreateDbContext();
|
||||
HashSet<ulong> groupMembers = context.LiebUsers.Where(u => u.RoleAssignments.Where(r => r.LiebRole.LiebRoleId == reminder.RoleId).Any()).Select(u => u.Id).ToHashSet();
|
||||
if (await SendMessageToGroup(reminder.Message, raidTitle, groupMembers))
|
||||
{
|
||||
reminder.Sent = true;
|
||||
context.Update(reminder);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> SendMessageToGroup(string message, string raidTitle, HashSet<ulong> userIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient(Constants.HttpClientName);
|
||||
|
||||
ApiUserReminder apiReminder = ConvertGroupReminder(message, raidTitle, userIds);
|
||||
|
||||
var raidItemJson = new StringContent(
|
||||
JsonSerializer.Serialize(apiReminder),
|
||||
Encoding.UTF8,
|
||||
Application.Json);
|
||||
|
||||
var httpResponseMessage = await httpClient.PostAsync("raid/SendUserReminder", raidItemJson);
|
||||
|
||||
return httpResponseMessage.IsSuccessStatusCode;
|
||||
}
|
||||
catch {}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ApiUserReminder ConvertGroupReminder(string message, string raidTitle, HashSet<ulong> groupIds)
|
||||
{
|
||||
ApiUserReminder apiReminder = new ApiUserReminder()
|
||||
{
|
||||
Message = $"{raidTitle}: {message}"
|
||||
};
|
||||
apiReminder.UserIds = groupIds.ToList();
|
||||
return apiReminder;
|
||||
}
|
||||
#endregion GroupReminder
|
||||
|
||||
private async Task UpdateDiscordMessages(IEnumerable<ApiRaid.DiscordMessage> messages, Raid raid)
|
||||
{
|
||||
foreach(ApiRaid.DiscordMessage message in messages)
|
||||
|
@ -406,5 +398,17 @@ namespace Lieb.Data
|
|||
}
|
||||
catch {}
|
||||
}
|
||||
|
||||
public async Task SendReminderOptOutMessage(ulong userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient(Constants.HttpClientName);
|
||||
var httpResponseMessage = await httpClient.GetAsync($"raid/SendReminderOptOutMessage/{userId}");
|
||||
|
||||
httpResponseMessage.EnsureSuccessStatusCode();
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -519,7 +519,7 @@ namespace Lieb.Data
|
|||
await _discordService.SendChannelReminder(reminder, raid.Title);
|
||||
break;
|
||||
case RaidReminder.ReminderType.Group:
|
||||
await _discordService.SendGroupReminder(reminder, raid.Title);
|
||||
await _discordService.SendGroupReminder(reminder, raid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ namespace Lieb.Data
|
|||
context.RoleAssignments.Add(roleAssignment);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
await _discordService.SendReminderOptOutMessage(newUser.Id);
|
||||
}
|
||||
|
||||
public async Task EditUser(LiebUser user)
|
||||
|
@ -343,5 +344,17 @@ namespace Lieb.Data
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ReminderOptOut(ulong userId)
|
||||
{
|
||||
using var context = _contextFactory.CreateDbContext();
|
||||
LiebUser? user = await context.LiebUsers
|
||||
.FirstOrDefaultAsync(u => u.Id == userId);
|
||||
if(user == null) return false;
|
||||
|
||||
user.ReminderSubscription = false;
|
||||
await context.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
659
Lieb/Migrations/20221209181937_AddRoleReminderTypeAndSubscription.Designer.cs
generated
Normal file
659
Lieb/Migrations/20221209181937_AddRoleReminderTypeAndSubscription.Designer.cs
generated
Normal file
|
@ -0,0 +1,659 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Lieb.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Lieb.Migrations
|
||||
{
|
||||
[DbContext(typeof(LiebContext))]
|
||||
[Migration("20221209181937_AddRoleReminderTypeAndSubscription")]
|
||||
partial class AddRoleReminderTypeAndSubscription
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.10");
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.DiscordSettings", b =>
|
||||
{
|
||||
b.Property<ulong>("DiscordSettingsId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ChangeUserNames")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("DiscordLogChannel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("DiscordSettingsId");
|
||||
|
||||
b.ToTable("DiscordSettings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Equipped", b =>
|
||||
{
|
||||
b.Property<int>("EquippedId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("CanTank")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("GuildWars2AccountId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("GuildWars2BuildId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("EquippedId");
|
||||
|
||||
b.HasIndex("GuildWars2AccountId");
|
||||
|
||||
b.HasIndex("GuildWars2BuildId");
|
||||
|
||||
b.ToTable("Equipped", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.GuildWars2Account", b =>
|
||||
{
|
||||
b.Property<int>("GuildWars2AccountId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AccountName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ApiKey")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<ulong?>("LiebUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("GuildWars2AccountId");
|
||||
|
||||
b.HasIndex("LiebUserId");
|
||||
|
||||
b.ToTable("GuildWars2Account", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.GuildWars2Build", b =>
|
||||
{
|
||||
b.Property<int>("GuildWars2BuildId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Alacrity")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("BuildName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(60)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Class")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DamageType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EliteSpecialization")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Might")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Quickness")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Source")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceLink")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("UseInRandomRaid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("GuildWars2BuildId");
|
||||
|
||||
b.ToTable("GuildWars2Build", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.DiscordRaidMessage", b =>
|
||||
{
|
||||
b.Property<int>("DiscordRaidMessageId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("DiscordChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("DiscordGuildId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("DiscordMessageId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("RaidId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("RaidTemplateId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("DiscordRaidMessageId");
|
||||
|
||||
b.HasIndex("RaidId");
|
||||
|
||||
b.HasIndex("RaidTemplateId");
|
||||
|
||||
b.ToTable("DiscordRaidMessage", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.Raid", b =>
|
||||
{
|
||||
b.Property<int>("RaidId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("EndTimeUTC")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("FreeForAllTimeUTC")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Guild")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("MoveFlexUsers")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Organizer")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<ulong?>("RaidOwnerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RaidType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RequiredRole")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("StartTimeUTC")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("VoiceChat")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("RaidId");
|
||||
|
||||
b.ToTable("Raid", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.RaidReminder", b =>
|
||||
{
|
||||
b.Property<int>("RaidReminderId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("DiscordChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("DiscordServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("RaidId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("RaidTemplateId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("ReminderTimeUTC")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("RoleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RoleType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Sent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TimeType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("RaidReminderId");
|
||||
|
||||
b.HasIndex("RaidId");
|
||||
|
||||
b.HasIndex("RaidTemplateId");
|
||||
|
||||
b.ToTable("RaidReminder", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.RaidRole", b =>
|
||||
{
|
||||
b.Property<int>("RaidRoleId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsRandomSignUpRole")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("RaidId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("RaidTemplateId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Spots")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("RaidRoleId");
|
||||
|
||||
b.HasIndex("RaidId");
|
||||
|
||||
b.HasIndex("RaidTemplateId");
|
||||
|
||||
b.ToTable("RaidRole", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.RaidSignUp", b =>
|
||||
{
|
||||
b.Property<int>("RaidSignUpId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ExternalUserName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("GuildWars2AccountId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong?>("LiebUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RaidId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RaidRoleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SignUpType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("RaidSignUpId");
|
||||
|
||||
b.HasIndex("GuildWars2AccountId");
|
||||
|
||||
b.HasIndex("LiebUserId");
|
||||
|
||||
b.HasIndex("RaidId");
|
||||
|
||||
b.HasIndex("RaidRoleId");
|
||||
|
||||
b.ToTable("RaidSignUp", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.RaidTemplate", b =>
|
||||
{
|
||||
b.Property<int>("RaidTemplateId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("CreateDaysBefore")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("EndTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("FreeForAllTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Guild")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Interval")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("MoveFlexUsers")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Organizer")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<ulong?>("RaidOwnerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RaidType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RequiredRole")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("StartTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TimeZone")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("VoiceChat")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("RaidTemplateId");
|
||||
|
||||
b.ToTable("RaidTemplate", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.LiebRole", b =>
|
||||
{
|
||||
b.Property<int>("LiebRoleId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LevelToAssign")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RoleName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("LiebRoleId");
|
||||
|
||||
b.ToTable("LiebRole", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.LiebUser", b =>
|
||||
{
|
||||
b.Property<ulong>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("AlwaysSignUpWithMainAccount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("BannedUntil")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("Birthday")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("LastSignUpAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MainGW2Account")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Pronouns")
|
||||
.IsRequired()
|
||||
.HasMaxLength(60)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ReminderSubscription")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LiebUser", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.RoleAssignment", b =>
|
||||
{
|
||||
b.Property<int>("RoleAssignmentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LiebRoleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("LiebUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("RoleAssignmentId");
|
||||
|
||||
b.HasIndex("LiebRoleId");
|
||||
|
||||
b.HasIndex("LiebUserId");
|
||||
|
||||
b.ToTable("RoleAssignment", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Equipped", b =>
|
||||
{
|
||||
b.HasOne("Lieb.Models.GuildWars2.GuildWars2Account", "GuildWars2Account")
|
||||
.WithMany("EquippedBuilds")
|
||||
.HasForeignKey("GuildWars2AccountId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Lieb.Models.GuildWars2.GuildWars2Build", "GuildWars2Build")
|
||||
.WithMany("EquippedRoles")
|
||||
.HasForeignKey("GuildWars2BuildId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("GuildWars2Account");
|
||||
|
||||
b.Navigation("GuildWars2Build");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.GuildWars2Account", b =>
|
||||
{
|
||||
b.HasOne("Lieb.Models.LiebUser", null)
|
||||
.WithMany("GuildWars2Accounts")
|
||||
.HasForeignKey("LiebUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.DiscordRaidMessage", b =>
|
||||
{
|
||||
b.HasOne("Lieb.Models.GuildWars2.Raid.Raid", null)
|
||||
.WithMany("DiscordRaidMessages")
|
||||
.HasForeignKey("RaidId");
|
||||
|
||||
b.HasOne("Lieb.Models.GuildWars2.Raid.RaidTemplate", null)
|
||||
.WithMany("DiscordRaidMessages")
|
||||
.HasForeignKey("RaidTemplateId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.RaidReminder", b =>
|
||||
{
|
||||
b.HasOne("Lieb.Models.GuildWars2.Raid.Raid", null)
|
||||
.WithMany("Reminders")
|
||||
.HasForeignKey("RaidId");
|
||||
|
||||
b.HasOne("Lieb.Models.GuildWars2.Raid.RaidTemplate", null)
|
||||
.WithMany("Reminders")
|
||||
.HasForeignKey("RaidTemplateId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.RaidRole", b =>
|
||||
{
|
||||
b.HasOne("Lieb.Models.GuildWars2.Raid.Raid", null)
|
||||
.WithMany("Roles")
|
||||
.HasForeignKey("RaidId");
|
||||
|
||||
b.HasOne("Lieb.Models.GuildWars2.Raid.RaidTemplate", null)
|
||||
.WithMany("Roles")
|
||||
.HasForeignKey("RaidTemplateId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.RaidSignUp", b =>
|
||||
{
|
||||
b.HasOne("Lieb.Models.GuildWars2.GuildWars2Account", "GuildWars2Account")
|
||||
.WithMany()
|
||||
.HasForeignKey("GuildWars2AccountId");
|
||||
|
||||
b.HasOne("Lieb.Models.LiebUser", "LiebUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("LiebUserId");
|
||||
|
||||
b.HasOne("Lieb.Models.GuildWars2.Raid.Raid", "Raid")
|
||||
.WithMany("SignUps")
|
||||
.HasForeignKey("RaidId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Lieb.Models.GuildWars2.Raid.RaidRole", "RaidRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RaidRoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("GuildWars2Account");
|
||||
|
||||
b.Navigation("LiebUser");
|
||||
|
||||
b.Navigation("Raid");
|
||||
|
||||
b.Navigation("RaidRole");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.RoleAssignment", b =>
|
||||
{
|
||||
b.HasOne("Lieb.Models.LiebRole", "LiebRole")
|
||||
.WithMany("RoleAssignments")
|
||||
.HasForeignKey("LiebRoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Lieb.Models.LiebUser", "LiebUser")
|
||||
.WithMany("RoleAssignments")
|
||||
.HasForeignKey("LiebUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("LiebRole");
|
||||
|
||||
b.Navigation("LiebUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.GuildWars2Account", b =>
|
||||
{
|
||||
b.Navigation("EquippedBuilds");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.GuildWars2Build", b =>
|
||||
{
|
||||
b.Navigation("EquippedRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.Raid", b =>
|
||||
{
|
||||
b.Navigation("DiscordRaidMessages");
|
||||
|
||||
b.Navigation("Reminders");
|
||||
|
||||
b.Navigation("Roles");
|
||||
|
||||
b.Navigation("SignUps");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.GuildWars2.Raid.RaidTemplate", b =>
|
||||
{
|
||||
b.Navigation("DiscordRaidMessages");
|
||||
|
||||
b.Navigation("Reminders");
|
||||
|
||||
b.Navigation("Roles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.LiebRole", b =>
|
||||
{
|
||||
b.Navigation("RoleAssignments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Lieb.Models.LiebUser", b =>
|
||||
{
|
||||
b.Navigation("GuildWars2Accounts");
|
||||
|
||||
b.Navigation("RoleAssignments");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Lieb.Migrations
|
||||
{
|
||||
public partial class AddRoleReminderTypeAndSubscription : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "RoleType",
|
||||
table: "RaidReminder",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ReminderSubscription",
|
||||
table: "LiebUser",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RoleType",
|
||||
table: "RaidReminder");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ReminderSubscription",
|
||||
table: "LiebUser");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -247,6 +247,9 @@ namespace Lieb.Migrations
|
|||
b.Property<int>("RoleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RoleType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Sent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
@ -469,6 +472,9 @@ namespace Lieb.Migrations
|
|||
.HasMaxLength(60)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ReminderSubscription")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LiebUser", (string)null);
|
||||
|
|
|
@ -18,6 +18,13 @@ namespace Lieb.Models.GuildWars2.Raid
|
|||
Dynamic = 2
|
||||
}
|
||||
|
||||
public enum RoleReminderType
|
||||
{
|
||||
All = 0,
|
||||
SignedUp = 1,
|
||||
NotSignedUp = 2
|
||||
}
|
||||
|
||||
public int RaidReminderId { get; set; }
|
||||
|
||||
[Required]
|
||||
|
@ -40,6 +47,8 @@ namespace Lieb.Models.GuildWars2.Raid
|
|||
public ulong DiscordChannelId { get; set; }
|
||||
|
||||
public int RoleId {get; set; }
|
||||
|
||||
public RoleReminderType RoleType {get; set;}
|
||||
|
||||
public bool Sent { get; set; } = false;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace Lieb.Models
|
|||
public DateTime? LastSignUpAt { get; set; }
|
||||
public int MainGW2Account { get; set; }
|
||||
public bool AlwaysSignUpWithMainAccount { get; set; } = false;
|
||||
public bool ReminderSubscription { get; set; } = true;
|
||||
public ICollection<GuildWars2Account> GuildWars2Accounts { get; set; } = new List<GuildWars2Account>();
|
||||
public ICollection<RoleAssignment> RoleAssignments { get; set; } = new List<RoleAssignment>();
|
||||
}
|
||||
|
|
|
@ -14,30 +14,24 @@
|
|||
<th>Hours</th>
|
||||
<th>Minutes</th>
|
||||
<th>Type</th>
|
||||
@{bool channelReminderExists = _raidReminders.Where(r => r.Type == RaidReminder.ReminderType.Channel).Any();
|
||||
bool groupReminderExists = _raidReminders.Where(r => r.Type == RaidReminder.ReminderType.Group).Any();}
|
||||
@if(channelReminderExists && groupReminderExists)
|
||||
@if(_raidReminders.Where(r => r.Type == RaidReminder.ReminderType.Group).Any())
|
||||
{
|
||||
<th>Group</th>
|
||||
<th>Server</th>
|
||||
<th>Channel</th>
|
||||
}
|
||||
else if(channelReminderExists)
|
||||
{
|
||||
<th></th>
|
||||
<th>Server</th>
|
||||
<th>Channel</th>
|
||||
}
|
||||
else if(groupReminderExists)
|
||||
{
|
||||
<th>Group</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th>Signed Up</th>
|
||||
}
|
||||
else
|
||||
{
|
||||
<th></th>
|
||||
<th></th>
|
||||
}
|
||||
@if(_raidReminders.Where(r => r.Type == RaidReminder.ReminderType.Channel).Any())
|
||||
{
|
||||
<th>Server</th>
|
||||
<th>Channel</th>
|
||||
}
|
||||
else
|
||||
{
|
||||
<th></th>
|
||||
<th></th>
|
||||
}
|
||||
<th>Message</th>
|
||||
|
@ -66,6 +60,14 @@
|
|||
}
|
||||
</InputSelect>
|
||||
</td>
|
||||
<td>
|
||||
<InputSelect @bind-Value="reminder.RoleType" hidden="@groupHidden">
|
||||
@foreach(RaidReminder.RoleReminderType type in Enum.GetValues(typeof(RaidReminder.RoleReminderType)))
|
||||
{
|
||||
<option value="@type">@type.ToString()</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</td>
|
||||
<td>
|
||||
<InputSelect @bind-Value="reminder.DiscordServerId" hidden="@discordHidden">
|
||||
@foreach(DiscordServer item in _discordServers)
|
||||
|
|
|
@ -13,30 +13,24 @@
|
|||
<th>Date</th>
|
||||
<th>Time</th>
|
||||
<th>Type</th>
|
||||
@{bool channelReminderExists = _raidReminders.Where(r => r.Type == RaidReminder.ReminderType.Channel).Any();
|
||||
bool groupReminderExists = _raidReminders.Where(r => r.Type == RaidReminder.ReminderType.Group).Any();}
|
||||
@if(channelReminderExists && groupReminderExists)
|
||||
@if(_raidReminders.Where(r => r.Type == RaidReminder.ReminderType.Group).Any())
|
||||
{
|
||||
<th>Group</th>
|
||||
<th>Server</th>
|
||||
<th>Channel</th>
|
||||
}
|
||||
else if(channelReminderExists)
|
||||
{
|
||||
<th></th>
|
||||
<th>Server</th>
|
||||
<th>Channel</th>
|
||||
}
|
||||
else if(groupReminderExists)
|
||||
{
|
||||
<th>Group</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th>Signed Up</th>
|
||||
}
|
||||
else
|
||||
{
|
||||
<th></th>
|
||||
<th></th>
|
||||
}
|
||||
@if(_raidReminders.Where(r => r.Type == RaidReminder.ReminderType.Channel).Any())
|
||||
{
|
||||
<th>Server</th>
|
||||
<th>Channel</th>
|
||||
}
|
||||
else
|
||||
{
|
||||
<th></th>
|
||||
<th></th>
|
||||
}
|
||||
<th>Message</th>
|
||||
|
@ -73,6 +67,14 @@
|
|||
}
|
||||
</InputSelect>
|
||||
</td>
|
||||
<td>
|
||||
<InputSelect @bind-Value="reminder.RoleType" hidden="@groupHidden">
|
||||
@foreach(RaidReminder.RoleReminderType type in Enum.GetValues(typeof(RaidReminder.RoleReminderType)))
|
||||
{
|
||||
<option value="@type">@type.ToString()</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</td>
|
||||
<td>
|
||||
<InputSelect @bind-Value="reminder.DiscordServerId" hidden="@discordHidden">
|
||||
@foreach(DiscordServer item in _discordServers)
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
<p>
|
||||
<input type="checkbox" @bind="_user.AlwaysSignUpWithMainAccount" /> Always sign up with main account
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox" @bind="_user.ReminderSubscription" /> Get Raid Reminders
|
||||
</p>
|
||||
@*<p>
|
||||
<label>
|
||||
Pronouns:
|
||||
|
|
|
@ -75,18 +75,22 @@ main {
|
|||
}
|
||||
|
||||
::deep button:disabled {
|
||||
background-color: #777777;
|
||||
color: darkgrey;
|
||||
/*background-color: #777777;
|
||||
color: darkgrey;*/
|
||||
background-color: rgb(38,38,38);
|
||||
color: rgb(38,38,38);
|
||||
}
|
||||
|
||||
::deep button[type="submit"] {
|
||||
color: lightgray;
|
||||
background-color: green;
|
||||
background-color: #355835;
|
||||
border: none;
|
||||
}
|
||||
|
||||
::deep button[type="delete"] {
|
||||
color: lightgray;
|
||||
background-color: red;
|
||||
background-color: #6e2424;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue