Skip to content

Commit

Permalink
End of section 13
Browse files Browse the repository at this point in the history
  • Loading branch information
TryCatchLearn committed Aug 5, 2023
1 parent d22ada7 commit 3ddb556
Show file tree
Hide file tree
Showing 31 changed files with 407 additions and 40 deletions.
7 changes: 4 additions & 3 deletions API/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ public async Task<ActionResult<UserDto>> Register(RegisterDto registerDto)
{
Username = user.UserName,
Token = _tokenService.CreateToken(user),
KnownAs = user.KnownAs

KnownAs = user.KnownAs,
Gender = user.Gender
};
}

Expand All @@ -71,7 +71,8 @@ public async Task<ActionResult<UserDto>> Login(LoginDto loginDto)
Username = user.UserName,
Token = _tokenService.CreateToken(user),
PhotoUrl = user.Photos.FirstOrDefault(x => x.IsMain)?.Url,
KnownAs = user.KnownAs
KnownAs = user.KnownAs,
Gender = user.Gender
};
}

Expand Down
4 changes: 3 additions & 1 deletion API/Controllers/BaseApiController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Microsoft.AspNetCore.Mvc;
using API.Helpers;
using Microsoft.AspNetCore.Mvc;

namespace API.Controllers;

[ServiceFilter(typeof(LogUserActivity))]
[ApiController]
[Route("api/[controller]")]
public class BaseApiController :ControllerBase
Expand Down
14 changes: 12 additions & 2 deletions API/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using API.DTOs;
using API.Entities;
using API.Extensions;
using API.Helpers;
using API.Interfaces;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
Expand All @@ -26,9 +27,18 @@ public UsersController(IUserRepository userRepository, IMapper mapper, IPhotoSer
}

[HttpGet]
public async Task<ActionResult<IEnumerable<MemberDto>>> GetUsers()
public async Task<ActionResult<IEnumerable<MemberDto>>> GetUsers([FromQuery] UserParams userParams)
{
var users = await _userRepository.GetMembersAsync();
var currentUser = await _userRepository.GetUserByUsernameAsync(User.GetUsername());
userParams.CurrentUsername = currentUser.UserName;

if (string.IsNullOrEmpty(userParams.Gender))
userParams.Gender = currentUser.Gender == "male" ? "female" : "male";

var users = await _userRepository.GetMembersAsync(userParams);

Response.AddPaginationHeader(new PaginationHeader(users.CurrentPage, users.PageSize,
users.TotalCount, users.TotalPages));

return Ok(users);
}
Expand Down
1 change: 1 addition & 0 deletions API/DTOs/UserDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ public class UserDto
public string Token { get; set; }
public string PhotoUrl { get; set; }
public string KnownAs { get; set; }
public string Gender { get; set; }
}
25 changes: 21 additions & 4 deletions API/Data/UserRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using API.Entities;
using API.Helpers;
using API.Interfaces;
using AutoMapper;
using AutoMapper.QueryableExtensions;
Expand All @@ -25,11 +26,27 @@ public async Task<MemberDto> GetMemberAsync(string username)
.SingleOrDefaultAsync();
}

public async Task<IEnumerable<MemberDto>> GetMembersAsync()
public async Task<PagedList<MemberDto>> GetMembersAsync(UserParams userParams)
{
return await _context.Users
.ProjectTo<MemberDto>(_mapper.ConfigurationProvider)
.ToListAsync();
var query = _context.Users.AsQueryable();

query = query.Where(u => u.UserName != userParams.CurrentUsername);
query = query.Where(u => u.Gender == userParams.Gender);

var minDob = DateOnly.FromDateTime(DateTime.Today.AddYears(-userParams.MaxAge - 1));
var maxDob = DateOnly.FromDateTime(DateTime.Today.AddYears(-userParams.MinAge));

query = query.Where(u => u.DateOfBirth >= minDob && u.DateOfBirth <= maxDob);

query = userParams.OrderBy switch
{
"created" => query.OrderByDescending(u => u.Created),
_ => query.OrderByDescending(u => u.LastActive)
};

return await PagedList<MemberDto>.CreateAsync(query.AsNoTracking()
.ProjectTo<MemberDto>(_mapper.ConfigurationProvider),
userParams.PageNumber, userParams.PageSize);
}

public async Task<AppUser> GetUserByIdAsync(int id)
Expand Down
1 change: 1 addition & 0 deletions API/Extensions/ApplicationServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.Configure<CloudinarySettings>(config.GetSection("CloudinarySettings"));
services.AddScoped<IPhotoService, PhotoService>();
services.AddScoped<LogUserActivity>();

return services;
}
Expand Down
7 changes: 6 additions & 1 deletion API/Extensions/ClaimsPrincipalExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ public static class ClaimsPrincipalExtensions
{
public static string GetUsername(this ClaimsPrincipal user)
{
return user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
return user.FindFirst(ClaimTypes.Name)?.Value;
}

public static int GetUserId(this ClaimsPrincipal user)
{
return int.Parse(user.FindFirst(ClaimTypes.NameIdentifier)?.Value);
}
}
13 changes: 13 additions & 0 deletions API/Extensions/HttpExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Text.Json;

namespace API.Extensions;

public static class HttpExtensions
{
public static void AddPaginationHeader(this HttpResponse response, PaginationHeader header)
{
var jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
response.Headers.Add("Pagination", JsonSerializer.Serialize(header, jsonOptions));
response.Headers.Add("Access-Control-Expose-Headers", "Pagination");
}
}
22 changes: 22 additions & 0 deletions API/Helpers/LogUserActivity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using API.Extensions;
using API.Interfaces;
using Microsoft.AspNetCore.Mvc.Filters;

namespace API.Helpers;

public class LogUserActivity : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var resultContext = await next();

if (!resultContext.HttpContext.User.Identity.IsAuthenticated) return;

var userId = resultContext.HttpContext.User.GetUserId();

var repo = resultContext.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
var user = await repo.GetUserByIdAsync(userId);
user.LastActive = DateTime.UtcNow;
await repo.SaveAllAsync();
}
}
27 changes: 27 additions & 0 deletions API/Helpers/PagedList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.EntityFrameworkCore;

namespace API.Helpers;

public class PagedList<T> : List<T>
{
public PagedList(IEnumerable<T> items, int count, int pageNumber, int pageSize)
{
CurrentPage = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
PageSize = pageSize;
TotalCount = count;
AddRange(items);
}

public int CurrentPage { get; set; }
public int TotalPages { get; set; }
public int PageSize { get; set; }
public int TotalCount { get; set; }

public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
}
17 changes: 17 additions & 0 deletions API/Helpers/PaginationHeader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace API;

public class PaginationHeader
{
public PaginationHeader(int currentPage, int itemsPerPage, int totalItems, int totalPages)
{
CurrentPage = currentPage;
ItemsPerPage = itemsPerPage;
TotalItems = totalItems;
TotalPages = totalPages;
}

public int CurrentPage { get; set; }
public int ItemsPerPage { get; set; }
public int TotalItems { get; set; }
public int TotalPages { get; set; }
}
20 changes: 20 additions & 0 deletions API/Helpers/UserParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace API;

public class UserParams
{
private const int MaxPageSize = 50;
public int PageNumber { get; set; } = 1;
private int _pageSize = 10;

public int PageSize
{
get => _pageSize;
set => _pageSize = (value > MaxPageSize) ? MaxPageSize : value;
}

public string CurrentUsername { get; set; }
public string Gender { get; set; }
public int MinAge { get; set; } = 18;
public int MaxAge { get; set; } = 100;
public string OrderBy { get; set; } = "lastActive";
}
5 changes: 3 additions & 2 deletions API/Interfaces/IUserRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using API.Entities;
using API.Helpers;

namespace API.Interfaces;

Expand All @@ -9,7 +10,7 @@ public interface IUserRepository
Task<IEnumerable<AppUser>> GetUsersAsync();
Task<AppUser> GetUserByIdAsync(int id);
Task<AppUser> GetUserByUsernameAsync(string username);
Task<IEnumerable<MemberDto>> GetMembersAsync();
Task<PagedList<MemberDto>> GetMembersAsync(UserParams userParams);
Task<MemberDto> GetMemberAsync(string username);

}

3 changes: 2 additions & 1 deletion API/Services/TokenService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public string CreateToken(AppUser user)
{
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.NameId, user.UserName)
new Claim(JwtRegisteredClaimNames.NameId, user.Id.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
};

var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
Expand Down
Binary file modified API/datingapp.db
Binary file not shown.
Binary file modified API/datingapp.db-shm
Binary file not shown.
Binary file modified API/datingapp.db-wal
Binary file not shown.
13 changes: 13 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"ng2-file-upload": "^5.0.0",
"ngx-bootstrap": "^11.0.2",
"ngx-spinner": "^16.0.2",
"ngx-timeago": "^3.0.0",
"ngx-toastr": "^17.0.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
Expand Down
11 changes: 11 additions & 0 deletions client/src/app/_models/pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface Pagination {
currentPage: number;
itemsPerPage: number;
totalItems: number;
totalPages: number;
}

export class PaginatedResult<T> {
result?: T;
pagination?: Pagination
}
2 changes: 2 additions & 0 deletions client/src/app/_models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ export interface User {
username: string;
token: string;
photoUrl: string;
knownAs: string;
gender: string;
}
14 changes: 14 additions & 0 deletions client/src/app/_models/userParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { User } from "./user";

export class UserParams {
gender: string;
minAge = 18;
maxAge = 99;
pageNumber = 1;
pageSize = 5;
orderBy = 'lastActive';

constructor(user: User) {
this.gender = user.gender === 'female' ? 'male' : 'female'
}
}
13 changes: 11 additions & 2 deletions client/src/app/_modules/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import { ToastrModule } from 'ngx-toastr';
import { NgxSpinnerModule } from 'ngx-spinner';
import { FileUploadModule } from 'ng2-file-upload';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
import { PaginationModule } from 'ngx-bootstrap/pagination';
import { ButtonsModule } from 'ngx-bootstrap/buttons';
import { TimeagoModule } from "ngx-timeago";

@NgModule({
declarations: [],
imports: [
CommonModule,
BsDropdownModule.forRoot(),
PaginationModule.forRoot(),
ButtonsModule.forRoot(),
TabsModule.forRoot(),
ToastrModule.forRoot({
positionClass: 'toast-bottom-right'
Expand All @@ -20,15 +25,19 @@ import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
type: 'line-scale-party'
}),
FileUploadModule,
BsDatepickerModule.forRoot()
BsDatepickerModule.forRoot(),
TimeagoModule.forRoot()
],
exports: [
BsDropdownModule,
ToastrModule,
TabsModule,
NgxSpinnerModule,
FileUploadModule,
BsDatepickerModule
BsDatepickerModule,
PaginationModule,
ButtonsModule,
TimeagoModule
]
})
export class SharedModule { }
Loading

0 comments on commit 3ddb556

Please sign in to comment.