Skip to content

Commit bc7e862

Browse files
Fix: Fixed issue where soft link details were misaligned in git repos (#16593)
1 parent 40af234 commit bc7e862

15 files changed

+251
-69
lines changed

src/Files.App/Actions/Content/Run/RunAsAdminAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ ContentPageContext.SelectedItem is not null &&
2323
ContentPageContext.PageType != ContentPageTypes.RecycleBin &&
2424
ContentPageContext.PageType != ContentPageTypes.ZipFolder &&
2525
(FileExtensionHelpers.IsExecutableFile(ContentPageContext.SelectedItem.FileExtension) ||
26-
(ContentPageContext.SelectedItem is ShortcutItem shortcut &&
26+
(ContentPageContext.SelectedItem is IShortcutItem shortcut &&
2727
shortcut.IsExecutable));
2828

2929
public RunAsAdminAction() : base("runas")

src/Files.App/Actions/Content/Run/RunAsAnotherUserAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ ContentPageContext.SelectedItem is not null &&
2323
ContentPageContext.PageType != ContentPageTypes.ZipFolder &&
2424
!FileExtensionHelpers.IsAhkFile(ContentPageContext.SelectedItem.FileExtension) &&
2525
(FileExtensionHelpers.IsExecutableFile(ContentPageContext.SelectedItem.FileExtension) ||
26-
(ContentPageContext.SelectedItem is ShortcutItem shortcut &&
26+
(ContentPageContext.SelectedItem is IShortcutItem shortcut &&
2727
shortcut.IsExecutable));
2828

2929
public RunAsAnotherUserAction() : base("runasuser")

src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public RichGlyph Glyph
2121
public bool IsExecutable =>
2222
context.ShellPage is not null &&
2323
context.HasSelection &&
24-
context.SelectedItem is ShortcutItem;
24+
context.SelectedItem is IShortcutItem;
2525

2626
public OpenFileLocationAction()
2727
{

src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,12 +520,12 @@ public static List<ContextMenuFlyoutItemViewModel> GetBaseItemMenuItems(
520520
}.Build(),
521521
new ContextMenuFlyoutItemViewModelBuilder(Commands.PinToStart)
522522
{
523-
IsVisible = selectedItems.All(x => (x.PrimaryItemAttribute == StorageItemTypes.Folder || x.IsExecutable || (x is ShortcutItem shortcutItem && FileExtensionHelpers.IsExecutableFile(shortcutItem.TargetPath))) && !x.IsArchive && !x.IsItemPinnedToStart),
523+
IsVisible = selectedItems.All(x => (x.PrimaryItemAttribute == StorageItemTypes.Folder || x.IsExecutable || (x is IShortcutItem shortcutItem && FileExtensionHelpers.IsExecutableFile(shortcutItem.TargetPath))) && !x.IsArchive && !x.IsItemPinnedToStart),
524524
ShowOnShift = true,
525525
}.Build(),
526526
new ContextMenuFlyoutItemViewModelBuilder(Commands.UnpinFromStart)
527527
{
528-
IsVisible = selectedItems.All(x => (x.PrimaryItemAttribute == StorageItemTypes.Folder || x.IsExecutable|| (x is ShortcutItem shortcutItem && FileExtensionHelpers.IsExecutableFile(shortcutItem.TargetPath))) && !x.IsArchive && x.IsItemPinnedToStart),
528+
IsVisible = selectedItems.All(x => (x.PrimaryItemAttribute == StorageItemTypes.Folder || x.IsExecutable|| (x is IShortcutItem shortcutItem && FileExtensionHelpers.IsExecutableFile(shortcutItem.TargetPath))) && !x.IsArchive && x.IsItemPinnedToStart),
529529
ShowOnShift = true,
530530
}.Build(),
531531
new ContextMenuFlyoutItemViewModel

src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static ObservableCollection<NavigationViewItemButtonStyleItem> Initialize
7575
{
7676
var firstFileExtension = listedItems.FirstOrDefault()?.FileExtension;
7777
var commonFileExt = listedItems.All(x => x.FileExtension == firstFileExtension) ? firstFileExtension : null;
78-
var compatibilityItemEnabled = listedItems.All(listedItem => FileExtensionHelpers.IsExecutableFile(listedItem is ShortcutItem sht ? sht.TargetPath : commonFileExt, true));
78+
var compatibilityItemEnabled = listedItems.All(listedItem => FileExtensionHelpers.IsExecutableFile(listedItem is IShortcutItem sht ? sht.TargetPath : commonFileExt, true));
7979
var onlyFiles = listedItems.All(listedItem => listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem.IsArchive);
8080

8181
if (!compatibilityItemEnabled)
@@ -101,7 +101,7 @@ public static ObservableCollection<NavigationViewItemButtonStyleItem> Initialize
101101
var hashItemEnabled = !(isFolder && !listedItem.IsArchive) && !isLibrary && !listedItem.IsRecycleBinItem;
102102
var detailsItemEnabled = !(isFolder && !listedItem.IsArchive) && !isLibrary && !listedItem.IsRecycleBinItem;
103103
var customizationItemEnabled = !isLibrary && (isFolder && !listedItem.IsArchive || isShortcut && !listedItem.IsLinkItem);
104-
var compatibilityItemEnabled = FileExtensionHelpers.IsExecutableFile(listedItem is ShortcutItem sht ? sht.TargetPath : fileExt, true);
104+
var compatibilityItemEnabled = FileExtensionHelpers.IsExecutableFile(listedItem is IShortcutItem sht ? sht.TargetPath : fileExt, true);
105105

106106
if (!securityItemEnabled)
107107
PropertiesNavigationViewItems.Remove(securityItem);

src/Files.App/Data/Items/ListedItem.cs

Lines changed: 133 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,13 @@ public override string ToString()
405405

406406
public bool IsFolder => PrimaryItemAttribute is StorageItemTypes.Folder;
407407
public bool IsRecycleBinItem => this is RecycleBinItem;
408-
public bool IsShortcut => this is ShortcutItem;
408+
public bool IsShortcut => this is IShortcutItem;
409409
public bool IsLibrary => this is LibraryItem;
410410
public bool IsLinkItem => IsShortcut && ((ShortcutItem)this).IsUrl;
411411
public bool IsFtpItem => this is FtpItem;
412412
public bool IsArchive => this is ZipItem;
413413
public bool IsAlternateStream => this is AlternateStreamItem;
414-
public bool IsGitItem => this is GitItem;
414+
public bool IsGitItem => this is IGitItem;
415415
public virtual bool IsExecutable => !IsFolder && FileExtensionHelpers.IsExecutableFile(ItemPath);
416416
public virtual bool IsScriptFile => FileExtensionHelpers.IsScriptFile(ItemPath);
417417
public bool IsPinned => App.QuickAccessManager.Model.PinnedFolders.Contains(itemPath);
@@ -507,7 +507,7 @@ public FtpItem(FtpListItem item, string folder) : base(null)
507507
};
508508
}
509509

510-
public sealed class ShortcutItem : ListedItem
510+
public sealed class ShortcutItem : ListedItem, IShortcutItem
511511
{
512512
public ShortcutItem(string folderRelativeId) : base(folderRelativeId)
513513
{
@@ -602,7 +602,7 @@ public override string Name
602602
}
603603
}
604604

605-
public sealed class GitItem : ListedItem
605+
public class GitItem : ListedItem, IGitItem
606606
{
607607
private volatile int statusPropertiesInitialized = 0;
608608
public bool StatusPropertiesInitialized
@@ -678,4 +678,133 @@ public string? GitLastCommitFullSha
678678
set => SetProperty(ref _GitLastCommitFullSha, value);
679679
}
680680
}
681+
public sealed class GitShortcutItem : GitItem,IShortcutItem
682+
{
683+
private volatile int statusPropertiesInitialized = 0;
684+
public bool StatusPropertiesInitialized
685+
{
686+
get => statusPropertiesInitialized == 1;
687+
set => Interlocked.Exchange(ref statusPropertiesInitialized, value ? 1 : 0);
688+
}
689+
690+
private volatile int commitPropertiesInitialized = 0;
691+
public bool CommitPropertiesInitialized
692+
{
693+
get => commitPropertiesInitialized == 1;
694+
set => Interlocked.Exchange(ref commitPropertiesInitialized, value ? 1 : 0);
695+
}
696+
697+
private Style? _UnmergedGitStatusIcon;
698+
public Style? UnmergedGitStatusIcon
699+
{
700+
get => _UnmergedGitStatusIcon;
701+
set => SetProperty(ref _UnmergedGitStatusIcon, value);
702+
}
703+
704+
private string? _UnmergedGitStatusName;
705+
public string? UnmergedGitStatusName
706+
{
707+
get => _UnmergedGitStatusName;
708+
set => SetProperty(ref _UnmergedGitStatusName, value);
709+
}
710+
711+
private DateTimeOffset? _GitLastCommitDate;
712+
public DateTimeOffset? GitLastCommitDate
713+
{
714+
get => _GitLastCommitDate;
715+
set
716+
{
717+
SetProperty(ref _GitLastCommitDate, value);
718+
GitLastCommitDateHumanized = value is DateTimeOffset dto ? dateTimeFormatter.ToShortLabel(dto) : "";
719+
}
720+
}
721+
722+
private string? _GitLastCommitDateHumanized;
723+
public string? GitLastCommitDateHumanized
724+
{
725+
get => _GitLastCommitDateHumanized;
726+
set => SetProperty(ref _GitLastCommitDateHumanized, value);
727+
}
728+
729+
private string? _GitLastCommitMessage;
730+
public string? GitLastCommitMessage
731+
{
732+
get => _GitLastCommitMessage;
733+
set => SetProperty(ref _GitLastCommitMessage, value);
734+
}
735+
736+
private string? _GitCommitAuthor;
737+
public string? GitLastCommitAuthor
738+
{
739+
get => _GitCommitAuthor;
740+
set => SetProperty(ref _GitCommitAuthor, value);
741+
}
742+
743+
private string? _GitLastCommitSha;
744+
public string? GitLastCommitSha
745+
{
746+
get => _GitLastCommitSha;
747+
set => SetProperty(ref _GitLastCommitSha, value);
748+
}
749+
750+
private string? _GitLastCommitFullSha;
751+
public string? GitLastCommitFullSha
752+
{
753+
get => _GitLastCommitFullSha;
754+
set => SetProperty(ref _GitLastCommitFullSha, value);
755+
}
756+
757+
public string TargetPath { get; set; }
758+
759+
public override string Name
760+
=> IsSymLink ? base.Name : Path.GetFileNameWithoutExtension(ItemNameRaw); // Always hide extension for shortcuts
761+
762+
public string Arguments { get; set; }
763+
public string WorkingDirectory { get; set; }
764+
public bool RunAsAdmin { get; set; }
765+
public bool IsUrl { get; set; }
766+
public bool IsSymLink { get; set; }
767+
public override bool IsExecutable => FileExtensionHelpers.IsExecutableFile(TargetPath, true);
768+
}
769+
public interface IGitItem
770+
{
771+
public bool StatusPropertiesInitialized { get ; set; }
772+
public bool CommitPropertiesInitialized { get; set; }
773+
774+
public Style? UnmergedGitStatusIcon{ get; set; }
775+
776+
public string? UnmergedGitStatusName{ get; set; }
777+
778+
public DateTimeOffset? GitLastCommitDate{ get; set; }
779+
780+
public string? GitLastCommitDateHumanized{ get; set; }
781+
782+
public string? GitLastCommitMessage{ get; set; }
783+
784+
public string? GitLastCommitAuthor{ get; set; }
785+
786+
public string? GitLastCommitSha{ get; set; }
787+
788+
public string? GitLastCommitFullSha{ get; set; }
789+
790+
public string ItemPath
791+
{
792+
get;
793+
set;
794+
}
795+
}
796+
public interface IShortcutItem
797+
{
798+
public string TargetPath { get; set; }
799+
public string Name { get; }
800+
public string Arguments { get; set; }
801+
public string WorkingDirectory { get; set; }
802+
public bool RunAsAdmin { get; set; }
803+
public bool IsUrl { get; set; }
804+
public bool IsSymLink { get; set; }
805+
806+
public bool IsExecutable { get; }
807+
808+
809+
}
681810
}

src/Files.App/Helpers/UI/UIFilesystemHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ public static async Task<bool> HandleShortcutCannotBeCreated(string shortcutName
250250
/// <param name="arguments"></param>
251251
/// <param name="workingDir"></param>
252252
/// <param name="runAsAdmin"></param>
253-
public static void UpdateShortcutItemProperties(ShortcutItem item, string targetPath, string arguments, string workingDir, bool runAsAdmin)
253+
public static void UpdateShortcutItemProperties(IShortcutItem item, string targetPath, string arguments, string workingDir, bool runAsAdmin)
254254
{
255255
item.TargetPath = Environment.ExpandEnvironmentVariables(targetPath);
256256
item.Arguments = arguments;

src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs

Lines changed: 93 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -275,26 +275,51 @@ CancellationToken cancellationToken
275275
if (isSymlink)
276276
{
277277
var targetPath = Win32Helper.ParseSymLink(itemPath);
278-
279-
return new ShortcutItem(null)
278+
if (isGitRepo)
280279
{
281-
PrimaryItemAttribute = StorageItemTypes.File,
282-
FileExtension = itemFileExtension,
283-
IsHiddenItem = isHidden,
284-
Opacity = opacity,
285-
FileImage = null,
286-
LoadFileIcon = itemThumbnailImgVis,
287-
ItemNameRaw = itemName,
288-
ItemDateModifiedReal = itemModifiedDate,
289-
ItemDateAccessedReal = itemLastAccessDate,
290-
ItemDateCreatedReal = itemCreatedDate,
291-
ItemType = "Shortcut".GetLocalizedResource(),
292-
ItemPath = itemPath,
293-
FileSize = itemSize,
294-
FileSizeBytes = itemSizeBytes,
295-
TargetPath = targetPath,
296-
IsSymLink = true
297-
};
280+
return new GitShortcutItem()
281+
{
282+
PrimaryItemAttribute = StorageItemTypes.File,
283+
FileExtension = itemFileExtension,
284+
IsHiddenItem = isHidden,
285+
Opacity = opacity,
286+
FileImage = null,
287+
LoadFileIcon = itemThumbnailImgVis,
288+
ItemNameRaw = itemName,
289+
ItemDateModifiedReal = itemModifiedDate,
290+
ItemDateAccessedReal = itemLastAccessDate,
291+
ItemDateCreatedReal = itemCreatedDate,
292+
ItemType = "Shortcut".GetLocalizedResource(),
293+
ItemPath = itemPath,
294+
FileSize = itemSize,
295+
FileSizeBytes = itemSizeBytes,
296+
TargetPath = targetPath,
297+
IsSymLink = true,
298+
};
299+
}
300+
else
301+
{
302+
return new ShortcutItem(null)
303+
{
304+
PrimaryItemAttribute = StorageItemTypes.File,
305+
FileExtension = itemFileExtension,
306+
IsHiddenItem = isHidden,
307+
Opacity = opacity,
308+
FileImage = null,
309+
LoadFileIcon = itemThumbnailImgVis,
310+
ItemNameRaw = itemName,
311+
ItemDateModifiedReal = itemModifiedDate,
312+
ItemDateAccessedReal = itemLastAccessDate,
313+
ItemDateCreatedReal = itemCreatedDate,
314+
ItemType = "Shortcut".GetLocalizedResource(),
315+
ItemPath = itemPath,
316+
FileSize = itemSize,
317+
FileSizeBytes = itemSizeBytes,
318+
TargetPath = targetPath,
319+
IsSymLink = true
320+
};
321+
}
322+
298323
}
299324
else if (FileExtensionHelpers.IsShortcutOrUrlFile(findData.cFileName))
300325
{
@@ -304,28 +329,56 @@ CancellationToken cancellationToken
304329
if (shInfo is null)
305330
return null;
306331

307-
return new ShortcutItem(null)
332+
if (isGitRepo)
308333
{
309-
PrimaryItemAttribute = shInfo.IsFolder ? StorageItemTypes.Folder : StorageItemTypes.File,
310-
FileExtension = itemFileExtension,
311-
IsHiddenItem = isHidden,
312-
Opacity = opacity,
313-
FileImage = null,
314-
LoadFileIcon = !shInfo.IsFolder && itemThumbnailImgVis,
315-
ItemNameRaw = itemName,
316-
ItemDateModifiedReal = itemModifiedDate,
317-
ItemDateAccessedReal = itemLastAccessDate,
318-
ItemDateCreatedReal = itemCreatedDate,
319-
ItemType = isUrl ? "ShortcutWebLinkFileType".GetLocalizedResource() : "Shortcut".GetLocalizedResource(),
320-
ItemPath = itemPath,
321-
FileSize = itemSize,
322-
FileSizeBytes = itemSizeBytes,
323-
TargetPath = shInfo.TargetPath,
324-
Arguments = shInfo.Arguments,
325-
WorkingDirectory = shInfo.WorkingDirectory,
326-
RunAsAdmin = shInfo.RunAsAdmin,
327-
IsUrl = isUrl,
328-
};
334+
return new GitShortcutItem()
335+
{
336+
PrimaryItemAttribute = shInfo.IsFolder ? StorageItemTypes.Folder : StorageItemTypes.File,
337+
FileExtension = itemFileExtension,
338+
IsHiddenItem = isHidden,
339+
Opacity = opacity,
340+
FileImage = null,
341+
LoadFileIcon = !shInfo.IsFolder && itemThumbnailImgVis,
342+
ItemNameRaw = itemName,
343+
ItemDateModifiedReal = itemModifiedDate,
344+
ItemDateAccessedReal = itemLastAccessDate,
345+
ItemDateCreatedReal = itemCreatedDate,
346+
ItemType = isUrl ? "ShortcutWebLinkFileType".GetLocalizedResource() : "Shortcut".GetLocalizedResource(),
347+
ItemPath = itemPath,
348+
FileSize = itemSize,
349+
FileSizeBytes = itemSizeBytes,
350+
TargetPath = shInfo.TargetPath,
351+
Arguments = shInfo.Arguments,
352+
WorkingDirectory = shInfo.WorkingDirectory,
353+
RunAsAdmin = shInfo.RunAsAdmin,
354+
IsUrl = isUrl,
355+
};
356+
}
357+
else
358+
{
359+
return new ShortcutItem(null)
360+
{
361+
PrimaryItemAttribute = shInfo.IsFolder ? StorageItemTypes.Folder : StorageItemTypes.File,
362+
FileExtension = itemFileExtension,
363+
IsHiddenItem = isHidden,
364+
Opacity = opacity,
365+
FileImage = null,
366+
LoadFileIcon = !shInfo.IsFolder && itemThumbnailImgVis,
367+
ItemNameRaw = itemName,
368+
ItemDateModifiedReal = itemModifiedDate,
369+
ItemDateAccessedReal = itemLastAccessDate,
370+
ItemDateCreatedReal = itemCreatedDate,
371+
ItemType = isUrl ? "ShortcutWebLinkFileType".GetLocalizedResource() : "Shortcut".GetLocalizedResource(),
372+
ItemPath = itemPath,
373+
FileSize = itemSize,
374+
FileSizeBytes = itemSizeBytes,
375+
TargetPath = shInfo.TargetPath,
376+
Arguments = shInfo.Arguments,
377+
WorkingDirectory = shInfo.WorkingDirectory,
378+
RunAsAdmin = shInfo.RunAsAdmin,
379+
IsUrl = isUrl,
380+
};
381+
}
329382
}
330383
else if (App.LibraryManager.TryGetLibrary(itemPath, out LibraryLocationItem library))
331384
{

src/Files.App/ViewModels/Properties/CompatibilityViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public string SelectedHighDpiOverride
8181

8282
public CompatibilityViewModel(ListedItem item)
8383
{
84-
ItemPath = item is ShortcutItem shortcutItem ? shortcutItem.TargetPath : item.ItemPath;
84+
ItemPath = item is IShortcutItem shortcutItem ? shortcutItem.TargetPath : item.ItemPath;
8585

8686
CompatibilityOptions = WindowsCompatibilityService.GetCompatibilityOptionsForPath(ItemPath);
8787

0 commit comments

Comments
 (0)