From 00eb6af1bd21df42fc41195ceed0fad73bbb5f27 Mon Sep 17 00:00:00 2001 From: Derf Null Date: Sun, 4 Jun 2023 18:21:36 +0200 Subject: [PATCH] expose follows / social interaction in frontend --- lib/Travelynx.pm | 7 + lib/Travelynx/Command/database.pm | 21 +++ lib/Travelynx/Controller/Account.pm | 254 ++++++++++++++++++++++++-- lib/Travelynx/Controller/Profile.pm | 100 +++++++++- lib/Travelynx/Controller/Traveling.pm | 1 + lib/Travelynx/Model/Users.pm | 106 ++++++++++- templates/_checked_in.html.ep | 9 +- templates/_format_train.html.ep | 9 + templates/_public_status_card.html.ep | 39 ++-- templates/account.html.ep | 95 +++++++++- templates/edit_visibility.html.ep | 15 +- templates/layouts/default.html.ep | 12 +- templates/privacy.html.ep | 22 +-- templates/profile.html.ep | 73 +++++++- templates/social.html.ep | 66 +++++++ templates/social_list.html.ep | 230 +++++++++++++++++++++++ 16 files changed, 984 insertions(+), 75 deletions(-) create mode 100644 templates/_format_train.html.ep create mode 100644 templates/social.html.ep create mode 100644 templates/social_list.html.ep diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index adb6132..6dd77fd 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -399,6 +399,9 @@ sub startup { if ( $visibility eq 'travelynx' ) { return 'lock_open'; } + if ( $visibility eq 'followers' ) { + return 'group'; + } if ( $visibility eq 'unlisted' ) { return 'lock_outline'; } @@ -2213,6 +2216,8 @@ sub startup { $authed_r->get('/account')->to('account#account'); $authed_r->get('/account/privacy')->to('account#privacy'); + $authed_r->get('/account/social')->to('account#social'); + $authed_r->get('/account/social/:kind')->to('account#social_list'); $authed_r->get('/account/profile')->to('account#profile'); $authed_r->get('/account/hooks')->to('account#webhook'); $authed_r->get('/account/traewelling')->to('traewelling#settings'); @@ -2240,6 +2245,7 @@ sub startup { $authed_r->get('/s/*station')->to('traveling#station'); $authed_r->get('/confirm_mail/:token')->to('account#confirm_mail'); $authed_r->post('/account/privacy')->to('account#privacy'); + $authed_r->post('/account/social')->to('account#social'); $authed_r->post('/account/profile')->to('account#profile'); $authed_r->post('/account/hooks')->to('account#webhook'); $authed_r->post('/account/traewelling')->to('traewelling#settings'); @@ -2254,6 +2260,7 @@ sub startup { $authed_r->post('/account/password')->to('account#change_password'); $authed_r->post('/account/mail')->to('account#change_mail'); $authed_r->post('/account/name')->to('account#change_name'); + $authed_r->post('/social-action')->to('account#social_action'); $authed_r->post('/delete')->to('account#delete'); $authed_r->post('/logout')->to('account#do_logout'); $authed_r->post('/set_token')->to('api#set_token'); diff --git a/lib/Travelynx/Command/database.pm b/lib/Travelynx/Command/database.pm index 53168bf..3878502 100644 --- a/lib/Travelynx/Command/database.pm +++ b/lib/Travelynx/Command/database.pm @@ -1545,6 +1545,27 @@ my @migrations = ( } ); }, + + # v37 -> v38 + sub { + my ($db) = @_; + $db->query( + qq{ + drop view followers; + create view followers as select + relations.object_id as self_id, + users.id as id, + users.name as name, + users.accept_follows as accept_follows, + r2.predicate as inverse_predicate + from relations + join users on relations.subject_id = users.id + left join relations as r2 on relations.subject_id = r2.object_id + where relations.predicate = 1; + update schema_version set version = 38; + } + ); + }, ); # TODO add 'hafas' column to in_transit (and maybe journeys? undo/redo needs something to work with...) diff --git a/lib/Travelynx/Controller/Account.pm b/lib/Travelynx/Controller/Account.pm index af97c96..fe5d5cc 100644 --- a/lib/Travelynx/Controller/Account.pm +++ b/lib/Travelynx/Controller/Account.pm @@ -408,11 +408,8 @@ sub delete { my ($self) = @_; my $uid = $self->current_user->{id}; if ( $self->validation->csrf_protect->has_error('csrf_token') ) { - $self->render( - 'account', - api_token => $self->users->get_api_token( uid => $uid ), - invalid => 'csrf', - ); + $self->flash( invalid => 'csrf' ); + $self->redirect_to('account'); return; } @@ -424,11 +421,8 @@ sub delete { ) ) { - $self->render( - 'account', - api_token => $self->users->get_api_token( uid => $uid ), - invalid => 'deletion password' - ); + $self->flash( invalid => 'deletion password' ); + $self->redirect_to('account'); return; } $self->users->flag_deletion( uid => $uid ); @@ -501,6 +495,228 @@ sub privacy { } } +sub social { + my ($self) = @_; + + my $user = $self->current_user; + + if ( $self->param('action') and $self->param('action') eq 'save' ) { + if ( $self->validation->csrf_protect->has_error('csrf_token') ) { + $self->render( + 'social', + invalid => 'csrf', + ); + return; + } + + my %opt; + my $accept_follow = $self->param('accept_follow'); + + if ( $accept_follow eq 'yes' ) { + $opt{accept_follows} = 1; + } + elsif ( $accept_follow eq 'request' ) { + $opt{accept_follow_requests} = 1; + } + + $self->users->set_social( + uid => $user->{id}, + %opt + ); + + $self->flash( success => 'social' ); + $self->redirect_to('account'); + } + else { + if ( $user->{accept_follows} ) { + $self->param( accept_follow => 'yes' ); + } + elsif ( $user->{accept_follow_requests} ) { + $self->param( accept_follow => 'request' ); + } + else { + $self->param( accept_follow => 'no' ); + } + $self->render( 'social', name => $user->{name} ); + } +} + +sub social_list { + my ($self) = @_; + + my $kind = $self->stash('kind'); + my $user = $self->current_user; + + if ( $kind eq 'follow-requests' ) { + my @follow_reqs + = $self->users->get_follow_requests( uid => $user->{id} ); + $self->render( + 'social_list', + type => 'follow-requests', + entries => [@follow_reqs], + notifications => $user->{notifications}, + ); + } + elsif ( $kind eq 'followers' ) { + my @followers = $self->users->get_followers( uid => $user->{id} ); + $self->render( + 'social_list', + type => 'followers', + entries => [@followers], + notifications => $user->{notifications}, + ); + } + elsif ( $kind eq 'follows' ) { + my @following = $self->users->get_followees( uid => $user->{id} ); + $self->render( + 'social_list', + type => 'follows', + entries => [@following], + notifications => $user->{notifications}, + ); + } + elsif ( $kind eq 'blocks' ) { + my @blocked = $self->users->get_blocked_users( uid => $user->{id} ); + $self->render( + 'social_list', + type => 'blocks', + entries => [@blocked], + notifications => $user->{notifications}, + ); + } + else { + $self->render( 'not_found', status => 404 ); + } +} + +sub social_action { + my ($self) = @_; + + my $user = $self->current_user; + my $action = $self->param('action'); + my $target_ids = $self->param('target'); + my $redirect_to = $self->param('redirect_to'); + + for my $key ( + qw(follow request_follow follow_or_request unfollow remove_follower cancel_follow_request accept_follow_request reject_follow_request block unblock) + ) + { + if ( $self->param($key) ) { + $action = $key; + $target_ids = $self->param($key); + } + } + + if ( $self->validation->csrf_protect->has_error('csrf_token') ) { + $self->redirect_to('/'); + return; + } + + if ( $action and $action eq 'clear_notifications' ) { + $self->users->update_notifications( + db => $self->pg->db, + uid => $user->{id}, + has_follow_requests => 0 + ); + $self->flash( success => 'clear_notifications' ); + $self->redirect_to('account'); + return; + } + + if ( not( $action and $target_ids and $redirect_to ) ) { + $self->redirect_to('/'); + return; + } + + for my $target_id ( split( qr{,}, $target_ids ) ) { + my $target = $self->users->get_privacy_by( uid => $target_id ); + + if ( not $target ) { + next; + } + + if ( $action eq 'follow' and $target->{accept_follows} ) { + $self->users->follow( + uid => $user->{id}, + target => $target->{id} + ); + } + elsif ( $action eq 'request_follow' + and $target->{accept_follow_requests} ) + { + $self->users->request_follow( + uid => $user->{id}, + target => $target->{id} + ); + } + elsif ( $action eq 'follow_or_request' ) { + if ( $target->{accept_follows} ) { + $self->users->follow( + uid => $user->{id}, + target => $target->{id} + ); + } + elsif ( $target->{accept_follow_requests} ) { + $self->users->request_follow( + uid => $user->{id}, + target => $target->{id} + ); + } + } + elsif ( $action eq 'unfollow' ) { + $self->users->unfollow( + uid => $user->{id}, + target => $target->{id} + ); + } + elsif ( $action eq 'remove_follower' ) { + $self->users->remove_follower( + uid => $user->{id}, + follower => $target->{id} + ); + } + elsif ( $action eq 'cancel_follow_request' ) { + $self->users->cancel_follow_request( + uid => $user->{id}, + target => $target->{id} + ); + } + elsif ( $action eq 'accept_follow_request' ) { + $self->users->accept_follow_request( + uid => $user->{id}, + applicant => $target->{id} + ); + } + elsif ( $action eq 'reject_follow_request' ) { + $self->users->reject_follow_request( + uid => $user->{id}, + applicant => $target->{id} + ); + } + elsif ( $action eq 'block' ) { + $self->users->block( + uid => $user->{id}, + target => $target->{id} + ); + } + elsif ( $action eq 'unblock' ) { + $self->users->unblock( + uid => $user->{id}, + target => $target->{id} + ); + } + + if ( $redirect_to eq 'profile' ) { + + # profile links do not perform bulk actions + $self->redirect_to( '/p/' . $target->{name} ); + return; + } + } + + $self->redirect_to($redirect_to); +} + sub profile { my ($self) = @_; my $user = $self->current_user; @@ -1012,11 +1228,21 @@ sub confirm_mail { } sub account { - my ($self) = @_; - my $uid = $self->current_user->{id}; + my ($self) = @_; + my $uid = $self->current_user->{id}; + my $follow_requests = $self->users->has_follow_requests( uid => $uid ); + my $followers = $self->users->has_followers( uid => $uid ); + my $following = $self->users->has_followees( uid => $uid ); + my $blocked = $self->users->has_blocked_users( uid => $uid ); - $self->render( 'account', - api_token => $self->users->get_api_token( uid => $uid ) ); + $self->render( + 'account', + api_token => $self->users->get_api_token( uid => $uid ), + num_follow_requests => $follow_requests, + num_followers => $followers, + num_following => $following, + num_blocked => $blocked, + ); $self->users->mark_seen( uid => $uid ); } diff --git a/lib/Travelynx/Controller/Profile.pm b/lib/Travelynx/Controller/Profile.pm index 86b8922..b11dd71 100755 --- a/lib/Travelynx/Controller/Profile.pm +++ b/lib/Travelynx/Controller/Profile.pm @@ -186,6 +186,23 @@ sub profile { metadata => $profile->{metadata}, public_level => $user->{public_level}, is_self => $is_self, + following => ( $relation and $relation eq 'follows' ) ? 1 : 0, + follow_requested => ( $relation and $relation eq 'requests_follow' ) + ? 1 + : 0, + can_follow => ( $my_user and $user->{accept_follows} and not $relation ) + ? 1 + : 0, + can_request_follow => + ( $my_user and $user->{accept_follow_requests} and not $relation ) + ? 1 + : 0, + follows_me => ( $inverse_relation and $inverse_relation eq 'follows' ) + ? 1 + : 0, + follow_reqs_me => + ( $inverse_relation and $inverse_relation eq 'requests_follow' ) ? 1 + : 0, journey => $status, journey_visibility => $visibility, journeys => [@journeys], @@ -201,6 +218,24 @@ sub journey_details { $self->param( journey_id => $journey_id ); + my $my_user; + my $relation; + my $inverse_relation; + my $is_self; + if ( $self->is_user_authenticated ) { + $my_user = $self->current_user; + if ( $my_user->{id} == $user->{id} ) { + $is_self = 1; + $my_user = undef; + } + else { + $relation = $self->users->get_relation( + subject => $my_user->{id}, + object => $user->{id} + ); + } + } + if ( not( $user and $journey_id and $journey_id =~ m{ ^ \d+ $ }x ) ) { $self->render( 'journey', @@ -249,7 +284,12 @@ sub journey_details { and $self->journey_token_ok($journey) ) or ( $visibility eq 'travelynx' - and ( ( $self->is_user_authenticated and not $is_past ) + and ( ( $my_user and not $is_past ) + or $self->journey_token_ok($journey) ) + ) + or ( + $visibility eq 'followers' + and ( ( $relation and $relation eq 'follows' ) or $self->journey_token_ok($journey) ) ) ) @@ -337,6 +377,24 @@ sub user_status { return; } + my $my_user; + my $relation; + my $inverse_relation; + my $is_self; + if ( $self->is_user_authenticated ) { + $my_user = $self->current_user; + if ( $my_user->{id} == $user->{id} ) { + $is_self = 1; + $my_user = undef; + } + else { + $relation = $self->users->get_relation( + subject => $my_user->{id}, + object => $user->{id} + ); + } + } + my $status = $self->get_user_status( $user->{id} ); if ( @@ -364,7 +422,12 @@ sub user_status { and $self->journey_token_ok( $journey, $ts ) ) or ( $visibility eq 'travelynx' - and ( $self->is_user_authenticated + and ( $my_user + or $self->journey_token_ok( $journey, $ts ) ) + ) + or ( + $visibility eq 'followers' + and ( ( $relation and $relation eq 'follows' ) or $self->journey_token_ok( $journey, $ts ) ) ) ) @@ -408,7 +471,12 @@ sub user_status { and $self->status_token_ok( $status, $ts ) ) or ( $visibility eq 'travelynx' - and ( $self->is_user_authenticated + and ( $my_user + or $self->status_token_ok( $status, $ts ) ) + ) + or ( + $visibility eq 'followers' + and ( ( $relation and $relation eq 'follows' ) or $self->status_token_ok( $status, $ts ) ) ) ) @@ -486,6 +554,24 @@ sub status_card { return; } + my $my_user; + my $relation; + my $inverse_relation; + my $is_self; + if ( $self->is_user_authenticated ) { + $my_user = $self->current_user; + if ( $my_user->{id} == $user->{id} ) { + $is_self = 1; + $my_user = undef; + } + else { + $relation = $self->users->get_relation( + subject => $my_user->{id}, + object => $user->{id} + ); + } + } + my $status = $self->get_user_status( $user->{id} ); my $visibility; if ( $status->{checked_in} or $status->{arr_name} ) { @@ -500,7 +586,12 @@ sub status_card { and $self->status_token_ok($status) ) or ( $visibility eq 'travelynx' - and ( $self->is_user_authenticated + and ( $my_user + or $self->status_token_ok($status) ) + ) + or ( + $visibility eq 'followers' + and ( ( $relation and $relation eq 'follows' ) or $self->status_token_ok($status) ) ) ) @@ -523,6 +614,7 @@ sub status_card { public_level => $user->{public_level}, journey => $status, journey_visibility => $visibility, + from_profile => $self->param('profile') ? 1 : 0, ); } diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm index 27005e8..5483e00 100755 --- a/lib/Travelynx/Controller/Traveling.pm +++ b/lib/Travelynx/Controller/Traveling.pm @@ -1459,6 +1459,7 @@ sub journey_details { if ( $visibility eq 'public' or $visibility eq 'travelynx' + or $visibility eq 'followers' or $visibility eq 'unlisted' ) { my $delay = 'pünktlich '; diff --git a/lib/Travelynx/Model/Users.pm b/lib/Travelynx/Model/Users.pm index 7326e34..ade9711 100644 --- a/lib/Travelynx/Model/Users.pm +++ b/lib/Travelynx/Model/Users.pm @@ -180,13 +180,14 @@ sub get_privacy_by { my $res = $db->select( 'users', - [ 'id', 'public_level', 'accept_follows' ], + [ 'id', 'name', 'public_level', 'accept_follows' ], { %where, status => 1 } ); if ( my $user = $res->hash ) { return { id => $user->{id}, + name => $user->{name}, public_level => $user->{public_level}, # todo remove? default_visibility => $user->{public_level} & 0x7f, default_visibility_str => @@ -777,16 +778,16 @@ sub get_profile { sub get_relation { my ( $self, %opt ) = @_; - my $db = $opt{db} // $self->{pg}->db; - my $uid = $opt{uid}; - my $target = $opt{target}; + my $db = $opt{db} // $self->{pg}->db; + my $subject = $opt{subject}; + my $object = $opt{object}; my $res_h = $db->select( 'relations', ['predicate'], { - subject_id => $uid, - object_id => $target + subject_id => $subject, + object_id => $object, } )->hash; @@ -824,6 +825,24 @@ sub update_notifications { $db->update( 'users', { notifications => $notifications }, { id => $uid } ); } +sub follow { + my ( $self, %opt ) = @_; + + my $db = $opt{db} // $self->{pg}->db; + my $uid = $opt{uid}; + my $target = $opt{target}; + + $db->insert( + 'relations', + { + subject_id => $uid, + predicate => $predicate_atoi{follows}, + object_id => $target, + ts => DateTime->now( time_zone => 'Europe/Berlin' ), + } + ); +} + sub request_follow { my ( $self, %opt ) = @_; @@ -920,6 +939,16 @@ sub reject_follow_request { } } +sub cancel_follow_request { + my ( $self, %opt ) = @_; + + $self->reject_follow_request( + db => $opt{db}, + uid => $opt{target}, + applicant => $opt{uid}, + ); +} + sub unfollow { my ( $self, %opt ) = @_; @@ -1005,9 +1034,50 @@ sub get_followers { my $db = $opt{db} // $self->{pg}->db; my $uid = $opt{uid}; - my $res = $db->select( 'followers', [ 'id', 'name' ], { self_id => $uid } ); + my $res = $db->select( + 'followers', + [ 'id', 'name', 'accept_follows', 'inverse_predicate' ], + { self_id => $uid } + ); - return $res->hashes->each; + my @ret; + while ( my $row = $res->hash ) { + push( + @ret, + { + id => $row->{id}, + name => $row->{name}, + following_back => ( + $row->{inverse_predicate} + and $row->{inverse_predicate} == $predicate_atoi{follows} + ) ? 1 : 0, + followback_requested => ( + $row->{inverse_predicate} + and $row->{inverse_predicate} + == $predicate_atoi{requests_follow} + ) ? 1 : 0, + can_follow_back => ( + not $row->{inverse_predicate} + and $row->{accept_follows} == 2 + ) ? 1 : 0, + can_request_follow_back => ( + not $row->{inverse_predicate} + and $row->{accept_follows} == 1 + ) ? 1 : 0, + } + ); + } + return @ret; +} + +sub has_followers { + my ( $self, %opt ) = @_; + + my $db = $opt{db} // $self->{pg}->db; + my $uid = $opt{uid}; + + return $db->select( 'followers', 'count(*) as count', { self_id => $uid } ) + ->hash->{count}; } sub get_follow_requests { @@ -1043,6 +1113,16 @@ sub get_followees { return $res->hashes->each; } +sub has_followees { + my ( $self, %opt ) = @_; + + my $db = $opt{db} // $self->{pg}->db; + my $uid = $opt{uid}; + + return $db->select( 'followees', 'count(*) as count', { self_id => $uid } ) + ->hash->{count}; +} + sub get_blocked_users { my ( $self, %opt ) = @_; @@ -1055,4 +1135,14 @@ sub get_blocked_users { return $res->hashes->each; } +sub has_blocked_users { + my ( $self, %opt ) = @_; + + my $db = $opt{db} // $self->{pg}->db; + my $uid = $opt{uid}; + + return $db->select( 'blocked_users', 'count(*) as count', + { self_id => $uid } )->hash->{count}; +} + 1; diff --git a/templates/_checked_in.html.ep b/templates/_checked_in.html.ep index 2d39842..1002654 100644 --- a/templates/_checked_in.html.ep +++ b/templates/_checked_in.html.ep @@ -3,8 +3,13 @@
- - Eingecheckt in <%= $journey->{train_type} %> <%= $journey->{train_no} %> + + % if ($journey->{train_line}) { + <%= $journey->{train_type} %> <%= $journey->{train_line} %> <%= $journey->{train_no} %> + % } + % else { + <%= $journey->{train_type} %> <%= $journey->{train_no} %> + % } % if ($journey->{comment}) {

<%= $journey->{comment} %>

diff --git a/templates/_format_train.html.ep b/templates/_format_train.html.ep new file mode 100644 index 0000000..5b61682 --- /dev/null +++ b/templates/_format_train.html.ep @@ -0,0 +1,9 @@ +% if ($journey->{extra_data}{wagonorder_pride}) { + 🏳️‍🌈 +% } +% if ($journey->{train_line}) { + <%= $journey->{train_type} %> <%= $journey->{train_line} %> <%= $journey->{train_no} %> +% } +% else { + <%= $journey->{train_type} %> <%= $journey->{train_no} %> +% } diff --git a/templates/_public_status_card.html.ep b/templates/_public_status_card.html.ep index 94ebf44..8e3eec7 100644 --- a/templates/_public_status_card.html.ep +++ b/templates/_public_status_card.html.ep @@ -1,28 +1,28 @@ -
+
% if ($journey->{checked_in}) {
- <%= $name %> ist unterwegs - % if ($journey_visibility) { - <%= visibility_icon($journey_visibility) %> - % } + + % if (stash('from_profile')) { + Unterwegs mit <%= include '_format_train', journey => $journey %> + % } + % else { + <%= $name %> ist unterwegs + % } + % if ($journey_visibility) { + <%= visibility_icon($journey_visibility) %> + % } % if ($public_level & 0x04 and $journey->{comment}) {

„<%= $journey->{comment} %>“

% }

-

- % if ($journey->{train_line}) { - <%= $journey->{train_type} %> <%= $journey->{train_line} %> <%= $journey->{train_no} %> - % } - % else { - <%= $journey->{train_type} %> <%= $journey->{train_no} %> - % } - % if ($journey->{extra_data}{wagonorder_pride}) { - 🏳️‍🌈 - % } -
+ % if (not stash('from_profile')) { +
+ %= include '_format_train', journey => $journey +
+ % }
- <%= $name %> ist gerade nicht eingecheckt + % if (stash('from_profile')) { + Aktuell nicht eingecheckt + % } + % else { + <%= $name %> ist gerade nicht eingecheckt + % }

% if ($journey->{arr_name}) { Zuletzt gesehen diff --git a/templates/account.html.ep b/templates/account.html.ep index ef6b847..bb03c7b 100644 --- a/templates/account.html.ep +++ b/templates/account.html.ep @@ -1,4 +1,4 @@ -% if (my $invalid = stash('invalid')) { +% if (my $invalid = flash('invalid')) { %= include '_invalid_input', invalid => $invalid % } @@ -19,6 +19,9 @@ % elsif ($success eq 'privacy') { Einstellungen zu öffentlichen Account-Daten geändert % } + % elsif ($success eq 'social') { + Einstellungen zur Interaktionen mit anderen Accounts geändert + % } % elsif ($success eq 'traewelling') { Träwelling-Verknüpfung aktualisiert % } @@ -31,6 +34,9 @@ % elsif ($success eq 'webhook') { Web Hook aktualisiert % } + % elsif ($success eq 'clear_notifications') { + Benachrichtigungen gelesen + % }

@@ -77,6 +83,27 @@ • Öffentliches Profil + + Interaktion + + edit + % if ($acc->{accept_follows}) { + Accounts können dir direkt folgen + % } + % elsif ($acc->{accept_follow_requests}) { + Accounts können dir auf Anfrage folgen + % if ($num_follow_requests == 1) { + – eine offene Anfrage + % } elsif ($num_follow_requests) { + – <%= $num_follow_requests %> offene Anfragen + % } + + % } + % else { + Accounts können dir nicht folgen + % } + + Web Hook @@ -152,6 +179,72 @@
+% if ($num_follow_requests or $num_followers or $num_following or $num_blocked) { +
+
+

Interaktion

+ + + + + + + + + + + + + + + + + +
Anfragen + % if ($num_follow_requests == 0) { + keine offen + % } + % elsif ($num_follow_requests == 1) { + ein Account + % } + % else { + <%= $num_follow_requests %> Accounts + % } +
Dir folg<%= $num_followers == 1 ? 't' : 'en' %> + % if ($num_followers == 0) { + keine Accounts + % } + % elsif ($num_followers == 1) { + ein Account + % } + % else { + <%= $num_followers %> Accounts + % } +
Du folgst + % if ($num_following == 0) { + keinen Accounts + % } + % elsif ($num_following == 1) { + einem Account + % } + % else { + <%= $num_following %> Accounts + % } +
Blockiert + % if ($num_blocked == 0) { + keine Accounts + % } + % elsif ($num_blocked == 1) { + ein Account + % } + % else { + <%= $num_blocked %> Accounts + % } +
+
+
+% } + % my $token = stash('api_token') // {};
diff --git a/templates/edit_visibility.html.ep b/templates/edit_visibility.html.ep index 28b1426..9bf8d56 100644 --- a/templates/edit_visibility.html.ep +++ b/templates/edit_visibility.html.ep @@ -40,7 +40,10 @@ Die Fahrt ist öffentlich sichtbar. % } % elsif ($user_level eq 'travelynx') { - Die Fahrt ist nur für auf dieser Seite angemeldete Personen oder mit Link sichtbar. + Die Fahrt ist nur für auf dieser Seite angemeldete Accounts oder mit Link sichtbar. + % } + % elsif ($user_level eq 'followers') { + Die Fahrt ist nur für dir folgende Accounts oder mit Link sichtbar. % } % elsif ($user_level eq 'unlisted') { Die Fahrt ist nur mit Link sichtbar. @@ -68,29 +71,27 @@
-
diff --git a/templates/layouts/default.html.ep b/templates/layouts/default.html.ep index bae0fcc..51608d5 100644 --- a/templates/layouts/default.html.ep +++ b/templates/layouts/default.html.ep @@ -70,6 +70,7 @@ %= javascript "/static/${av}/leaflet/leaflet.js" % } +% my $acc = is_user_authenticated() && current_user(); -

Profil

+

Vergangene Fahrten

@@ -111,10 +109,10 @@ Fahrten, die über die Standardeinstellung (siehe oben) oder per individueller Einstellung für die aufrufende Person sichtbar sind, werden hier verlinkt. Derzeit werden nur die letzten zehn Fahrten - angezeigt; dies kann sich in Zukunft ändern. + angezeigt; in Zukunft wird dies ggf. auf sämtliche Fahrten im + gewählten Zeitraum erweitert.
-

Vergangenheit

@@ -133,7 +131,7 @@
Hier kannst du auswählen, ob alle deiner vergangenen Fahrten für - Profil und Detailseiten in Frage kommen, oder nur die letzten vier + Profil und Detailseiten in Frage kommen oder nur die letzten vier Wochen zugänglich sein sollen. Sofern du sie auf die letzten vier Wochen beschränkst, sind ältere Fahrten nur über einen mit Hilfe des „Teilen“-Knopfs erstellten Links zugänglich. diff --git a/templates/profile.html.ep b/templates/profile.html.ep index 63bc3ff..06f8cfe 100644 --- a/templates/profile.html.ep +++ b/templates/profile.html.ep @@ -11,14 +11,81 @@
% }
-
- %= include '_public_status_card', name => $name, public_level => $public_level, journey => $journey, journey_visibility => $journey_visibility +
+
+
+ <%= $name %> + % if ($following and $follows_me) { + group + % } + % elsif ($follow_reqs_me) { + + notifications + + % } + % elsif ($is_self) { + edit + % } + + % if ($bio) { + %== $bio + % } + % if (@{$metadata // []}) { + + % for my $entry (@{$metadata}) { + + + + + % } +
<%= $entry->{key} %><%== $entry->{value}{html} %>
+ % } +
+ % if ($following or $follow_requested or $can_follow or $can_request_follow) { +
+ %= form_for "/social-action" => (method => 'POST') => begin + %= csrf_field + %= hidden_field target => $uid + %= hidden_field redirect_to => 'profile' + % if ($following) { + + % } + % elsif ($follow_requested) { + + % } + % elsif ($can_follow or $can_request_follow) { + + % } + %= end +
+ % } +
+
+
+
+
+ %= include '_public_status_card', name => $name, public_level => $public_level, journey => $journey, journey_visibility => $journey_visibility, from_profile => 1
% if ($journeys and @{$journeys}) {
-

Letzte Fahrten von <%= $name %>

+

Vergangene Fahrten

%= include '_history_trains', date_format => '%d.%m.%Y', link_prefix => "/p/${name}/j/", journeys => $journeys; diff --git a/templates/social.html.ep b/templates/social.html.ep new file mode 100644 index 0000000..2743abf --- /dev/null +++ b/templates/social.html.ep @@ -0,0 +1,66 @@ +

Interaktion

+
+
+ Hier kannst du einstellen, ob und wie dir Accounts folgen können. + Ebenfalls kannst du bestehende Verbindungen und Folge-Anfragen + bearbeiten. Die hier vorgenommenen Einstellungen haben keinen Einfluss + darauf, ob/wie du anderen Accounts folgen kannst. +
+
+%= form_for '/account/social' => (method => 'POST') => begin +%= csrf_field +

Folgen

+
+
+

+ Accounts die dir folgen können alle Checkins sehen, die nicht als privat oder nur mit Link zugänglich vermerkt sind. + Später werden sie zusätzlich die Möglichkeit haben, deinen aktuellen Checkin (sofern sichtbar) als Teil einer Übersicht über die Checkins aller gefolgten Accounts direkt anzusehen (analog zur Timeline im Fediverse). +

+

+ Du hast jederzeit die Möglichkeit, Accounts aus deiner Followerliste zu entfernen, Folge-Anfragen abzulehnen oder Accounts zu blockieren, so dass sie dir weder folgen noch neue Folge-Anfragen stellen können. +

+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+%= end diff --git a/templates/social_list.html.ep b/templates/social_list.html.ep new file mode 100644 index 0000000..ed8e92a --- /dev/null +++ b/templates/social_list.html.ep @@ -0,0 +1,230 @@ +%= form_for "/social-action" => (method => 'POST') => begin +%= csrf_field +%= hidden_field redirect_to => '/account' +% my $count = scalar @{$entries}; +% if ($type eq 'follow-requests') { +
+
+

Folge-Anfragen

+
+
+
+
+ block
Blockieren +
+
+ cancel
Ablehnen +
+
+ check
Annehmen +
+
+ % if ($notifications) { +
+
+ +
+
+ % } + +% } +% elsif ($type eq 'followers') { +
+
+ % if ($count == 1) { +

Dir folgt ein Account

+ % } + % else { +

Dir folgen <%= $count %> Accounts

+ % } +
+
+
+
+ block
Blockieren +
+
+ remove
Entfernen +
+
+ person_add
Zurückfolgen +
+
+
+
+
+
+ access_time
Folgen angefragt +
+
+ group
Du folgst diesem Account +
+
+ +% } +% elsif ($type eq 'follows') { +
+
+ % if ($count == 1) { +

Du folgst einem Account

+ % } + % else { +

Du folgst <%= $count %> Accounts

+ % } +
+
+
+
+ remove
Nicht mehr folgen +
+
+ +% } +% elsif ($type eq 'blocks') { +
+
+

Blockierte Accounts

+

+ Blockierte Accounts können dir nicht folgen und keine Folge-Anfragen stellen. + Sie haben weiterhin Zugriff auf deine als öffentlich oder travelynx-intern markierten Checkins. +

+
+
+
+
+ remove
Entblockieren +
+
+ +% } +%= end + +
+
+ %= form_for "/social-action" => (method => 'POST') => begin + %= csrf_field + %= hidden_field redirect_to => "/account/social/$type" + + % for my $entry (@{$entries}) { + + + % if ($type eq 'follow-requests') { + + + + % } + % elsif ($type eq 'followers') { + + + + % } + % elsif ($type eq 'follows') { + + % } + % elsif ($type eq 'blocks') { + + % } + + % } +
<%= $entry->{name} %> + + + + + + + + + + + % if ($entry->{following_back}) { + group + % } + % elsif ($entry->{followback_requested}) { + access_time + % } + % elsif ($entry->{can_follow_back} or $entry->{can_request_follow_back}) { + + % } + + + + +
+ %= end +
+