Initial moving of DLC/updates to UI.Common
This commit is contained in:
parent
2e93c96c86
commit
e1171086f4
13 changed files with 167 additions and 79 deletions
|
@ -497,7 +497,6 @@ namespace Ryujinx.UI.App.Common
|
||||||
: IntegrityCheckLevel.None;
|
: IntegrityCheckLevel.None;
|
||||||
|
|
||||||
using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem);
|
using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem);
|
||||||
// Dictionary<ulong, ContentMetaData> updates = pfs.GetContentData(ContentMetaType.AddOnContent, _virtualFileSystem, checkLevel);
|
|
||||||
|
|
||||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||||
{
|
{
|
||||||
|
@ -517,11 +516,7 @@ namespace Ryujinx.UI.App.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (titleUpdates.Count == 0)
|
return titleUpdates.Count != 0;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
src/Ryujinx.UI.Common/Models/DownloadableContentModel.cs
Normal file
23
src/Ryujinx.UI.Common/Models/DownloadableContentModel.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
namespace Ryujinx.UI.Common.Models
|
||||||
|
{
|
||||||
|
public class DownloadableContentModel
|
||||||
|
{
|
||||||
|
public ulong TitleId { get; }
|
||||||
|
public string ContainerPath { get; }
|
||||||
|
public string FullPath { get; }
|
||||||
|
public bool Enabled { get; }
|
||||||
|
public bool IsBundled { get; }
|
||||||
|
|
||||||
|
public string FileName => System.IO.Path.GetFileName(ContainerPath);
|
||||||
|
public string TitleIdStr => TitleId.ToString("X16");
|
||||||
|
|
||||||
|
public DownloadableContentModel(ulong titleId, string containerPath, string fullPath, bool enabled)
|
||||||
|
{
|
||||||
|
TitleId = titleId;
|
||||||
|
ContainerPath = containerPath;
|
||||||
|
FullPath = fullPath;
|
||||||
|
Enabled = enabled;
|
||||||
|
IsBundled = System.IO.Path.GetExtension(containerPath)?.ToLower() == ".xci";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/Ryujinx.UI.Common/Models/TitleUpdateModel.cs
Normal file
22
src/Ryujinx.UI.Common/Models/TitleUpdateModel.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Ryujinx.UI.Common.Models
|
||||||
|
{
|
||||||
|
public class TitleUpdateModel
|
||||||
|
{
|
||||||
|
public ulong TitleId { get; }
|
||||||
|
public ulong Version { get; }
|
||||||
|
public string DisplayVersion { get; }
|
||||||
|
public string Path { get; }
|
||||||
|
public bool IsBundled { get; }
|
||||||
|
|
||||||
|
public string TitleIdStr => TitleId.ToString("X16");
|
||||||
|
|
||||||
|
public TitleUpdateModel(ulong titleId, ulong version, string displayVersion, string path)
|
||||||
|
{
|
||||||
|
TitleId = titleId;
|
||||||
|
Version = version;
|
||||||
|
DisplayVersion = displayVersion;
|
||||||
|
Path = path;
|
||||||
|
IsBundled = System.IO.Path.GetExtension(path)?.ToLower() == ".xci";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
src/Ryujinx/UI/Helpers/DownloadableContentLabelConverter.cs
Normal file
42
src/Ryujinx/UI/Helpers/DownloadableContentLabelConverter.cs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
|
{
|
||||||
|
internal class DownloadableContentLabelConverter : IMultiValueConverter
|
||||||
|
{
|
||||||
|
public static DownloadableContentLabelConverter Instance = new();
|
||||||
|
|
||||||
|
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (values.Any(it => it is UnsetValueType))
|
||||||
|
{
|
||||||
|
return BindingOperations.DoNothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.Count != 2 || !targetType.IsAssignableFrom(typeof(string)))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values is not [string label, bool isBundled])
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isBundled ? $"{LocaleManager.Instance[LocaleKeys.TitleBundledDlcLabel]} {label}" : label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object[] ConvertBack(object[] values, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
42
src/Ryujinx/UI/Helpers/TitleUpdateLabelConverter.cs
Normal file
42
src/Ryujinx/UI/Helpers/TitleUpdateLabelConverter.cs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
|
{
|
||||||
|
internal class TitleUpdateLabelConverter : IMultiValueConverter
|
||||||
|
{
|
||||||
|
public static TitleUpdateLabelConverter Instance = new();
|
||||||
|
|
||||||
|
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (values.Any(it => it is UnsetValueType))
|
||||||
|
{
|
||||||
|
return BindingOperations.DoNothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.Count != 2 || !targetType.IsAssignableFrom(typeof(string)))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values is not [string label, bool isBundled])
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = isBundled ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel;
|
||||||
|
return LocaleManager.Instance.UpdateAndGetDynamicValue(key, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object[] ConvertBack(object[] values, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,39 +0,0 @@
|
||||||
using Ryujinx.Ava.Common.Locale;
|
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models
|
|
||||||
{
|
|
||||||
public class DownloadableContentModel : BaseModel
|
|
||||||
{
|
|
||||||
private bool _enabled;
|
|
||||||
|
|
||||||
public bool Enabled
|
|
||||||
{
|
|
||||||
get => _enabled;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_enabled = value;
|
|
||||||
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string TitleId { get; }
|
|
||||||
public string ContainerPath { get; }
|
|
||||||
public string FullPath { get; }
|
|
||||||
|
|
||||||
public string FileName => Path.GetFileName(ContainerPath);
|
|
||||||
|
|
||||||
public string Label =>
|
|
||||||
Path.GetExtension(FileName)?.ToLower() == ".xci" ? $"{LocaleManager.Instance[LocaleKeys.TitleBundledDlcLabel]} {FileName}" : FileName;
|
|
||||||
|
|
||||||
public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled)
|
|
||||||
{
|
|
||||||
TitleId = titleId;
|
|
||||||
ContainerPath = containerPath;
|
|
||||||
FullPath = fullPath;
|
|
||||||
Enabled = enabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using Ryujinx.Ava.Common.Locale;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models
|
|
||||||
{
|
|
||||||
public class TitleUpdateModel
|
|
||||||
{
|
|
||||||
public uint Version { get; }
|
|
||||||
public string Path { get; }
|
|
||||||
public string Label { get; }
|
|
||||||
|
|
||||||
public TitleUpdateModel(uint version, string displayVersion, string path)
|
|
||||||
{
|
|
||||||
Version = version;
|
|
||||||
Label = LocaleManager.Instance.UpdateAndGetDynamicValue(
|
|
||||||
System.IO.Path.GetExtension(path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
|
|
||||||
displayVersion
|
|
||||||
);
|
|
||||||
Path = path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,6 +19,7 @@ using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.UI.App.Common;
|
||||||
|
using Ryujinx.UI.Common.Models;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -142,7 +143,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
|
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
|
||||||
if (nca != null)
|
if (nca != null)
|
||||||
{
|
{
|
||||||
var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"),
|
var content = new DownloadableContentModel(nca.Header.TitleId,
|
||||||
downloadableContentContainer.ContainerPath,
|
downloadableContentContainer.ContainerPath,
|
||||||
downloadableContentNca.FullPath,
|
downloadableContentNca.FullPath,
|
||||||
downloadableContentNca.Enabled);
|
downloadableContentNca.Enabled);
|
||||||
|
@ -183,7 +184,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
if (arg is DownloadableContentModel content)
|
if (arg is DownloadableContentModel content)
|
||||||
{
|
{
|
||||||
return string.IsNullOrWhiteSpace(_search) || content.FileName.ToLower().Contains(_search.ToLower()) || content.TitleId.ToLower().Contains(_search.ToLower());
|
return string.IsNullOrWhiteSpace(_search) || content.FileName.ToLower().Contains(_search.ToLower()) || content.TitleIdStr.ToLower().Contains(_search.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -261,7 +262,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true);
|
var content = new DownloadableContentModel(nca.Header.TitleId, path, fileEntry.FullPath, true);
|
||||||
DownloadableContents.Add(content);
|
DownloadableContents.Add(content);
|
||||||
Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(content));
|
Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(content));
|
||||||
|
|
||||||
|
@ -327,7 +328,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
container.DownloadableContentNcaList.Add(new DownloadableContentNca
|
container.DownloadableContentNcaList.Add(new DownloadableContentNca
|
||||||
{
|
{
|
||||||
Enabled = downloadableContent.Enabled,
|
Enabled = downloadableContent.Enabled,
|
||||||
TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16),
|
TitleId = downloadableContent.TitleId,
|
||||||
FullPath = downloadableContent.FullPath,
|
FullPath = downloadableContent.FullPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.UI.App.Common;
|
||||||
using Ryujinx.UI.Common.Configuration;
|
using Ryujinx.UI.Common.Configuration;
|
||||||
|
using Ryujinx.UI.Common.Models;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -190,7 +191,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
||||||
|
|
||||||
var displayVersion = controlData.DisplayVersionString.ToString();
|
var displayVersion = controlData.DisplayVersionString.ToString();
|
||||||
var update = new TitleUpdateModel(content.Version.Version, displayVersion, path);
|
var update = new TitleUpdateModel(content.ApplicationId, content.Version.Version, displayVersion, path);
|
||||||
|
|
||||||
TitleUpdates.Add(update);
|
TitleUpdates.Add(update);
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,17 @@
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models"
|
xmlns:models="clr-namespace:Ryujinx.UI.Common.Models;assembly=Ryujinx.UI.Common"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||||
Width="500"
|
Width="500"
|
||||||
Height="380"
|
Height="380"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:DataType="viewModels:DownloadableContentManagerViewModel"
|
x:DataType="viewModels:DownloadableContentManagerViewModel"
|
||||||
Focusable="True">
|
Focusable="True">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<helpers:DownloadableContentLabelConverter x:Key="DownloadableContentLabel" />
|
||||||
|
</UserControl.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
@ -96,8 +100,14 @@
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
MaxLines="2"
|
MaxLines="2"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
TextTrimming="CharacterEllipsis"
|
TextTrimming="CharacterEllipsis">
|
||||||
Text="{Binding Label}" />
|
<TextBlock.Text>
|
||||||
|
<MultiBinding Converter="{StaticResource DownloadableContentLabel}">
|
||||||
|
<Binding Path="FileName" />
|
||||||
|
<Binding Path="IsBundled" />
|
||||||
|
</MultiBinding>
|
||||||
|
</TextBlock.Text>
|
||||||
|
</TextBlock>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="10 0"
|
Margin="10 0"
|
||||||
|
|
|
@ -8,6 +8,7 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.UI.App.Common;
|
||||||
using Ryujinx.UI.Common.Helper;
|
using Ryujinx.UI.Common.Helper;
|
||||||
|
using Ryujinx.UI.Common.Models;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
|
@ -92,7 +93,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||||
|
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
{
|
{
|
||||||
ViewModel.DownloadableContents[index].Enabled = true;
|
// ViewModel.DownloadableContents[index].Enabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +106,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||||
|
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
{
|
{
|
||||||
ViewModel.DownloadableContents[index].Enabled = false;
|
// ViewModel.DownloadableContents[index].Enabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,17 @@
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models"
|
xmlns:models="clr-namespace:Ryujinx.UI.Common.Models;assembly=Ryujinx.UI.Common"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||||
Width="500"
|
Width="500"
|
||||||
Height="300"
|
Height="300"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:DataType="viewModels:TitleUpdateViewModel"
|
x:DataType="viewModels:TitleUpdateViewModel"
|
||||||
Focusable="True">
|
Focusable="True">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<helpers:TitleUpdateLabelConverter x:Key="TitleUpdateLabel" />
|
||||||
|
</UserControl.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
|
@ -38,8 +42,14 @@
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap">
|
||||||
Text="{Binding Label}" />
|
<TextBlock.Text>
|
||||||
|
<MultiBinding Converter="{StaticResource TitleUpdateLabel}">
|
||||||
|
<Binding Path="DisplayVersion" />
|
||||||
|
<Binding Path="IsBundled" />
|
||||||
|
</MultiBinding>
|
||||||
|
</TextBlock.Text>
|
||||||
|
</TextBlock>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Spacing="10"
|
Spacing="10"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.UI.App.Common;
|
||||||
using Ryujinx.UI.Common.Helper;
|
using Ryujinx.UI.Common.Helper;
|
||||||
|
using Ryujinx.UI.Common.Models;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
|
|
Loading…
Reference in a new issue