Skip to content

Commit a9feb85

Browse files
committed
Added ability to hide feed from homepage
Closes #22
1 parent 9bf5d33 commit a9feb85

File tree

9 files changed

+75
-19
lines changed

9 files changed

+75
-19
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The following features are built into the application:
1313
- Every hour by default, configurable down to 5 mins.
1414
- Custom feed names and colors.
1515
- Feed-based tags for categorization.
16+
- Ability to hide feed posts by default.
1617
- 3 different post layout modes (card, list, compact).
1718
- Fetching of page open-graph images.
1819
- Feeds managed via a single plaintext file.
@@ -131,6 +132,11 @@ https://example.com/feed-b.xml News_Site #news
131132
# Feed color can be set using square brackets after the name.
132133
# The color must be a CSS-compatible color value.
133134
https://example.com/feed-c.xml Blue_News[#0078b9] #news #blue
135+
136+
# Feeds starting with a '-' are flagged as hidden.
137+
# Posts for hidden feeds won't be shown on the homepage
138+
# but can be seen via any type of active filter.
139+
- https://example.com/feed-d.xml Cat_Facts #cats #facts
134140
```
135141

136142
## App Configuration

app/Config/ConfiguredFeed.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public function __construct(
1616
public string $url,
1717
public string $color,
1818
public array $tags,
19+
public bool $hidden,
1920
) {
2021
}
2122

@@ -26,6 +27,7 @@ public function jsonSerialize(): mixed
2627
'color' => $this->color,
2728
'url' => $this->url,
2829
'tags' => $this->tags,
30+
'hidden' => $this->hidden,
2931
'reloading' => $this->reloading,
3032
'outdated' => $this->isOutdated(),
3133
];

app/Config/ConfiguredFeedList.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Config;
44

5+
use ArrayIterator;
56
use IteratorAggregate;
67
use JsonSerializable;
78
use Traversable;
@@ -50,7 +51,7 @@ public function reloadOutdatedFeeds(): int
5051

5152
public function getIterator(): Traversable
5253
{
53-
return $this->feeds;
54+
return new ArrayIterator($this->feeds);
5455
}
5556

5657
public function jsonSerialize(): mixed

app/Config/ConfiguredFeedProvider.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class ConfiguredFeedProvider
99
protected RssConfig $config;
1010

1111
/** @var ConfiguredFeed[] */
12-
protected $feeds = [];
12+
protected array $feeds = [];
1313

1414
public function loadFromConfig(): void
1515
{
@@ -54,7 +54,8 @@ protected function getConfiguredFeeds(): array
5454
$this->config->getName($feedUrl),
5555
$feedUrl,
5656
$this->config->getColor($feedUrl),
57-
$this->config->getTags($feedUrl)
57+
$this->config->getTags($feedUrl),
58+
$this->config->getHidden($feedUrl),
5859
);
5960

6061
$configuredFeeds[] = $configured;
@@ -78,6 +79,16 @@ public function getAll()
7879
return new ConfiguredFeedList($this->feeds);
7980
}
8081

82+
public function getVisible()
83+
{
84+
$feeds = array_filter($this->feeds, function (ConfiguredFeed $feed) {
85+
return !$feed->hidden;
86+
});
87+
88+
$this->updateLastAccessedForFeeds($feeds);
89+
return new ConfiguredFeedList($feeds);
90+
}
91+
8192
public function get(string $feedUrl): ?ConfiguredFeed
8293
{
8394
foreach ($this->feeds as $feed) {

app/Config/RssConfig.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ class RssConfig
88
* The configured feeds.
99
* Array keys are the feed URLs and values are arrays of tags as strings.
1010
* Tag strings include their '#' prefix.
11-
* @var array<string, array{name: string, tags: string[], color: string}>
11+
* @var array<string, array{name: string, tags: string[], color: string, hidden: bool}>
1212
*/
13-
protected $feeds = [];
13+
protected array $feeds = [];
1414

1515
/**
1616
* Get all feed URLs
@@ -24,12 +24,13 @@ public function getFeedUrls(): array
2424
/**
2525
* Add a new feed to the config.
2626
*/
27-
public function addFeed(string $feed, string $name, array $tags = [], string $color = ''): void
27+
public function addFeed(string $feed, string $name, array $tags = [], string $color = '', bool $hidden = false): void
2828
{
2929
$this->feeds[$feed] = [
3030
'name' => $name,
3131
'tags' => $tags,
3232
'color' => $color,
33+
'hidden' => $hidden,
3334
];
3435
}
3536

@@ -73,6 +74,14 @@ public function getColor(string $feed): string
7374
return $this->feeds[$feed]['color'] ?? '';
7475
}
7576

77+
/**
78+
* Get the hidden status for the given feed.
79+
*/
80+
public function getHidden(string $feed): bool
81+
{
82+
return $this->feeds[$feed]['hidden'] ?? false;
83+
}
84+
7685
/**
7786
* Get the configuration as a string.
7887
*/
@@ -92,6 +101,10 @@ public function toString(): string
92101
$line .= " {$tag}";
93102
}
94103

104+
if ($details['hidden']) {
105+
$line = '-' . $line;
106+
}
107+
95108
$lines[] = $line;
96109
}
97110

@@ -107,8 +120,14 @@ public function parseFromString(string $configString): void
107120

108121
foreach ($lines as $line) {
109122
$line = trim($line);
110-
$parts = explode(' ', $line);
123+
$hidden = false;
111124

125+
if (str_starts_with($line, '-')) {
126+
$hidden = true;
127+
$line = ltrim($line, '- ');
128+
}
129+
130+
$parts = explode(' ', $line);
112131
if (empty($line) || str_starts_with($line, '#') || count($parts) < 2) {
113132
continue;
114133
}
@@ -127,7 +146,7 @@ public function parseFromString(string $configString): void
127146
$tags = array_filter(array_slice($parts, 2), fn ($str) => str_starts_with($str, '#'));
128147

129148
if (str_starts_with($url, 'http://') || str_starts_with($url, 'https://')) {
130-
$this->addFeed($url, $name, $tags, $color);
149+
$this->addFeed($url, $name, $tags, $color, $hidden);
131150
}
132151
}
133152
}

app/Http/Controllers/PostViewController.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,20 @@ public function __construct(
1919

2020
public function home(Request $request)
2121
{
22-
$feeds = $this->feedProvider->getAll();
23-
$feeds->reloadOutdatedFeeds();
22+
$displayFeeds = $this->feedProvider->getAll();
23+
$displayFeeds->reloadOutdatedFeeds();
24+
25+
$postFeeds = $this->feedProvider->getVisible();
2426

25-
return $this->renderPostsView($request, $feeds);
27+
return $this->renderPostsView($request, $displayFeeds, $postFeeds);
2628
}
2729

2830
public function tag(Request $request, string $tag)
2931
{
3032
$feeds = $this->feedProvider->getForTag('#' . $tag);
3133
$feeds->reloadOutdatedFeeds();
3234

33-
return $this->renderPostsView($request, $feeds, ['tag' => $tag]);
35+
return $this->renderPostsView($request, $feeds, $feeds, ['tag' => $tag]);
3436
}
3537

3638
public function feed(Request $request, string $feed)
@@ -40,10 +42,10 @@ public function feed(Request $request, string $feed)
4042
$feeds = $this->feedProvider->getAsList($feed);
4143
$feeds->reloadOutdatedFeeds();
4244

43-
return $this->renderPostsView($request, $feeds, ['feed' => $feed]);
45+
return $this->renderPostsView($request, $feeds, $feeds, ['feed' => $feed]);
4446
}
4547

46-
protected function renderPostsView(Request $request, ConfiguredFeedList $feeds, array $additionalData = [])
48+
protected function renderPostsView(Request $request, ConfiguredFeedList $displayFeeds, ConfiguredFeedList $postFeeds, array $additionalData = [])
4749
{
4850
$page = max(intval($request->get('page')), 1);
4951
$query = $request->get('query', '');
@@ -57,14 +59,14 @@ protected function renderPostsView(Request $request, ConfiguredFeedList $feeds,
5759
}
5860

5961
$posts = $this->postProvider->getLatest(
60-
$feeds,
62+
$postFeeds,
6163
100,
6264
$page,
6365
$subFilter
6466
);
6567

6668
$coreData = [
67-
'feeds' => $feeds,
69+
'feeds' => $displayFeeds,
6870
'posts' => $posts,
6971
'page' => $page,
7072
'search' => $query,

resources/js/Parts/Feed.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
<div class="py-1 my-2">
33
<h4 class="font-bold text-black dark:text-gray-400" :style="{color: feed.color}">
44
<Link :href="`f/${encodeURIComponent(encodeURIComponent(feed.url))}`">{{ feed.name }}</Link>
5+
<span v-if="feed.hidden" title="Posts hidden by default" class="text-xs ml-2 text-gray-600">
6+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="inline bi bi-eye-slash" viewBox="0 0 16 16">
7+
<path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z"/>
8+
<path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z"/>
9+
<path d="M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12-.708.708z"/>
10+
</svg>
11+
</span>
512
</h4>
613
<div class="font-mono text-gray-600 dark:text-gray-500 text-xs my-1 overflow-ellipsis whitespace-nowrap w-full overflow-hidden">{{ feed.url }}</div>
714
<div class="flex gap-1 text-gray-600 dark:text-gray-500 text-sm flex-wrap">

tests/Feature/ConfiguredFeedTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ public function test_is_outdated_can_be_controlled_by_config(): void
2121
'My great feed',
2222
'https://example.com',
2323
'#fff',
24-
['#a']
24+
['#a'],
25+
false,
2526
);
2627

2728
config()->set('app.feed_update_frequency', 60);
@@ -45,7 +46,8 @@ public function test_start_reloading_dispatched_refresh_job(): void
4546
'My great feed',
4647
'https://example.com',
4748
'#fff',
48-
['#a']
49+
['#a'],
50+
false,
4951
);
5052
Queue::fake();
5153

tests/Unit/RssConfigTest.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,23 @@ public function test_parse_from_string(): void
5252
# A comment
5353
https://example-C.com/cats?test=abc#okay
5454
55+
-https://example-hidden-a.com Hidden_A #news
56+
- https://example-hidden-b.com/ Hidden_B
57+
5558
http://beans.com/feed.xml#food d_is_cool #cooking
5659
");
5760

58-
$this->assertCount(3, $config->getFeedUrls());
61+
$this->assertCount(5, $config->getFeedUrls());
5962
$this->assertCount(0, $config->getTags('https://example-B.com/cats?test=abc#okay'));
6063
$this->assertEquals(['#dog', '#cat'], $config->getTags('https://example.com'));
6164
$this->assertEquals(['#cooking'], $config->getTags('http://beans.com/feed.xml#food'));
6265
$this->assertEquals('a', $config->getName('https://example-B.com/cats?test=abc#okay'));
6366
$this->assertEquals('b', $config->getName('https://example.com'));
6467
$this->assertEquals('#000', $config->getColor('https://example.com'));
6568
$this->assertEquals('d is cool', $config->getName('http://beans.com/feed.xml#food'));
69+
$this->assertTrue($config->getHidden('https://example-hidden-a.com'));
70+
$this->assertTrue($config->getHidden('https://example-hidden-b.com/'));
71+
$this->assertFalse($config->getHidden('http://beans.com/feed.xml#food'));
6672
}
6773

6874

0 commit comments

Comments
 (0)