feat: regex converter

This commit is contained in:
relikd
2025-06-24 15:36:10 +02:00
parent f577ec1ec2
commit 839eee7d39
22 changed files with 887 additions and 40 deletions

View File

@@ -13,6 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface ModalFeedEdit : ModalEditDialog <NSTextFieldDelegate>
- (void)didClickWarningButton:(NSButton*)sender;
- (void)openRegexConverter;
@end
@interface ModalGroupEdit : ModalEditDialog

View File

@@ -11,6 +11,9 @@
#import "NSView+Ext.h"
#import "NSDate+Ext.h"
#import "NSURL+Ext.h"
#import "RegexConverterController.h"
#import "RegexConverterModal.h"
#import "RegexConverter+Ext.h"
// ################################################################
// #
@@ -59,6 +62,8 @@
@property (strong) FeedDownload *memFeed;
@property (weak) FaviconDownload *memIcon;
@property (strong) RefreshStatisticsView *statisticsView;
@property (nonatomic, assign) BOOL openRegexAfterDownload;
@property (weak) id eventMonitor;
@end
@implementation ModalFeedEdit
@@ -71,6 +76,13 @@
self.view.refreshNum.intValue = 30;
[NSDate populateUnitsMenu:self.view.refreshUnit selected:TimeUnitMinutes];
[self populateTextFields:self.feedGroup];
// removed in windowShouldClose:
self.eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskFlagsChanged handler:^(NSEvent *event) {
BOOL optionKeyActive = ((event.modifierFlags & NSEventModifierFlagOption) != 0);
self.view.regexConverterButton.hidden = !self.feedGroup.feed.regex && !optionKeyActive;
return event;
}];
}
/// Pre-fill UI control field values with @c FeedGroup properties.
@@ -81,6 +93,7 @@
self.view.url.objectValue = fg.feed.meta.url;
self.previousURL = self.view.url.stringValue;
self.view.favicon.image = [fg.feed iconImage16];
self.view.regexConverterButton.hidden = !fg.feed.regex;
[NSDate setInterval:fg.feed.meta.refresh forPopup:self.view.refreshUnit andField:self.view.refreshNum animate:NO];
[self statsForCoreDataObject];
}
@@ -131,7 +144,9 @@
self.view.name.placeholderString = NSLocalizedString(@"Loading …", nil);
}
self.previousURL = self.view.url.stringValue;
self.memFeed = [[FeedDownload withURL:self.previousURL] startWithDelegate:self];
self.memFeed = [[[FeedDownload withURL:self.previousURL]
withRegex:self.feedGroup.feed.regex enforce:self.openRegexAfterDownload]
startWithDelegate:self];
}
/**
@@ -210,8 +225,43 @@
- (void)downloadComplete {
[self.view.spinnerURL stopAnimation:nil];
[self.modalSheet setDoneEnabled:YES];
if (self.openRegexAfterDownload) {
[self openRegexConverter];
}
}
#pragma mark - Regex Converter
- (void)openRegexConverter {
if (!self.openRegexAfterDownload) {
self.openRegexAfterDownload = YES;
[self downloadRSS];
return;
}
self.openRegexAfterDownload = NO;
// shrink FeedEdit modal size to effectively hide it behind new modal
NSRect previous = self.modalSheet.frame;
CGFloat minWidthDiff = previous.size.width - self.modalSheet.minSize.width;
[self.modalSheet setFrame:NSInsetRect(previous, minWidthDiff / 2.0, 0) display:NO];
Feed *feed = self.feedGroup.feed;
RegexConverterController *c = [RegexConverterController withData:self.memFeed.rawData andConverter:feed.regex];
[self.modalSheet.sheetParent beginCriticalSheet:[c getModalSheet] completionHandler:^(NSModalResponse returnCode) {
// reset previous size
[self.modalSheet setFrame:previous display:NO];
if (returnCode == NSModalResponseOK) {
[c applyChanges:feed];
self.view.regexConverterButton.hidden = !feed.regex;
[self downloadRSS];
}
}];
}
#pragma mark - Feed Statistics
/// Perform statistics on newly downloaded feed item
@@ -264,6 +314,7 @@
[[NSNotificationCenter defaultCenter] postNotificationName:NSControlTextDidEndEditingNotification object:self.view.url];
return NO;
}
[NSEvent removeMonitor:self.eventMonitor];
return YES;
}

View File

@@ -18,6 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
@property NSPopover *warningPopover;
@property (strong) IBOutlet NSTextField *warningText;
@property (strong) IBOutlet NSButton *warningReload;
@property (strong) IBOutlet NSButton *regexConverterButton;
- (instancetype)initWithController:(ModalFeedEdit*)controller NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithFrame:(NSRect)frameRect NS_UNAVAILABLE;

View File

@@ -1,6 +1,7 @@
#import "ModalFeedEditView.h"
#import "ModalFeedEdit.h"
#import "NSView+Ext.h"
#import "Constants.h"
@interface StrictUIntFormatter : NSFormatter
@end
@@ -25,7 +26,8 @@
self.url = [[[NSView inputField:@"https://example.org/feed.rss" width:0] placeIn:self x:x yTop:0] sizeToRight:PAD_S + 18];
self.spinnerURL = [[NSView activitySpinner] placeIn:self xRight:1 yTop:2.5];
self.favicon = [[[NSView imageView:nil size:18] tooltip:NSLocalizedString(@"Favicon", nil)] placeIn:self xRight:0 yTop:1.5];
self.warningButton = [[[[NSView buttonIcon:NSImageNameCaution size:18] action:@selector(didClickWarningButton:) target:nil] // up the responder chain
self.warningButton = [[[[NSView buttonIcon:NSImageNameCaution size:18]
action:@selector(didClickWarningButton:) target:nil] // up the responder chain
tooltip:NSLocalizedString(@"Click here to show failure reason", nil)]
placeIn:self xRight:0 yTop:1.5];
// 2. row
@@ -34,6 +36,10 @@
// 3. row
self.refreshNum = [[NSView inputField:@"30" width:85] placeIn:self x:x yTop:2*rowHeight];
self.refreshUnit = [[NSView popupButton:120] placeIn:self x:NSMaxX(self.refreshNum.frame) + PAD_M yTop:2*rowHeight];
self.regexConverterButton = [[[[NSView buttonIcon:RSSImageRegexIcon size:19]
action:@selector(openRegexConverter) target:controller]
tooltip:NSLocalizedString(@"Regex converter", nil)]
placeIn:self xRight:0 yTop:2*rowHeight + 1];
// initial state
self.url.accessibilityLabel = lbls[0];
@@ -41,6 +47,7 @@
self.refreshNum.accessibilityLabel = NSLocalizedString(@"Refresh interval", nil);
self.url.delegate = controller;
self.warningButton.hidden = YES;
self.regexConverterButton.hidden = YES;
self.refreshNum.formatter = [StrictUIntFormatter new]; // see below ...
[self prepareWarningPopover];
return self;