- Added native confirmation sheets in the Mac client.

Manual content actions now confirm clearly:
Draft shows “Draft Saved”
Publish shows “Published”
Update shows “Updated”
Delete first asks “Delete Post/Page?” and then shows “Deleted” after the 
API succeeds
Autosave stays quiet so it does not spam popups.
Updated:
[macclient/AppKitClient/main.m](/Users/tyemeclifford/Documents/GH/blog/macclient/AppKitClient/main.m)
[macclient/README.md](/Users/tyemeclifford/Documents/GH/blog/macclient/README.md)
[CHANGELOG.md](/Users/tyemeclifford/Documents/GH/blog/CHANGELOG.md)
This commit is contained in:
Ty Clifford
2026-07-05 21:22:17 -04:00
parent 3e7cea4491
commit df56bfd57c
8 changed files with 366 additions and 38 deletions
+310 -28
View File
@@ -72,6 +72,22 @@ static NSString *TCMSFormatBytes(id value) {
}
@end
@interface TCMSTableView : NSTableView
@end
@implementation TCMSTableView
- (NSMenu *)menuForEvent:(NSEvent *)event {
NSPoint point = [self convertPoint:event.locationInWindow fromView:nil];
NSInteger row = [self rowAtPoint:point];
if (row >= 0) {
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
} else {
return nil;
}
return [super menuForEvent:event];
}
@end
@interface TCMSAPIClient : NSObject
@property (nonatomic, copy) NSString *baseURLString;
@property (nonatomic, copy) NSString *username;
@@ -270,6 +286,8 @@ static NSString *TCMSFormatBytes(id value) {
@property (nonatomic, strong) NSImageView *mediaImageView;
@property (nonatomic, strong) AVPlayerView *mediaPlayerView;
@property (nonatomic, strong) NSButton *openMediaButton;
@property (nonatomic, strong) NSButton *mediaURLCopyButton;
@property (nonatomic, strong) NSButton *mediaEmbedCopyButton;
@property (nonatomic, copy) NSString *mediaPreviewURLString;
@property (nonatomic, strong) NSPopUpButton *commentStatusPopup;
@property (nonatomic, strong) NSButton *previousPageButton;
@@ -413,8 +431,8 @@ static NSString *TCMSFormatBytes(id value) {
NSMenuItem *appItem = [NSMenuItem new];
[mainMenu addItem:appItem];
NSMenu *appMenu = [[NSMenu alloc] initWithTitle:@"TCMS Mac Client"];
[appMenu addItemWithTitle:@"About TCMS Mac Client" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
NSMenu *appMenu = [[NSMenu alloc] initWithTitle:@"TCMS"];
[appMenu addItemWithTitle:@"About TCMS" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
NSMenuItem *settings = [appMenu addItemWithTitle:@"Settings..." action:@selector(showSettingsFromMenu:) keyEquivalent:@","];
settings.target = self;
@@ -425,12 +443,12 @@ static NSString *TCMSFormatBytes(id value) {
[appMenu addItem:servicesItem];
NSApp.servicesMenu = servicesMenu;
[appMenu addItem:[NSMenuItem separatorItem]];
[appMenu addItemWithTitle:@"Hide TCMS Mac Client" action:@selector(hide:) keyEquivalent:@"h"];
[appMenu addItemWithTitle:@"Hide TCMS" action:@selector(hide:) keyEquivalent:@"h"];
NSMenuItem *hideOthers = [appMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
hideOthers.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption;
[appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
[appMenu addItemWithTitle:@"Quit TCMS Mac Client" action:@selector(terminate:) keyEquivalent:@"q"];
[appMenu addItemWithTitle:@"Quit TCMS" action:@selector(terminate:) keyEquivalent:@"q"];
appItem.submenu = appMenu;
NSMenuItem *fileItem = [NSMenuItem new];
@@ -523,6 +541,8 @@ static NSString *TCMSFormatBytes(id value) {
self.mediaImageView = nil;
self.mediaPlayerView = nil;
self.openMediaButton = nil;
self.mediaURLCopyButton = nil;
self.mediaEmbedCopyButton = nil;
self.mediaPreviewURLString = nil;
}
@@ -534,12 +554,13 @@ static NSString *TCMSFormatBytes(id value) {
NSTextField *heading = [self label:[section capitalizedString] frame:NSMakeRect(20, 690, 180, 28) font:[NSFont boldSystemFontOfSize:24]];
[self.workspace addSubview:heading];
[self addButton:@"Refresh" frame:NSMakeRect(210, 690, 90, 30) action:@selector(refreshCurrentSection)];
[self addButton:@"New" frame:NSMakeRect(310, 690, 70, 30) action:@selector(newContent)];
[self addButton:@"Save Draft" frame:NSMakeRect(390, 690, 100, 30) action:@selector(saveDraft)];
[self addButton:@"Publish" frame:NSMakeRect(500, 690, 90, 30) action:@selector(publishContent)];
[self addButton:@"Update" frame:NSMakeRect(600, 690, 80, 30) action:@selector(updateContent)];
[self addButton:@"Delete" frame:NSMakeRect(690, 690, 80, 30) action:@selector(deleteContent)];
[self addButton:@"Refresh" frame:NSMakeRect(210, 690, 78, 30) action:@selector(refreshCurrentSection)];
[self addButton:@"New" frame:NSMakeRect(296, 690, 58, 30) action:@selector(newContent)];
[self addButton:@"Draft" frame:NSMakeRect(362, 690, 70, 30) action:@selector(saveDraft)];
[self addButton:@"Update" frame:NSMakeRect(440, 690, 76, 30) action:@selector(updateContent)];
[self addButton:@"Publish" frame:NSMakeRect(524, 690, 78, 30) action:@selector(publishContent)];
[self addButton:@"Upload Media" frame:NSMakeRect(610, 690, 118, 30) action:@selector(uploadEditorMedia)];
[self addButton:@"Delete" frame:NSMakeRect(736, 690, 70, 30) action:@selector(deleteContent)];
NSScrollView *listScroll = [self buildTableWithFrame:NSMakeRect(20, 56, 280, 614) columns:@[@"title", @"status"]];
listScroll.autoresizingMask = NSViewHeightSizable | NSViewMaxXMargin;
@@ -649,15 +670,20 @@ static NSString *TCMSFormatBytes(id value) {
[self clearWorkspace];
self.contentListPage = 1;
[self.workspace addSubview:[self label:@"Media" frame:NSMakeRect(20, 690, 180, 28) font:[NSFont boldSystemFontOfSize:24]]];
[self addButton:@"Refresh" frame:NSMakeRect(210, 690, 90, 30) action:@selector(refreshCurrentSection)];
[self addButton:@"Upload" frame:NSMakeRect(310, 690, 90, 30) action:@selector(uploadMedia)];
self.openMediaButton = [self addButton:@"Open" frame:NSMakeRect(410, 690, 80, 30) action:@selector(openSelectedMedia:)];
[self addButton:@"Refresh" frame:NSMakeRect(210, 690, 78, 30) action:@selector(refreshCurrentSection)];
[self addButton:@"Upload" frame:NSMakeRect(296, 690, 78, 30) action:@selector(uploadMedia)];
self.openMediaButton = [self addButton:@"Open" frame:NSMakeRect(382, 690, 66, 30) action:@selector(openSelectedMedia:)];
self.openMediaButton.enabled = NO;
self.mediaURLCopyButton = [self addButton:@"Copy URL" frame:NSMakeRect(456, 690, 88, 30) action:@selector(copySelectedMediaURL:)];
self.mediaURLCopyButton.enabled = NO;
self.mediaEmbedCopyButton = [self addButton:@"Copy Embed" frame:NSMakeRect(552, 690, 104, 30) action:@selector(copySelectedMediaEmbed:)];
self.mediaEmbedCopyButton.enabled = NO;
NSScrollView *mediaScroll = [self buildTableWithFrame:NSMakeRect(20, 56, 420, 614) columns:@[@"name", @"mime", @"size"]];
mediaScroll.autoresizingMask = NSViewHeightSizable | NSViewMaxXMargin;
self.tableView.target = self;
self.tableView.doubleAction = @selector(openSelectedMedia:);
[self configureMediaContextMenu];
self.previousPageButton = [NSButton buttonWithTitle:@"Previous" target:self action:@selector(previousContentPage)];
self.previousPageButton.frame = NSMakeRect(20, 20, 78, 28);
@@ -791,7 +817,7 @@ static NSString *TCMSFormatBytes(id value) {
NSScrollView *scroll = [[NSScrollView alloc] initWithFrame:frame];
scroll.borderType = NSBezelBorder;
scroll.hasVerticalScroller = YES;
self.tableView = [[NSTableView alloc] initWithFrame:scroll.bounds];
self.tableView = [[TCMSTableView alloc] initWithFrame:scroll.bounds];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.columnAutoresizingStyle = NSTableViewUniformColumnAutoresizingStyle;
@@ -996,6 +1022,9 @@ static NSString *TCMSFormatBytes(id value) {
if (action == @selector(updateContent) || action == @selector(saveDraft) || action == @selector(publishContent)) {
return [self isContentSection] && self.markdownView != nil;
}
if (action == @selector(copySelectedMediaURL:) || action == @selector(copySelectedMediaEmbed:) || action == @selector(openSelectedMedia:)) {
return [self.section isEqualToString:@"media"] && [self selectedTableItem] != nil;
}
return YES;
}
@@ -1145,20 +1174,73 @@ static NSString *TCMSFormatBytes(id value) {
return item;
}
- (NSString *)contentKindForItem:(NSDictionary *)item capitalized:(BOOL)capitalized {
NSString *type = [TCMSString(item[@"type"]) lowercaseString];
NSString *kind = [type isEqualToString:@"page"] ? @"page" : @"post";
return capitalized ? [kind capitalizedString] : kind;
}
- (NSString *)contentTitleForItem:(NSDictionary *)item {
NSString *title = TCMSString(item[@"title"]);
if (title.length == 0) {
title = TCMSString(item[@"slug"]);
}
return title.length > 0 ? title : @"Untitled";
}
- (NSString *)confirmationMessageForItem:(NSDictionary *)item action:(NSString *)action {
NSString *kind = [self contentKindForItem:item capitalized:YES];
NSString *title = [self contentTitleForItem:item];
return [NSString stringWithFormat:@"%@ \"%@\" was %@.", kind, title, action];
}
- (void)showConfirmationWithTitle:(NSString *)title message:(NSString *)message {
NSAlert *alert = [NSAlert new];
alert.alertStyle = NSAlertStyleInformational;
alert.messageText = title.length > 0 ? title : @"Done";
alert.informativeText = message.length > 0 ? message : @"The action completed successfully.";
[alert addButtonWithTitle:@"OK"];
if (self.window) {
[alert beginSheetModalForWindow:self.window completionHandler:nil];
} else {
[alert runModal];
}
}
- (void)confirmDestructiveActionWithTitle:(NSString *)title message:(NSString *)message confirmButton:(NSString *)confirmButton completion:(void (^)(BOOL confirmed))completion {
NSAlert *alert = [NSAlert new];
alert.alertStyle = NSAlertStyleWarning;
alert.messageText = title.length > 0 ? title : @"Confirm";
alert.informativeText = message.length > 0 ? message : @"This action cannot be undone.";
NSButton *primary = [alert addButtonWithTitle:confirmButton.length > 0 ? confirmButton : @"Delete"];
primary.keyEquivalent = @"\r";
[alert addButtonWithTitle:@"Cancel"];
void (^handler)(NSModalResponse) = ^(NSModalResponse response) {
if (completion) {
completion(response == NSAlertFirstButtonReturn);
}
};
if (self.window) {
[alert beginSheetModalForWindow:self.window completionHandler:handler];
} else {
handler([alert runModal]);
}
}
- (void)saveDraft {
[self saveContentWithStatus:@"draft" message:@"Draft saved."];
[self saveContentWithStatus:@"draft" message:@"Draft saved." confirmationTitle:@"Draft Saved" confirmationAction:@"saved as a draft"];
}
- (void)publishContent {
[self saveContentWithStatus:@"published" message:@"Content published."];
[self saveContentWithStatus:@"published" message:@"Content published." confirmationTitle:@"Published" confirmationAction:@"published"];
}
- (void)saveContentWithStatus:(NSString *)status {
[self saveContentWithStatus:status message:@"Content saved."];
[self saveContentWithStatus:status message:@"Content saved." confirmationTitle:nil confirmationAction:nil];
}
- (void)updateContent {
[self saveContentWithStatus:nil message:@"Content updated."];
[self saveContentWithStatus:nil message:@"Content updated." confirmationTitle:@"Updated" confirmationAction:@"updated"];
}
- (NSString *)currentEditorMarkdown {
@@ -1172,7 +1254,7 @@ static NSString *TCMSFormatBytes(id value) {
return self.markdownView.string ?: @"";
}
- (void)saveContentWithStatus:(NSString *)status message:(NSString *)message {
- (void)saveContentWithStatus:(NSString *)status message:(NSString *)message confirmationTitle:(NSString *)confirmationTitle confirmationAction:(NSString *)confirmationAction {
if (![self isContentSection] || !self.markdownView) {
[self setStatus:@"Open Posts or Pages to save content."];
return;
@@ -1204,23 +1286,36 @@ static NSString *TCMSFormatBytes(id value) {
[self loadEditorFields];
[self refreshContentType:TCMSString(self.editorItem[@"type"]).length ? TCMSString(self.editorItem[@"type"]) : @"post"];
[self setStatus:serverEchoedSubmittedMarkdown ? (message ?: @"Content saved.") : @"Server response did not include the updated Markdown. Editor changes preserved."];
if (serverEchoedSubmittedMarkdown && confirmationTitle.length > 0) {
[self showConfirmationWithTitle:confirmationTitle message:[self confirmationMessageForItem:self.editorItem action:confirmationAction ?: @"saved"]];
}
}];
}
- (void)deleteContent {
NSString *slug = TCMSString(self.editorItem[@"slug"]);
NSDictionary *item = [self.editorItem copy] ?: @{};
NSString *slug = TCMSString(item[@"slug"]);
if (slug.length == 0) {
[self setStatus:@"Nothing selected to delete."];
return;
}
[self.client requestAction:@"content.delete" query:@{} method:@"POST" body:@{@"slug": slug} completion:^(NSDictionary *json, NSError *error) {
if (error) {
[self setStatus:error.localizedDescription];
NSString *kind = [self contentKindForItem:item capitalized:YES];
NSString *title = [self contentTitleForItem:item];
NSString *message = [NSString stringWithFormat:@"Delete \"%@\" from the server? This cannot be undone.", title];
[self confirmDestructiveActionWithTitle:[NSString stringWithFormat:@"Delete %@?", kind] message:message confirmButton:@"Delete" completion:^(BOOL confirmed) {
if (!confirmed) {
return;
}
[self setStatus:@"Content deleted."];
[self newContent];
[self refreshCurrentSection];
[self.client requestAction:@"content.delete" query:@{} method:@"POST" body:@{@"slug": slug} completion:^(NSDictionary *json, NSError *error) {
if (error) {
[self setStatus:error.localizedDescription];
return;
}
[self setStatus:@"Content deleted."];
[self newContent];
[self refreshCurrentSection];
[self showConfirmationWithTitle:@"Deleted" message:[self confirmationMessageForItem:item action:@"deleted"]];
}];
}];
}
@@ -1237,7 +1332,7 @@ static NSString *TCMSFormatBytes(id value) {
}];
}
- (void)uploadMedia {
- (void)chooseAndUploadMediaWithCompletion:(void (^)(NSDictionary *item))completion {
NSOpenPanel *panel = [NSOpenPanel openPanel];
panel.canChooseDirectories = NO;
panel.canChooseFiles = YES;
@@ -1250,11 +1345,35 @@ static NSString *TCMSFormatBytes(id value) {
[self setStatus:error.localizedDescription];
return;
}
NSDictionary *item = [json[@"item"] isKindOfClass:[NSDictionary class]] ? json[@"item"] : @{};
completion(item);
}];
}
- (void)uploadMedia {
[self chooseAndUploadMediaWithCompletion:^(NSDictionary *item) {
[self setStatus:@"Media uploaded."];
[self refreshMedia];
}];
}
- (void)uploadEditorMedia {
if (![self isContentSection] || !self.markdownView) {
[self setStatus:@"Open Posts or Pages to insert media."];
return;
}
[self chooseAndUploadMediaWithCompletion:^(NSDictionary *item) {
NSString *snippet = [self mediaMarkdownEmbedForItem:item];
if (snippet.length == 0) {
[self setStatus:@"Media uploaded, but no embed could be generated."];
return;
}
[self insertMarkdownSnippet:snippet];
[self setStatus:@"Media uploaded and inserted."];
}];
}
- (NSURL *)absoluteURLForMediaItem:(NSDictionary *)item {
NSString *url = TCMSString(item[@"url"]);
if (url.length == 0) {
@@ -1263,6 +1382,165 @@ static NSString *TCMSFormatBytes(id value) {
return [self.client absoluteURLForPath:url];
}
- (NSString *)mediaEmbedURLForItem:(NSDictionary *)item {
NSString *url = TCMSString(item[@"url"]);
if (url.length == 0) {
url = TCMSString(item[@"path"]);
}
return url;
}
- (NSString *)mediaTitleForItem:(NSDictionary *)item {
NSString *name = TCMSString(item[@"name"]);
if (name.length == 0) {
name = TCMSString(item[@"path"]).lastPathComponent;
}
NSString *base = name.stringByDeletingPathExtension;
return base.length > 0 ? base : name;
}
- (BOOL)mediaItem:(NSDictionary *)item hasMimePrefix:(NSString *)prefix extensions:(NSArray<NSString *> *)extensions {
NSString *mime = [TCMSString(item[@"mime"]) lowercaseString];
if ([mime hasPrefix:prefix]) {
return YES;
}
NSString *extension = [[TCMSString(item[@"name"]).pathExtension lowercaseString] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
if (extension.length == 0) {
extension = [[TCMSString(item[@"path"]).pathExtension lowercaseString] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
}
return [extensions containsObject:extension];
}
- (BOOL)isImageMediaItem:(NSDictionary *)item {
return [self mediaItem:item hasMimePrefix:@"image/" extensions:@[@"avif", @"bmp", @"gif", @"jpeg", @"jpg", @"png", @"svg", @"webp"]];
}
- (BOOL)isVideoMediaItem:(NSDictionary *)item {
return [self mediaItem:item hasMimePrefix:@"video/" extensions:@[@"m4v", @"mov", @"mp4", @"ogv", @"ogg", @"webm"]];
}
- (BOOL)isAudioMediaItem:(NSDictionary *)item {
return [self mediaItem:item hasMimePrefix:@"audio/" extensions:@[@"aac", @"aif", @"aiff", @"flac", @"m4a", @"mp3", @"oga", @"ogg", @"wav", @"weba"]];
}
- (NSString *)markdownSafeLabel:(NSString *)value {
NSString *label = [TCMSString(value) stringByReplacingOccurrencesOfString:@"\n" withString:@" "];
label = [label stringByReplacingOccurrencesOfString:@"[" withString:@"("];
label = [label stringByReplacingOccurrencesOfString:@"]" withString:@")"];
return label.length > 0 ? label : @"media";
}
- (NSString *)shortcodeSafeValue:(NSString *)value {
NSString *clean = [TCMSString(value) stringByReplacingOccurrencesOfString:@"\"" withString:@"%22"];
clean = [clean stringByReplacingOccurrencesOfString:@"\n" withString:@""];
return clean;
}
- (NSString *)mediaMarkdownEmbedForItem:(NSDictionary *)item {
NSString *url = [self mediaEmbedURLForItem:item];
if (url.length == 0) {
return @"";
}
NSString *title = [self markdownSafeLabel:[self mediaTitleForItem:item]];
if ([self isImageMediaItem:item]) {
return [NSString stringWithFormat:@"![%@](%@)", title, url];
}
if ([self isVideoMediaItem:item]) {
return [NSString stringWithFormat:@"[video file=\"%@\"]", [self shortcodeSafeValue:url]];
}
if ([self isAudioMediaItem:item]) {
return [NSString stringWithFormat:@"<audio controls src=\"%@\"></audio>", [self shortcodeSafeValue:url]];
}
return [NSString stringWithFormat:@"[%@](%@)", title, url];
}
- (void)insertMarkdownSnippet:(NSString *)snippet {
if (!self.markdownView || snippet.length == 0) {
return;
}
NSString *current = self.markdownView.string ?: @"";
NSRange range = self.markdownView.selectedRange;
if (range.location == NSNotFound || range.location > current.length) {
range = NSMakeRange(current.length, 0);
} else if (NSMaxRange(range) > current.length) {
range.length = current.length - range.location;
}
NSString *prefix = @"";
NSString *suffix = @"";
if (range.location > 0) {
NSString *before = [current substringToIndex:range.location];
if (![before hasSuffix:@"\n\n"]) {
prefix = [before hasSuffix:@"\n"] ? @"\n" : @"\n\n";
}
}
NSUInteger afterLocation = MIN(NSMaxRange(range), current.length);
if (afterLocation < current.length) {
NSString *after = [current substringFromIndex:afterLocation];
if (![after hasPrefix:@"\n\n"]) {
suffix = [after hasPrefix:@"\n"] ? @"\n" : @"\n\n";
}
} else {
suffix = @"\n";
}
NSString *insert = [NSString stringWithFormat:@"%@%@%@", prefix, snippet, suffix];
if ([self.markdownView shouldChangeTextInRange:range replacementString:insert]) {
[self.markdownView.textStorage replaceCharactersInRange:range withString:insert];
[self.markdownView didChangeText];
[self.markdownView setSelectedRange:NSMakeRange(range.location + prefix.length + snippet.length, 0)];
}
[self.window makeFirstResponder:self.markdownView];
[self markDirty];
}
- (void)configureMediaContextMenu {
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Media"];
NSMenuItem *copyURL = [[NSMenuItem alloc] initWithTitle:@"Copy URL" action:@selector(copySelectedMediaURL:) keyEquivalent:@""];
copyURL.target = self;
[menu addItem:copyURL];
NSMenuItem *copyEmbed = [[NSMenuItem alloc] initWithTitle:@"Copy Markdown Embed" action:@selector(copySelectedMediaEmbed:) keyEquivalent:@""];
copyEmbed.target = self;
[menu addItem:copyEmbed];
[menu addItem:[NSMenuItem separatorItem]];
NSMenuItem *open = [[NSMenuItem alloc] initWithTitle:@"Open" action:@selector(openSelectedMedia:) keyEquivalent:@""];
open.target = self;
[menu addItem:open];
self.tableView.menu = menu;
}
- (void)copyTextToPasteboard:(NSString *)text status:(NSString *)status {
if (text.length == 0) {
[self setStatus:@"Nothing to copy."];
return;
}
NSPasteboard *pasteboard = NSPasteboard.generalPasteboard;
[pasteboard clearContents];
[pasteboard setString:text forType:NSPasteboardTypeString];
[self setStatus:status];
}
- (void)copySelectedMediaURL:(id)sender {
NSDictionary *item = [self selectedTableItem];
if (!item) {
[self setStatus:@"Select a media item first."];
return;
}
NSURL *url = [self absoluteURLForMediaItem:item];
[self copyTextToPasteboard:url.absoluteString ?: [self mediaEmbedURLForItem:item] status:@"Media URL copied."];
}
- (void)copySelectedMediaEmbed:(id)sender {
NSDictionary *item = [self selectedTableItem];
if (!item) {
[self setStatus:@"Select a media item first."];
return;
}
[self copyTextToPasteboard:[self mediaMarkdownEmbedForItem:item] status:@"Markdown embed copied."];
}
- (NSString *)mediaDetailForItem:(NSDictionary *)item {
NSMutableArray *parts = [NSMutableArray array];
NSString *mime = TCMSString(item[@"mime"]);
@@ -1288,6 +1566,8 @@ static NSString *TCMSFormatBytes(id value) {
self.mediaImageView.hidden = YES;
self.mediaPreviewURLString = nil;
self.openMediaButton.enabled = NO;
self.mediaURLCopyButton.enabled = NO;
self.mediaEmbedCopyButton.enabled = NO;
self.mediaPreviewLabel.stringValue = @"No media selected";
self.mediaDetailLabel.stringValue = @"";
}
@@ -1304,6 +1584,8 @@ static NSString *TCMSFormatBytes(id value) {
NSString *urlString = url.absoluteString ?: @"";
self.mediaPreviewURLString = urlString;
self.openMediaButton.enabled = YES;
self.mediaURLCopyButton.enabled = YES;
self.mediaEmbedCopyButton.enabled = YES;
self.mediaPreviewLabel.stringValue = TCMSString(item[@"name"]).length ? TCMSString(item[@"name"]) : url.lastPathComponent;
self.mediaDetailLabel.stringValue = [self mediaDetailForItem:item];
self.mediaImageView.image = nil;
@@ -1455,7 +1737,7 @@ static NSString *TCMSFormatBytes(id value) {
return;
}
NSURL *base = [NSFileManager.defaultManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask].firstObject;
NSURL *folder = [base URLByAppendingPathComponent:@"TCMS Mac Client/Drafts" isDirectory:YES];
NSURL *folder = [base URLByAppendingPathComponent:@"TCMS/Drafts" isDirectory:YES];
[NSFileManager.defaultManager createDirectoryAtURL:folder withIntermediateDirectories:YES attributes:nil error:nil];
NSString *name = TCMSString(item[@"slug"]).length ? TCMSString(item[@"slug"]) : TCMSString(item[@"title"]);
name = [name stringByReplacingOccurrencesOfString:@"[^A-Za-z0-9._-]+" withString:@"-" options:NSRegularExpressionSearch range:NSMakeRange(0, name.length)];