Merge user feeds & followers #118

Merged
aheathershaw merged 16 commits from feature/111-user-activity-feeds into master 2018-11-19 19:08:50 +00:00
9 changed files with 173 additions and 4 deletions
Showing only changes of commit 245bfe546c - Show all commits

View File

@ -13,6 +13,7 @@ use App\User;
use App\UserActivity; use App\UserActivity;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -39,6 +40,25 @@ class UserController extends Controller
return redirect(route('userSettings')); return redirect(route('userSettings'));
} }
public function followUser($idOrAlias)
{
$user = $this->loadUserProfilePage($idOrAlias);
$isFollowing = $this->getUser()->following()->where('following_user_id', $user->id)->count() > 0;
if (!$isFollowing)
{
$this->getUser()->following()->attach(
$user->id,
[
'created_at' => new \DateTime(),
'updated_at' => new \DateTime()
]
);
}
return response()->json(true);
}
public function resetEmailChangeState(Request $request) public function resetEmailChangeState(Request $request)
{ {
$user = $this->getUser(); $user = $this->getUser();
@ -128,12 +148,24 @@ class UserController extends Controller
$daysInMonth = $this->getDaysInMonths(); $daysInMonth = $this->getDaysInMonths();
// Only logged-in users can follow other users (and if it's not their own page!)
$canFollow = !$this->getUser()->isAnonymous() && $this->getUser()->id != $user->id;
$isFollowing = false;
if ($canFollow)
{
// Is the current user following this user?
$isFollowing = $this->getUser()->following()->where('following_user_id', $user->id)->count() > 0;
}
return Theme::render('gallery.user_profile', [ return Theme::render('gallery.user_profile', [
'active_tab' => $request->get('tab'), 'active_tab' => $request->get('tab'),
'activity_taken' => $this->constructActivityGrid($activity['taken']), 'activity_taken' => $this->constructActivityGrid($activity['taken']),
'activity_uploaded' => $this->constructActivityGrid($activity['uploaded']), 'activity_uploaded' => $this->constructActivityGrid($activity['uploaded']),
'albums' => $albums, 'albums' => $albums,
'cameras' => $cameras, 'cameras' => $cameras,
'can_follow' => $canFollow,
'is_following' => $isFollowing,
'month_days' => $daysInMonth, 'month_days' => $daysInMonth,
'user' => $user 'user' => $user
]); ]);
@ -179,6 +211,19 @@ class UserController extends Controller
return response()->json($result); return response()->json($result);
} }
public function unFollowUser($idOrAlias)
{
$user = $this->loadUserProfilePage($idOrAlias);
$isFollowing = $this->getUser()->following()->where('following_user_id', $user->id)->count() > 0;
if ($isFollowing)
{
$this->getUser()->following()->detach($user->id);
}
return response()->json(true);
}
private function constructActivityGrid(Collection $collection) private function constructActivityGrid(Collection $collection)
{ {
$results = []; $results = [];

View File

@ -62,6 +62,23 @@ class User extends Authenticatable
]); ]);
} }
public function followers()
{
return $this->belongsToMany(User::class, 'user_followers', 'following_user_id', 'user_id');
}
public function following()
{
return $this->belongsToMany(User::class, 'user_followers', 'user_id', 'following_user_id');
}
public function followUrl()
{
return route('followUser', [
'idOrAlias' => (!empty($this->profile_alias) ? trim(strtolower($this->profile_alias)) : $this->id)
]);
}
public function groups() public function groups()
{ {
return $this->belongsToMany(Group::class, 'user_groups'); return $this->belongsToMany(Group::class, 'user_groups');
@ -83,4 +100,11 @@ class User extends Authenticatable
{ {
return trim(!empty($this->profile_alias) ? $this->profile_alias : $this->name); return trim(!empty($this->profile_alias) ? $this->profile_alias : $this->name);
} }
public function unFollowUrl()
{
return route('unFollowUser', [
'idOrAlias' => (!empty($this->profile_alias) ? trim(strtolower($this->profile_alias)) : $this->id)
]);
}
} }

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUserFollowersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_followers', function (Blueprint $table) {
$table->unsignedInteger('user_id');
$table->unsignedInteger('following_user_id');
$table->timestamps();
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
$table->foreign('following_user_id')
->references('id')->on('users')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_followers');
}
}

View File

@ -41622,7 +41622,8 @@ function UserViewModel(urls)
this.data = { this.data = {
feed_items: [], feed_items: [],
is_loading: true, is_loading: true,
selected_view: 'profile' selected_view: 'profile',
user_id: 0
}; };
this.computed = { this.computed = {
@ -41635,6 +41636,16 @@ function UserViewModel(urls)
}; };
this.methods = { this.methods = {
followUser: function(e)
{
$.post(urls.follow_user_url, '', function(data)
{
window.location.reload(true);
});
e.preventDefault();
return false;
},
loadFeedItems: function(e) loadFeedItems: function(e)
{ {
var self = this; var self = this;
@ -41679,6 +41690,16 @@ function UserViewModel(urls)
this.selected_view = 'profile'; this.selected_view = 'profile';
history.pushState('', '', urls.current_url + '?tab=profile'); history.pushState('', '', urls.current_url + '?tab=profile');
e.preventDefault();
return false;
},
unFollowUser: function(e)
{
$.post(urls.unfollow_user_url, '', function(data)
{
window.location.reload(true);
});
e.preventDefault(); e.preventDefault();
return false; return false;
} }

File diff suppressed because one or more lines are too long

View File

@ -86,7 +86,8 @@ function UserViewModel(urls)
this.data = { this.data = {
feed_items: [], feed_items: [],
is_loading: true, is_loading: true,
selected_view: 'profile' selected_view: 'profile',
user_id: 0
}; };
this.computed = { this.computed = {
@ -99,6 +100,16 @@ function UserViewModel(urls)
}; };
this.methods = { this.methods = {
followUser: function(e)
{
$.post(urls.follow_user_url, '', function(data)
{
window.location.reload(true);
});
e.preventDefault();
return false;
},
loadFeedItems: function(e) loadFeedItems: function(e)
{ {
var self = this; var self = this;
@ -143,6 +154,16 @@ function UserViewModel(urls)
this.selected_view = 'profile'; this.selected_view = 'profile';
history.pushState('', '', urls.current_url + '?tab=profile'); history.pushState('', '', urls.current_url + '?tab=profile');
e.preventDefault();
return false;
},
unFollowUser: function(e)
{
$.post(urls.unfollow_user_url, '', function(data)
{
window.location.reload(true);
});
e.preventDefault(); e.preventDefault();
return false; return false;
} }

View File

@ -96,6 +96,8 @@ return [
'albums' => 'Albums by :user_name', 'albums' => 'Albums by :user_name',
'cameras' => 'Cameras', 'cameras' => 'Cameras',
'feed_tab' => 'Activity', 'feed_tab' => 'Activity',
'follow_button' => 'Follow',
'following_button' => 'Following',
'no_albums_p1' => 'No Photo Albums', 'no_albums_p1' => 'No Photo Albums',
'no_albums_p2' => ':user_name has not created any albums yet.', 'no_albums_p2' => ':user_name has not created any albums yet.',
'no_feed_activity_p1' => 'No Activity', 'no_feed_activity_p1' => 'No Activity',

View File

@ -14,6 +14,13 @@
</div> </div>
<div class="col-sm-8 col-md-9 col-xl-10"> <div class="col-sm-8 col-md-9 col-xl-10">
@if ($can_follow)
@if ($is_following)
<a class="pull-right btn btn-lg btn-outline-primary" href="#" v-on:click="unFollowUser"><i class="fa fa-check fa-fw"></i> @lang('gallery.user_profile.following_button')</a>
@else
<a class="pull-right btn btn-lg btn-primary" href="#" v-on:click="followUser">@lang('gallery.user_profile.follow_button')</a>
@endif
@endif
<h1>{{ $user->name }}</h1> <h1>{{ $user->name }}</h1>
@if (!empty($user->profile_alias)) @if (!empty($user->profile_alias))
<h2 class="text-muted">{{ $user->profile_alias }}</h2> <h2 class="text-muted">{{ $user->profile_alias }}</h2>
@ -137,10 +144,13 @@
<script type="text/javascript"> <script type="text/javascript">
var viewModel = new UserViewModel({ var viewModel = new UserViewModel({
current_url: '{{ url()->current() }}', current_url: '{{ url()->current() }}',
feed_url: '{{ $user->feedJsonUrl() }}' feed_url: '{{ $user->feedJsonUrl() }}',
follow_user_url: '{{ $user->followUrl() }}',
unfollow_user_url: '{{ $user->unFollowUrl() }}'
}); });
var app = new Vue(viewModel); var app = new Vue(viewModel);
app.user_id = '{{ $user->id }}';
app.loadFeedItems(); app.loadFeedItems();
@if ($active_tab == 'feed') @if ($active_tab == 'feed')

View File

@ -143,6 +143,12 @@ Route::get('label/{labelAlias}', 'Gallery\LabelController@show')
Route::get('u/{idOrAlias}/feed.json', 'Gallery\UserController@showFeedJson') Route::get('u/{idOrAlias}/feed.json', 'Gallery\UserController@showFeedJson')
->name('viewUserFeedJson') ->name('viewUserFeedJson')
->where('idOrAlias', '.*'); ->where('idOrAlias', '.*');
Route::post('u/{idOrAlias}/follow', 'Gallery\UserController@followUser', ['middleware' => 'auth'])
->name('followUser')
->where('idOrAlias', '.*');
Route::post('u/{idOrAlias}/unfollow', 'Gallery\UserController@unFollowUser', ['middleware' => 'auth'])
->name('unFollowUser')
->where('idOrAlias', '.*');
Route::get('u/{idOrAlias}', 'Gallery\UserController@show') Route::get('u/{idOrAlias}', 'Gallery\UserController@show')
->name('viewUser') ->name('viewUser')
->where('idOrAlias', '.*'); ->where('idOrAlias', '.*');