Skip to content

Commit 9fa1d18

Browse files
committed
Support topic channels.
1 parent fe43aa7 commit 9fa1d18

File tree

11 files changed

+427
-3
lines changed

11 files changed

+427
-3
lines changed

Database/lib/OpenNewsWire/DB/Result/Message.pm

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,51 @@ __PACKAGE__->has_many(
203203
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-09-08 14:30:18
204204
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UqCL3m0DEMgcjDA73SZk2w
205205

206+
sub time_ago {
207+
my ( $self ) = @_;
206208

209+
my $delta = time - $self->created_at->epoch;
207210

211+
return "less than a minute ago" if $delta < 60; # 1 Minute
212+
return "about a minute ago" if $delta < 120; # 2 minute
213+
return int($delta / 60) . " minutes ago" if $delta < 45 * 60; # 45 minutes
214+
return "about an hour ago" if $delta < 60 * 60 * 2; # 2 hours
215+
return int($delta / 3600) . " hours ago" if $delta < 60 * 60 * 18; # 18 hours
216+
return "about an day ago" if $delta < 60 * 60 * 36; # 36 Hours
217+
return int($delta / (3600*24)) . " days ago";
218+
}
219+
220+
sub comment_count {
221+
my ( $self ) = @_;
222+
223+
return $self->_count_children( $self->id );
224+
225+
}
226+
227+
sub _count_children {
228+
my ( $self, $id ) = @_;
229+
230+
my $count = 0;
231+
232+
my $rs = $self->result_source->schema->resultset('Message')->search( { parent_id => $id } );
233+
while ( my $result = $rs->next ) {
234+
$count += 1;
235+
$count += $self->_count_children( $result->id );
236+
}
237+
238+
return $count;
239+
}
240+
241+
sub slug {
242+
my ( $self ) = @_;
243+
244+
my $slug = $self->title || 'permlink';
245+
246+
$slug =~ s/[^a-zA-Z0-9`]+/-/g;
247+
$slug =~ s/-$//;
248+
$slug =~ s/^-//;
249+
250+
return $slug;
251+
}
208252

209-
# You can replace this text with custom code or comments, and it will be preserved on regeneration
210253
1;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package OpenNewsWire::Web::Controller::Create;
2+
use Moose;
3+
use namespace::autoclean;
4+
5+
BEGIN { extends 'Catalyst::Controller' }
6+
7+
sub base :Chained('/base') PathPart('create') CaptureArgs(0) {
8+
my ( $self, $c ) = @_;
9+
10+
# Is there a valid user logged in? - If not, send them to the login page.
11+
if ( ! $c->stash->{user} ) {
12+
$c->res->redirect( $c->uri_for_action('/get_login') );
13+
$c->detach;
14+
}
15+
}
16+
17+
sub show_create_topic :Chained('base') PathPart('topic') Args(0) Method('GET') {
18+
my ( $self, $c ) = @_;
19+
20+
$c->stash->{template} = 'create/topic.tx';
21+
}
22+
23+
sub create_topic :Chained('base') PathPart('topic') Args(0) Method('POST') {
24+
my ( $self, $c ) = @_;
25+
26+
my $name = $c->req->params->{topic_name};
27+
28+
# TODO: Validate name
29+
# * Make sure it follows the rules (what are the rules?)
30+
# * Doesn't already exist)
31+
32+
$c->stash->{user}->create_related( 'topic_channels', {
33+
name => $name,
34+
});
35+
36+
$c->res->redirect( $c->uri_for_action( '/topicchannel/get_topic', [], $name ) );
37+
}
38+
39+
sub show_create_message :Chained('base') PathPart('message') Args(0) Method('GET') {
40+
my ( $self, $c ) = @_;
41+
42+
if ( $c->req->params->{topic} ) {
43+
$c->stash->{form_to} = 't/' . $c->req->params->{topic};
44+
}
45+
$c->stash->{template} = 'create/message.tx';
46+
}
47+
48+
sub create_message :Chained('base') PathPart('message') Args(0) Method('POST') {
49+
my ( $self, $c ) = @_;
50+
51+
$c->stash->{template} = 'create/message.tx';
52+
53+
my $target = $c->req->params->{to};
54+
my $title = $c->req->params->{title};
55+
my $url = $c->req->params->{url};
56+
my $content = $c->req->params->{message};
57+
my $parent = $c->req->params->{reply_to};
58+
my $msg_id = $c->req->params->{message_id};
59+
my $msg_slug = $c->req->params->{message_slug};
60+
my $chan_name = $c->req->params->{channel_name};
61+
62+
$c->stash(
63+
form_to => $target,
64+
form_title => $title,
65+
form_message => $content,
66+
);
67+
68+
$c->model('DB')->schema->txn_do( sub {
69+
my $message = $c->stash->{user}->create_related( 'messages', {
70+
( $url ? ( url => $url ) : ( ) ),
71+
( $title ? ( title => $title ) : ( ) ),
72+
( $content ? ( content => $content ) : ( ) ),
73+
( $parent ? ( parent_id => $parent ) : ( ) ),
74+
});
75+
76+
my ( $mode, $name ) = split( /\//, $target, 2 );
77+
78+
if ( $mode eq 't' ) {
79+
my $channel_id = $c->model('DB')->resultset('TopicChannel')->search({ name => $name })->first->id;
80+
$c->model('DB')->resultset('TopicChannelMessage')->create({
81+
author_id => $c->stash->{user}->id,
82+
message_id => $message->id,
83+
channel_id => $channel_id,
84+
});
85+
# Set the variables so that the user is redirected to the new message post..
86+
( $msg_id, $msg_slug, $chan_name ) = ( $message->id, $title, $name );
87+
}
88+
});
89+
90+
if ( $msg_id && $msg_slug && $chan_name ) {
91+
$c->res->redirect( $c->uri_for_action( '/topicchannel/get_topic_message', [], $chan_name, $msg_id, $msg_slug ) );
92+
}
93+
}
94+
95+
__PACKAGE__->meta->make_immutable;
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package OpenNewsWire::Web::Controller::TopicChannel;
2+
use Moose;
3+
use namespace::autoclean;
4+
5+
BEGIN { extends 'Catalyst::Controller' }
6+
7+
sub base :Chained('/base') PathPart('t') CaptureArgs(0) {
8+
my ( $self, $c ) = @_;
9+
10+
# Is there a valid user logged in? - If not, send them to the login page.
11+
if ( ! $c->stash->{user} ) {
12+
$c->res->redirect( $c->uri_for_action('/get_login') );
13+
$c->detach;
14+
}
15+
}
16+
17+
sub index :Chained('base') PathPart('') Args(0) Method('GET') {
18+
my ( $self, $c ) = @_;
19+
$c->stash->{template} = 'topic/index.tx';
20+
21+
push @{$c->stash->{topics}},
22+
$c->model('DB')->resultset('TopicChannel')->all;
23+
}
24+
25+
sub get_topic :Chained('base') PathPart('') Args(1) Method('GET') {
26+
my ( $self, $c, $topic_name ) = @_;
27+
$c->stash->{template} = 'topic/show_topic.tx';
28+
29+
my $topic = $c->model('DB')->resultset('TopicChannel')->search({ name => $topic_name })->first;
30+
31+
$c->stash->{topic} = $topic;
32+
33+
push @{$c->stash->{messages}},
34+
$c->model('DB')->resultset('TopicChannelMessage')->search({channel_id => $topic->id})->all;
35+
}
36+
37+
sub get_topic_message :Chained('base') PathPart('') Args(3) Method('GET') {
38+
my ( $self, $c, $topic_name, $message_id, $message_slug ) = @_;
39+
$c->stash->{template} = 'topic/show_topic_message.tx';
40+
41+
my $channel = $c->model('DB')->resultset('TopicChannel')->search({ name => $topic_name })->first;
42+
my $message = $c->model('DB')->resultset('Message')->find( $message_id );
43+
44+
$c->stash->{message} = $message;
45+
$c->stash->{channel} = $channel;
46+
$c->stash->{topic} = $channel;
47+
$c->stash->{comments} = $self->_get_message_children( $c, $message->id );
48+
$c->stash->{comment_count} = $self->_count_message_children( $c->stash->{comments} );
49+
}
50+
51+
sub _count_message_children {
52+
my ( $self, $comments ) = @_;
53+
54+
my $count = 0;
55+
56+
foreach my $comment ( @$comments ) {
57+
$count += 1;
58+
$count += $self->_count_message_children($comment->{children});
59+
}
60+
61+
return $count;
62+
}
63+
64+
sub _get_message_children {
65+
my ( $self, $c, $parent_id ) = @_;
66+
67+
my @results;
68+
69+
my $search_rs = $c->model('DB')->resultset('Message')->search({ parent_id => $parent_id } );
70+
while ( my $message = $search_rs->next ) {
71+
push @results, {
72+
message => {
73+
content => $message->content,
74+
id => $message->id,
75+
created => $message->created_at->strftime( '%F %T' ),
76+
time_ago => $message->time_ago,
77+
},
78+
author => {
79+
name => $message->author->name,
80+
id => $message->author->id,
81+
},
82+
children => $self->_get_message_children( $c, $message->id ),
83+
};
84+
}
85+
86+
return [ @results ];
87+
}
88+
89+
__PACKAGE__->meta->make_immutable;

Web/root/_sidebar.tx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
Create New Topic
1010
</a>
1111
</li>
12-
%% if ( $channel ) {
12+
%% if ( $topic ) {
1313
<li class="nav-item">
1414
<a class="nav-link [% $sb_active == "dashboard" ? "active" : "" %]" aria-current="page"
15-
href="[% $c.uri_for_action( '/create/create_message', [], { channel => $channel.name } ) %]">
15+
href="[% $c.uri_for_action( '/create/create_message', [], { topic => $topic.name } ) %]">
1616
<span data-feather="home"></span>
1717
Create New Post
1818
</a>

Web/root/create/message.tx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
%% cascade '_base.tx'
2+
3+
%% around content -> {
4+
<div class="row my-4">
5+
<form method="POST" action="[% $c.uri_for( '/create/message') %]">
6+
%% include '_forms/input.tx' { type => 'text', name => 'to',
7+
%% title => 'To',
8+
%% help => 'The user or channel to send the message to.',
9+
%% value => $form_to
10+
%% };
11+
12+
%% include '_forms/input.tx' { type => 'text', name => 'title',
13+
%% title => 'Title',
14+
%% help => 'Message title',
15+
%% value => $form_title
16+
%% };
17+
18+
%% include '_forms/input.tx' { type => 'text', name => 'url',
19+
%% title => 'URL',
20+
%% help => 'URL For Link',
21+
%% value => $form_url
22+
%% };
23+
24+
<div class="mb-3">
25+
<label for="message" class="form-label">Message</label>
26+
<textarea class="form-control" name="message" id="message" rows="3">[% $form_message %]</textarea>
27+
</div>
28+
29+
<button type="submit" class="btn btn-primary float-end">Post Message</button>
30+
</form>
31+
</div>
32+
<hr />
33+
%% }
34+

Web/root/create/topic.tx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
%% cascade '_base.tx'
2+
3+
%% around content -> {
4+
<div class="row my-4">
5+
<div class="col">
6+
%% if ( $errors ) {
7+
<h5>There were errors with your request:</h5>
8+
<ul>
9+
%% for $errors -> $error {
10+
<li class="text-danger">[% $error %]</li>
11+
%% }
12+
</ul>
13+
%% }
14+
</div>
15+
<div class="col">
16+
<form method="POST" action="[% $c.uri_for_action( '/create/create_topic' ) %]">
17+
<div class="input-group mb-3">
18+
<span class="input-group-text" id="basic-addon1">t/</span>
19+
<input type="text" name="topic_name" value="[% $form_topic_name %]" class="form-control" placeholder="topicName" aria-label="Topic name" aria-describedby="basic-addon1">
20+
</div>
21+
22+
<button type="submit" class="btn btn-primary float-end">Make Topic!</button>
23+
</form>
24+
</div>
25+
</div>
26+
%% }
27+

Web/root/topic/.show_topic.tx.swp

12 KB
Binary file not shown.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<div class="card my-4">
2+
<div class="card-body">
3+
<div class="">
4+
<a href="[% $c.uri_for_action( '/userchannel/get_user', [], $comment.message.author.name ) %]">u/[% $comment.author.name %]</a>
5+
<small><span title="[% $comment.message.created %]">[% $comment.message.time_ago %]</span></small>
6+
</div>
7+
<div class="pt-4">
8+
%% $comment.message.content
9+
</div>
10+
<br />
11+
<a class="card-link" data-bs-toggle="collapse" href="#replyForm[% $comment.message.id %]" role="button" aria-expanded="false" aria-controls="replyForm[% $comment.message.id %]">[Reply]</a>
12+
<a href="[% $c.uri_for_action( '/topicchannel/get_topic_message', [], $channel.name, $comment.message.id, 'permlink' ) %]" class="card-link">[Permlink]</a>
13+
<div id="replyForm[% $comment.message.id %]" class="row mx-2 my-4 collapse">
14+
<form method="POST" action="/create/message">
15+
<input type="hidden" name="reply_to" value="[% $comment.message.id %]" />
16+
<input type="hidden" name="message_id" value="[% $message.id %]" />
17+
<input type="hidden" name="message_slug" value="[% $message.title %]" />
18+
<input type="hidden" name="channel_name" value="[% $channel.name %]" />
19+
<div class="mb-3">
20+
<label for="message" class="form-label">Post a Reply</label>
21+
<textarea class="form-control" name="message" id="message" rows="3"></textarea>
22+
</div>
23+
24+
<button type="submit" class="btn btn-primary float-end">Post Message</button>
25+
</form>
26+
</div>
27+
</div>
28+
%% for $comment.children -> $child_comment {
29+
<div style="margin-left: 2em">
30+
%% include "topic/_show_topic_message_comment.tx" { comment => $child_comment };
31+
</div>
32+
%% }
33+
</div>

Web/root/topic/index.tx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
%% cascade '_base.tx'
2+
3+
%% around content -> {
4+
<nav aria-label="breadcrumb">
5+
<ol class="breadcrumb">
6+
<li class="breadcrumb-item active aria-current="page""><a href="[% $c.uri_for_action( '/topicchannel/index' ) %]">Topics</a></li>
7+
</ol>
8+
</nav>
9+
%% for $topics -> $topic {
10+
<div class="card my-4">
11+
<div class="card-body">
12+
<h5 class="card-title"><a href="[% $c.uri_for_action( '/topicchannel/get_topic', [], $topic.name ) %]">t/[% $topic.name %]</a></h5>
13+
<small class="text-mute">[% $message.message.url %]</small>
14+
</div>
15+
</div>
16+
%% }
17+
18+
%% }
19+

Web/root/topic/show_topic.tx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
%% cascade '_base.tx'
2+
3+
%% around content -> {
4+
<nav aria-label="breadcrumb">
5+
<ol class="breadcrumb">
6+
<li class="breadcrumb-item"><a href="[% $c.uri_for_action( '/topicchannel/index' ) %]">Topics</a></li>
7+
<li class="breadcrumb-item active" aria-current="page"><a href="[% $c.uri_for_action( '/topicchannel/get_topic', [], $topic.name ) %]">t/[% $topic.name %]</a></li>
8+
</ol>
9+
</nav>
10+
11+
%% for $messages -> $message {
12+
<div class="card my-4">
13+
<div class="card-body">
14+
%% if ( $message.message.url ) {
15+
<h5 class="card-title"><a href="[% $message.message.url %]">[% $message.message.title %]</a></h5>
16+
<small class="text-mute">[% $message.message.url %]</small>
17+
%% } else {
18+
<h5 class="card-title"><a href="[% $c.uri_for_action( '/topicchannel/get_topic_message', [ ], $topic.name, $message.message.id, $message.message.slug ) %]">[% $message.message.title %]</a></h5>
19+
<small class="text-mute">(text post)</small>
20+
%% }
21+
<br />
22+
<a href="#" class="card-link">Comments ([% $message.message.comment_count %])</a>
23+
<a class="card-link" href="[% $c.uri_for_action( '/userchannel/get_user', [], $message.author.name ) %]">u/[% $message.author.name %]</a>
24+
<small><span title="[% $message.message.created_at.strftime('%Y %T') %]">[% $message.message.time_ago %]</span></small>
25+
</div>
26+
</div>
27+
%% }
28+
29+
%% }

0 commit comments

Comments
 (0)