├── Reactivities
├── client-app
│ ├── .env.production
│ ├── src
│ │ ├── react-app-env.d.ts
│ │ ├── app
│ │ │ ├── model
│ │ │ │ ├── serverError.ts
│ │ │ │ ├── comment.ts
│ │ │ │ ├── user.ts
│ │ │ │ ├── pagination.ts
│ │ │ │ ├── profile.ts
│ │ │ │ └── activity.ts
│ │ │ ├── common
│ │ │ │ ├── options
│ │ │ │ │ └── categoryOptions.ts
│ │ │ │ ├── modals
│ │ │ │ │ └── ModalContainer.tsx
│ │ │ │ ├── imageUpload
│ │ │ │ │ ├── PhotoWidgetCropper.tsx
│ │ │ │ │ ├── PhotoWidgetDropzone.tsx
│ │ │ │ │ └── PhotoUploadWidget.tsx
│ │ │ │ └── form
│ │ │ │ │ ├── MyTextArea.tsx
│ │ │ │ │ ├── MyTextInput.tsx
│ │ │ │ │ ├── MyDateInput.tsx
│ │ │ │ │ └── MySelectInput.tsx
│ │ │ ├── layout
│ │ │ │ ├── ScrollToTop.tsx
│ │ │ │ ├── LoadingComponent.tsx
│ │ │ │ ├── PrivateRoute.tsx
│ │ │ │ ├── styles.css
│ │ │ │ ├── NavBar.tsx
│ │ │ │ └── App.tsx
│ │ │ └── stores
│ │ │ │ ├── modalStore.ts
│ │ │ │ ├── store.ts
│ │ │ │ ├── commonStore.ts
│ │ │ │ ├── userStore.ts
│ │ │ │ └── commentStore.ts
│ │ ├── features
│ │ │ ├── activities
│ │ │ │ ├── details
│ │ │ │ │ ├── ActivityDetailed.tsx
│ │ │ │ │ ├── ActivityDetails.tsx
│ │ │ │ │ ├── ActivityDetailedInfo.tsx
│ │ │ │ │ └── ActivityDetailedSidebar.tsx
│ │ │ │ └── dashboard
│ │ │ │ │ ├── ActivityList.tsx
│ │ │ │ │ ├── ActivityFilters.tsx
│ │ │ │ │ ├── AcivityListItemAttendee.tsx
│ │ │ │ │ ├── ActivityListItemPlaceholder.tsx
│ │ │ │ │ └── ActivityDashboard.tsx
│ │ │ ├── errors
│ │ │ │ ├── ValidationErrors.tsx
│ │ │ │ ├── NotFound.tsx
│ │ │ │ ├── ServerError.tsx
│ │ │ │ └── TestError.tsx
│ │ │ ├── profiles
│ │ │ │ ├── ProfileCard.tsx
│ │ │ │ ├── ProfileFollowings.tsx
│ │ │ │ ├── ProfilePage.tsx
│ │ │ │ ├── ProfileContent.tsx
│ │ │ │ ├── ProfileDescription.tsx
│ │ │ │ ├── FollowButton.tsx
│ │ │ │ ├── ProfileHeader.tsx
│ │ │ │ ├── ProfileEditForm.tsx
│ │ │ │ └── ProfileActivities.tsx
│ │ │ ├── users
│ │ │ │ ├── LoginForm.tsx
│ │ │ │ └── RegisterForm.tsx
│ │ │ └── home
│ │ │ │ └── HomePage.tsx
│ │ ├── setupTests.ts
│ │ ├── reportWebVitals.ts
│ │ ├── App.css
│ │ ├── index.tsx
│ │ └── logo.svg
│ ├── public
│ │ ├── robots.txt
│ │ ├── favicon.ico
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── assets
│ │ │ ├── logo.png
│ │ │ ├── user.png
│ │ │ ├── placeholder.png
│ │ │ └── categoryImages
│ │ │ │ ├── film.jpg
│ │ │ │ ├── food.jpg
│ │ │ │ ├── music.jpg
│ │ │ │ ├── culture.jpg
│ │ │ │ ├── drinks.jpg
│ │ │ │ └── travel.jpg
│ │ ├── manifest.json
│ │ └── index.html
│ ├── .env.development
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── README.md
│ └── package.json
├── API
│ ├── wwwroot
│ │ ├── robots.txt
│ │ ├── favicon.ico
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── assets
│ │ │ ├── logo.png
│ │ │ ├── user.png
│ │ │ ├── placeholder.png
│ │ │ └── categoryImages
│ │ │ │ ├── drinks.jpg
│ │ │ │ ├── film.jpg
│ │ │ │ ├── food.jpg
│ │ │ │ ├── music.jpg
│ │ │ │ ├── travel.jpg
│ │ │ │ └── culture.jpg
│ │ ├── static
│ │ │ ├── media
│ │ │ │ ├── flags.9c74e172.png
│ │ │ │ ├── icons.8e3c7f55.eot
│ │ │ │ ├── icons.b87b9ba5.ttf
│ │ │ │ ├── icons.faff9214.woff
│ │ │ │ ├── icons.0ab54153.woff2
│ │ │ │ ├── brand-icons.13db00b7.eot
│ │ │ │ ├── brand-icons.c5ebe0b3.ttf
│ │ │ │ ├── brand-icons.a046592b.woff
│ │ │ │ ├── brand-icons.e8c322de.woff2
│ │ │ │ ├── outline-icons.701ae6ab.eot
│ │ │ │ ├── outline-icons.ad97afd3.ttf
│ │ │ │ ├── outline-icons.cd6c777f.woff2
│ │ │ │ └── outline-icons.ef60a4f6.woff
│ │ │ ├── css
│ │ │ │ ├── main.88462492.chunk.css
│ │ │ │ └── main.88462492.chunk.css.map
│ │ │ └── js
│ │ │ │ └── runtime~main.a8a9905a.js
│ │ ├── manifest.json
│ │ ├── asset-manifest.json
│ │ ├── service-worker.js
│ │ ├── index.html
│ │ ├── precache-manifest.6a292ab25a73b8d93b860646affa8d63.js
│ │ └── precache-manifest.aa7497f6b9965e8bb998a85b893d1346.js
│ ├── DTOs
│ │ ├── LoginDTO.cs
│ │ ├── UserDto.cs
│ │ └── RegisterDto.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.json
│ ├── appsettings.Development.json
│ ├── Controllers
│ │ ├── FallbackController.cs
│ │ ├── BuggyController.cs
│ │ ├── PhotosController.cs
│ │ ├── FollowController.cs
│ │ ├── ProfilesController.cs
│ │ ├── WeatherForecastController.cs
│ │ ├── BaseApiController.cs
│ │ └── ActivitiesController.cs
│ ├── obj
│ │ ├── API.csproj.nuget.g.targets
│ │ └── API.csproj.nuget.g.props
│ ├── Extensions
│ │ ├── HttpExtensions.cs
│ │ └── IdentityServiceExtensions.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── SignalR
│ │ └── ChatHub.cs
│ ├── API.csproj
│ ├── Services
│ │ └── TokenService.cs
│ ├── Middleware
│ │ └── ExceptionMiddleware.cs
│ └── Program.cs
├── Domain
│ ├── obj
│ │ ├── Domain.csproj.nuget.g.targets
│ │ ├── Domain.csproj.nuget.g.props
│ │ ├── Domain.csproj.nuget.dgspec.json
│ │ └── project.nuget.cache
│ ├── Photo.cs
│ ├── Domain.csproj
│ ├── Comment.cs
│ ├── UserFollowing.cs
│ ├── ActivityAttendee.cs
│ ├── AppUser.cs
│ └── Activity.cs
├── Application
│ ├── obj
│ │ ├── Application.csproj.nuget.g.targets
│ │ └── Application.csproj.nuget.g.props
│ ├── Interfaces
│ │ ├── IUserAccessor.cs
│ │ └── IPhotoAccessor.cs
│ ├── Photos
│ │ ├── PhotoUploadResult.cs
│ │ ├── SetMain.cs
│ │ ├── Add.cs
│ │ └── Delete.cs
│ ├── Activities
│ │ ├── ActivityParams.cs
│ │ ├── AttendeeDto.cs
│ │ ├── ActivityValidator.cs
│ │ ├── ActivityDto.cs
│ │ ├── Delete.cs
│ │ ├── Details.cs
│ │ ├── Edit.cs
│ │ ├── Create.cs
│ │ ├── List.cs
│ │ └── UpdateAttendance.cs
│ ├── Comments
│ │ ├── CommentDto.cs
│ │ ├── List.cs
│ │ └── Create.cs
│ ├── Profiles
│ │ ├── UserActivityDto.cs
│ │ ├── Profile.cs
│ │ ├── Details.cs
│ │ ├── Edit.cs
│ │ └── ListActivities.cs
│ ├── Core
│ │ ├── PagingParams.cs
│ │ ├── Result.cs
│ │ ├── AppException.cs
│ │ ├── PagedList.cs
│ │ └── MappinngProfiles.cs
│ ├── Application.csproj
│ └── Followers
│ │ ├── FollowToggle.cs
│ │ └── List.cs
├── Persistence
│ ├── obj
│ │ ├── Persistence.csproj.nuget.g.targets
│ │ ├── Persistence.csproj.nuget.g.props
│ │ └── project.nuget.cache
│ ├── Persistence.csproj
│ └── DataContext.cs
├── Infrastructure
│ ├── Photos
│ │ ├── CloudinarySettings.cs
│ │ └── PhotoAccessor.cs
│ ├── Infrastructure.csproj
│ └── Security
│ │ ├── UserAccessor.cs
│ │ └── IsHostRequirement.cs
└── Reactivities.sln
└── README.md
/Reactivities/client-app/.env.production:
--------------------------------------------------------------------------------
1 | REACT_APP_API_URL=/api
2 | REACT_APP_CHAT_URL=/chat
--------------------------------------------------------------------------------
/Reactivities/client-app/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/Reactivities/client-app/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/logo192.png
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/logo512.png
--------------------------------------------------------------------------------
/Reactivities/client-app/.env.development:
--------------------------------------------------------------------------------
1 | REACT_APP_API_URL=https://localhost:7246/api
2 | REACT_APP_CHAT_URL=https://localhost:7246/chat
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/assets/logo.png
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/assets/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/assets/user.png
--------------------------------------------------------------------------------
/Reactivities/client-app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/favicon.ico
--------------------------------------------------------------------------------
/Reactivities/client-app/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/logo192.png
--------------------------------------------------------------------------------
/Reactivities/client-app/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/logo512.png
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/assets/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/assets/placeholder.png
--------------------------------------------------------------------------------
/Reactivities/client-app/public/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/assets/logo.png
--------------------------------------------------------------------------------
/Reactivities/client-app/public/assets/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/assets/user.png
--------------------------------------------------------------------------------
/Reactivities/client-app/public/assets/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/assets/placeholder.png
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/assets/categoryImages/drinks.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/assets/categoryImages/drinks.jpg
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/assets/categoryImages/film.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/assets/categoryImages/film.jpg
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/assets/categoryImages/food.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/assets/categoryImages/food.jpg
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/assets/categoryImages/music.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/assets/categoryImages/music.jpg
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/assets/categoryImages/travel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/assets/categoryImages/travel.jpg
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/flags.9c74e172.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/flags.9c74e172.png
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/icons.8e3c7f55.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/icons.8e3c7f55.eot
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/icons.b87b9ba5.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/icons.b87b9ba5.ttf
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/icons.faff9214.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/icons.faff9214.woff
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/model/serverError.ts:
--------------------------------------------------------------------------------
1 |
2 | export interface ServerError {
3 | statusCode: number;
4 | message: string;
5 | details: string;
6 | }
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/assets/categoryImages/culture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/assets/categoryImages/culture.jpg
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/icons.0ab54153.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/icons.0ab54153.woff2
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/brand-icons.13db00b7.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/brand-icons.13db00b7.eot
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/brand-icons.c5ebe0b3.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/brand-icons.c5ebe0b3.ttf
--------------------------------------------------------------------------------
/Reactivities/client-app/public/assets/categoryImages/film.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/assets/categoryImages/film.jpg
--------------------------------------------------------------------------------
/Reactivities/client-app/public/assets/categoryImages/food.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/assets/categoryImages/food.jpg
--------------------------------------------------------------------------------
/Reactivities/client-app/public/assets/categoryImages/music.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/assets/categoryImages/music.jpg
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/brand-icons.a046592b.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/brand-icons.a046592b.woff
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/brand-icons.e8c322de.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/brand-icons.e8c322de.woff2
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/outline-icons.701ae6ab.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/outline-icons.701ae6ab.eot
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/outline-icons.ad97afd3.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/outline-icons.ad97afd3.ttf
--------------------------------------------------------------------------------
/Reactivities/client-app/public/assets/categoryImages/culture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/assets/categoryImages/culture.jpg
--------------------------------------------------------------------------------
/Reactivities/client-app/public/assets/categoryImages/drinks.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/assets/categoryImages/drinks.jpg
--------------------------------------------------------------------------------
/Reactivities/client-app/public/assets/categoryImages/travel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/client-app/public/assets/categoryImages/travel.jpg
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/outline-icons.cd6c777f.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/outline-icons.cd6c777f.woff2
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/media/outline-icons.ef60a4f6.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dragon0513/react-dotnet/HEAD/Reactivities/API/wwwroot/static/media/outline-icons.ef60a4f6.woff
--------------------------------------------------------------------------------
/Reactivities/Domain/obj/Domain.csproj.nuget.g.targets:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Reactivities/API/DTOs/LoginDTO.cs:
--------------------------------------------------------------------------------
1 | namespace API.DTOs
2 | {
3 | public class LoginDTO
4 | {
5 | public string Email { get; set; }
6 | public string Password { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/Reactivities/Application/obj/Application.csproj.nuget.g.targets:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Reactivities/Persistence/obj/Persistence.csproj.nuget.g.targets:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/activities/details/ActivityDetailed.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function ActivityDetailed() {
4 | return (
5 |
Detailed
6 | )
7 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/model/comment.ts:
--------------------------------------------------------------------------------
1 | export interface ChatComment {
2 | id: number;
3 | createdAt: string;
4 | body: string;
5 | username: string;
6 | displayName: string;
7 | image: string;
8 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/Reactivities/API/DTOs/UserDto.cs:
--------------------------------------------------------------------------------
1 | namespace API.DTOs
2 | {
3 | public class UserDto
4 | {
5 | public string DisplayName { get; set; }
6 | public string Token { get; set; }
7 | public string Username { get; set; }
8 | public string Image { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/model/user.ts:
--------------------------------------------------------------------------------
1 | export interface User {
2 | username: string;
3 | displayName: string;
4 | token: string;
5 | image?: string;
6 | }
7 |
8 | export interface UserFormValues {
9 | email: string;
10 | password: string;
11 | displayName?: string;
12 | username?: string;
13 | }
--------------------------------------------------------------------------------
/Reactivities/Application/Interfaces/IUserAccessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Interfaces
8 | {
9 | public interface IUserAccessor
10 | {
11 | string GetUsername();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/common/options/categoryOptions.ts:
--------------------------------------------------------------------------------
1 | export const categoryOptions = [
2 | {text: 'Drinks', value: 'drinks'},
3 | {text: 'Culture', value: 'culture'},
4 | {text: 'Film', value: 'film'},
5 | {text: 'Food', value: 'food'},
6 | {text: 'Music', value: 'music'},
7 | {text: 'Travel', value: 'travel'},
8 | ]
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/layout/ScrollToTop.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | export default function ScrollToTop () {
5 | const pathName = useLocation();
6 |
7 | useEffect(() => {
8 | window.scroll(0, 0);
9 | }, [pathName]);
10 |
11 | return null;
12 | }
--------------------------------------------------------------------------------
/Reactivities/API/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | namespace API
2 | {
3 | public class WeatherForecast
4 | {
5 | public DateTime Date { get; set; }
6 |
7 | public int TemperatureC { get; set; }
8 |
9 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
10 |
11 | public string? Summary { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/Reactivities/API/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*",
9 | "Cloudinary": {
10 | "CloudName": "dvbaicnsy",
11 | "ApiKey": "653932124153488",
12 | "ApiSecret": "9cwxO2jxqtR-rXdpyGm_4SvanE4"
13 | }
14 |
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Reactivities/Domain/Photo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Domain
8 | {
9 | public class Photo
10 | {
11 | public string Id { get; set; }
12 | public string Url { get; set; }
13 | public bool IsMain { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Reactivities/API/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost; Port=5432; User Id=admin; Password=secret; Database=reactivities"
4 | },
5 | "TokenKey": "super secret key",
6 | "Logging": {
7 | "LogLevel": {
8 | "Default": "Information",
9 | "Microsoft.AspNetCore": "Warning"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Reactivities/Application/Photos/PhotoUploadResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Photos
8 | {
9 | public class PhotoUploadResult
10 | {
11 | public string PublicId { get; set; }
12 |
13 | public string Url { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Reactivities/Domain/Domain.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Reactivities/client-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Reactivities/Infrastructure/Photos/CloudinarySettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Infrastructure.Photos
8 | {
9 | public class CloudinarySettings
10 | {
11 | public string CloudName { get; set; }
12 |
13 | public string ApiKey { get; set; }
14 |
15 | public string ApiSecret { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/layout/LoadingComponent.tsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import {Dimmer, Loader} from 'semantic-ui-react';
4 |
5 | interface Props {
6 | inverted?: boolean;
7 | content?: string;
8 | }
9 |
10 | export default function LoadingComponent({inverted = true, content = 'Loading...'}: Props) {
11 | return (
12 |
13 |
14 |
15 | )
16 | }
--------------------------------------------------------------------------------
/Reactivities/Application/Interfaces/IPhotoAccessor.cs:
--------------------------------------------------------------------------------
1 | using Application.Photos;
2 | using Microsoft.AspNetCore.Http;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Interfaces
10 | {
11 | public interface IPhotoAccessor
12 | {
13 | Task AddPhoto(IFormFile file);
14 | Task DeletePhoto(string publicId);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Reactivities/API/Controllers/FallbackController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 | using Microsoft.AspNetCore.Mvc;
3 |
4 | namespace API.Controllers
5 | {
6 | public class FallbackController : Controller
7 | {
8 | [AllowAnonymous]
9 | public IActionResult Index()
10 | {
11 | return PhysicalFile(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot",
12 | "index.html"), "text/HTML");
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/ActivityParams.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Application.Activities
9 | {
10 | public class ActivityParams : PagingParams
11 | {
12 | public bool IsGoing { get; set; }
13 | public bool IsHost { get; set; }
14 | public DateTime StartDate { get; set; } = DateTime.UtcNow;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Reactivities/Domain/Comment.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Domain
8 | {
9 | public class Comment
10 | {
11 | public int Id { get; set; }
12 | public string Body { get; set; }
13 | public AppUser Author { get; set; }
14 | public Activity Activity { get; set; }
15 | public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Reactivities/Domain/UserFollowing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Domain
9 | {
10 | public class UserFollowing
11 | {
12 | public string ObserverId { get; set; }
13 | public AppUser Observer { get; set; }
14 |
15 | public string TargetId { get; set; }
16 | public AppUser Target { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Reactivities/Infrastructure/Infrastructure.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/Reactivities/Domain/ActivityAttendee.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Domain
8 | {
9 | public class ActivityAttendee
10 | {
11 | public string AppUserId { get; set; }
12 |
13 | public AppUser AppUser { get; set; }
14 |
15 | public Guid ActivityId { get; set; }
16 |
17 | public Activity Activity { get; set; }
18 |
19 | public bool IsHost { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Reactivities/Application/Comments/CommentDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Comments
8 | {
9 | public class CommentDto
10 | {
11 | public int Id { get; set; }
12 | public DateTime CreatedAt { get; set; }
13 | public string Body { get; set; }
14 | public string Username { get; set; }
15 | public string DisplayName { get; set; }
16 | public string Image { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Reactivities/API/obj/API.csproj.nuget.g.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Reactivities/API/DTOs/RegisterDto.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace API.DTOs
4 | {
5 | public class RegisterDto
6 | {
7 | [Required]
8 | public string DisplayName { get; set; }
9 |
10 | [Required]
11 | [EmailAddress]
12 | public string Email { get; set; }
13 |
14 | [Required]
15 | [RegularExpression("(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{4,8}$", ErrorMessage = "Password must be complex.")]
16 | public string Password { get; set; }
17 | public string Username { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/common/modals/ModalContainer.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react-lite";
2 | import React from "react";
3 | import { Modal } from "semantic-ui-react";
4 | import { useStore } from "../../stores/store";
5 |
6 | export default observer(function ModalContainer() {
7 | const {modalStore} = useStore();
8 |
9 | return (
10 |
11 |
12 | {modalStore.modal.body}
13 |
14 |
15 | )
16 | })
--------------------------------------------------------------------------------
/Reactivities/Application/Profiles/UserActivityDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 |
8 | namespace Application.Profiles
9 | {
10 | public class UserActivityDto
11 | {
12 | public Guid Id { get; set; }
13 | public string Title { get; set; }
14 | public string Category { get; set; }
15 | public DateTime Date { get; set; }
16 |
17 | [JsonIgnore]
18 | public string HostUsername { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Reactivities/Application/Core/PagingParams.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Core
8 | {
9 | public class PagingParams
10 | {
11 | private const int MaxPageSize = 50;
12 | public int PageNumber { get; set; } = 1;
13 |
14 |
15 | private int _pageSize = 10;
16 |
17 | public int PageSize
18 | {
19 | get => _pageSize;
20 | set => _pageSize = (value > MaxPageSize) ? MaxPageSize : value;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/errors/ValidationErrors.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Message } from 'semantic-ui-react';
3 |
4 | interface Props {
5 | errors: any;
6 | }
7 |
8 | export default function ValidationErrors({errors} : Props) {
9 | return (
10 |
11 | {errors && (
12 |
13 | {errors.map((err: any, i: any) => (
14 | {err}
15 | ))}
16 |
17 | )}
18 |
19 | )
20 | }
--------------------------------------------------------------------------------
/Reactivities/Application/Core/Result.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Core
8 | {
9 | public class Result
10 | {
11 | public bool IsSuccess { get; set; }
12 | public T Value { get; set; }
13 | public string Error { get; set; }
14 |
15 | public static Result Success(T value) => new Result { IsSuccess = true, Value = value };
16 | public static Result Failure(string error) => new Result { IsSuccess = false, Error = error };
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/Reactivities/client-app/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/css/main.88462492.chunk.css:
--------------------------------------------------------------------------------
1 | body{background-color:#eaeaea!important}.ui.inverted.top.fixed.menu{background-image:linear-gradient(135deg,#182a73,#218aae 64%,#20a7ac 89%)!important}.react-calendar{width:100%;border:0 rgba(34,36,38,.15)}.react-datepicker-wrapper{width:100%}.masthead{display:flex;align-items:center;background-image:linear-gradient(135deg,#182a73,#218aae 69%,#20a7ac 89%)!important;height:100vh}.masthead .ui.menu .ui.button,.ui.menu a.ui.inverted.button{margin-left:.5em}.masthead h1.ui.header{font-size:4em;font-weight:400}.masthead h2{font-size:1.7em;font-weight:400}
2 | /*# sourceMappingURL=main.88462492.chunk.css.map */
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/layout/PrivateRoute.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Redirect, Route, RouteComponentProps, RouteProps } from "react-router-dom";
3 | import { useStore } from "../stores/store";
4 |
5 | interface Props extends RouteProps {
6 | component: React.ComponentType> | React.ComponentType;
7 | }
8 |
9 | export default function PrivateRoute({component: Component, ...rest}: Props) {
10 | const {userStore: {isLoggedIn}} = useStore();
11 |
12 | return (
13 | isLoggedIn ? : } />
14 | )
15 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/stores/modalStore.ts:
--------------------------------------------------------------------------------
1 | import { makeAutoObservable } from "mobx";
2 |
3 | interface Modal {
4 | open: boolean;
5 | body: JSX.Element | null;
6 | }
7 |
8 | export default class ModalStore {
9 | modal: Modal = {
10 | open: false,
11 | body: null
12 | }
13 |
14 | constructor() {
15 | makeAutoObservable(this)
16 | }
17 |
18 | openModal = (content: JSX.Element) => {
19 | this.modal.open = true;
20 | this.modal.body = content;
21 | }
22 |
23 | closeModal = () => {
24 | this.modal.open = false;
25 | this.modal.body = null;
26 | }
27 | }
--------------------------------------------------------------------------------
/Reactivities/Application/Core/AppException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Core
8 | {
9 | public class AppException
10 | {
11 | public AppException(int statusCode, string message, string details = "")
12 | {
13 | StatusCode = statusCode;
14 | Message = message;
15 | Details = details;
16 | }
17 |
18 | public int StatusCode { get; set; }
19 | public string Message { get; set; }
20 | public string Details { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/AttendeeDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Activities
8 | {
9 | public class AttendeeDto
10 | {
11 | public string Username { get; set; }
12 |
13 | public string DisplayName { get; set; }
14 |
15 | public string Bio { get; set; }
16 |
17 | public string Image { get; set; }
18 |
19 |
20 |
21 | public bool Following { get; set; }
22 | public int FollowersCount { get; set; }
23 | public int FollowingsCount { get; set; }
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Reactivities/client-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "preserve"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/Reactivities/Domain/AppUser.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Domain
9 | {
10 | public class AppUser : IdentityUser
11 | {
12 | public string DisplayName { get; set; }
13 | public string Bio{ get; set; }
14 | public ICollection Activities { get; set; }
15 | public ICollection Photos { get; set; }
16 |
17 | public ICollection Followings { get; set; }
18 | public ICollection Followers { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Reactivities/Persistence/Persistence.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | net6.0
18 | enable
19 | enable
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/model/pagination.ts:
--------------------------------------------------------------------------------
1 | export interface Pagination {
2 | currentPage: number;
3 | itemsPerPage: number;
4 | totalItems: number;
5 | totalPages: number;
6 | }
7 |
8 | export class PaginatedResult {
9 | data: T;
10 | pagination: Pagination;
11 |
12 | constructor(data: T, pagination: Pagination) {
13 | this.data = data;
14 | this.pagination = pagination;
15 | }
16 | }
17 |
18 | export class PagingParams {
19 | pageNumber: number;
20 | pageSize: number;
21 |
22 | constructor(pageNumber = 1, pageSize = 2) {
23 | this.pageNumber = pageNumber;
24 | this.pageSize = pageSize;
25 | }
26 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/common/imageUpload/PhotoWidgetCropper.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Cropper} from 'react-cropper';
3 | import 'cropperjs/dist/cropper.css';
4 |
5 | interface Props {
6 | imagePreview: string;
7 | setCropper: (cropper: Cropper) => void;
8 | }
9 |
10 | export default function PhotoWidgetCropper({imagePreview, setCropper}: Props) {
11 | return (
12 | setCropper(cropper)}
15 | />
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/errors/NotFound.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import { Button, Header, Icon, Segment } from "semantic-ui-react";
4 |
5 | export default function NotFound () {
6 | return (
7 |
8 |
9 |
10 | Oops - we've looked everywhere and could not find this.
11 |
12 |
13 |
16 |
17 |
18 | )
19 | }
--------------------------------------------------------------------------------
/Reactivities/Application/Profiles/Profile.cs:
--------------------------------------------------------------------------------
1 | using Domain;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Application.Profiles
9 | {
10 | public class Profile
11 | {
12 | public string Username { get; set; }
13 |
14 | public string DisplayName { get; set; }
15 |
16 | public string Bio { get; set; }
17 |
18 | public string Image { get; set; }
19 |
20 |
21 | public bool Following { get; set; }
22 | public int FollowersCount { get; set; }
23 | public int FollowingsCount { get; set; }
24 |
25 |
26 | public ICollection Photos { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/ActivityValidator.cs:
--------------------------------------------------------------------------------
1 | using Domain;
2 | using FluentValidation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Activities
10 | {
11 | public class ActivityValidator : AbstractValidator
12 | {
13 | public ActivityValidator()
14 | {
15 | RuleFor(x => x.Title).NotEmpty();
16 | RuleFor(x => x.Description).NotEmpty();
17 | RuleFor(x => x.Date).NotEmpty();
18 | RuleFor(x => x.Category).NotEmpty();
19 | RuleFor(x => x.City).NotEmpty();
20 | RuleFor(x => x.Venue).NotEmpty();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Reactivities/API/Extensions/HttpExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 |
3 | namespace API.Extensions
4 | {
5 | public static class HttpExtensions
6 | {
7 | public static void AddPaginationHeader(this HttpResponse response, int currentPage,
8 | int itemsPerPage, int totalItems, int totalPages)
9 | {
10 | var paginationHeader = new
11 | {
12 | currentPage,
13 | itemsPerPage,
14 | totalItems,
15 | totalPages
16 | };
17 | response.Headers.Add("Pagination", JsonSerializer.Serialize(paginationHeader));
18 | response.Headers.Add("Access-Control-Expose-Headers", "Pagination");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/common/form/MyTextArea.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useField } from 'formik';
3 | import { Form, Label } from 'semantic-ui-react';
4 |
5 | interface Props {
6 | placeholder: string;
7 | name: string;
8 | rows: number;
9 | label?: string;
10 | }
11 |
12 | export default function MyTextArea(props: Props) {
13 | const[field, meta] = useField(props.name);
14 | return (
15 |
16 |
17 |
18 | {meta.touched && meta.error ? (
19 |
20 | ) : null}
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/common/form/MyTextInput.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useField } from 'formik';
3 | import { Form, Label } from 'semantic-ui-react';
4 |
5 | interface Props {
6 | placeholder: string;
7 | name: string;
8 | type?: string;
9 | label?: string;
10 | }
11 |
12 | export default function MyTextInput(props: Props) {
13 | const[field, meta] = useField(props.name);
14 | return (
15 |
16 |
17 |
18 | {meta.touched && meta.error ? (
19 |
20 | ) : null}
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/Reactivities/Application/Application.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/ActivityDto.cs:
--------------------------------------------------------------------------------
1 | using Application.Profiles;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Application.Activities
9 | {
10 | public class ActivityDto
11 | {
12 | public Guid Id { get; set; }
13 | public string Title { get; set; }
14 | public string Description { get; set; }
15 | public string Category { get; set; }
16 | public DateTime Date { get; set; }
17 | public string City { get; set; }
18 | public string Venue { get; set; }
19 | public string HostUsername { get; set; }
20 | public bool IsCancelled { get; set; }
21 | public ICollection Attendees { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Reactivities/Domain/Activity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Domain
8 | {
9 | public class Activity
10 | {
11 | public Guid Id { get; set; }
12 | public string Title { get; set; }
13 | public string Description { get; set; }
14 | public string Category { get; set; }
15 | public DateTime Date { get; set; }
16 | public string City { get; set; }
17 | public string Venue { get; set; }
18 | public bool IsCancelled { get; set; }
19 | public ICollection Attendees { get; set; } = new List();
20 | public ICollection Comments { get; set; } = new List();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Reactivities/Infrastructure/Security/UserAccessor.cs:
--------------------------------------------------------------------------------
1 | using Application.Interfaces;
2 | using Microsoft.AspNetCore.Http;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Security.Claims;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Infrastructure.Security
11 | {
12 | public class UserAccessor : IUserAccessor
13 | {
14 | private readonly IHttpContextAccessor httpContextAccessor;
15 |
16 | public UserAccessor(IHttpContextAccessor _httpContextAccessor)
17 | {
18 | httpContextAccessor = _httpContextAccessor;
19 | }
20 | public string GetUsername()
21 | {
22 | return httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.Name);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/model/profile.ts:
--------------------------------------------------------------------------------
1 | import { User } from "./user";
2 |
3 | export interface Profile {
4 | username: string;
5 | displayName: string;
6 | image?: string;
7 | bio?: string;
8 | followersCount: number;
9 | followingsCount: number;
10 | following: boolean;
11 | photos: Photo[]
12 | }
13 |
14 | export class Profile implements Profile {
15 | constructor(user: User) {
16 | this.username = user.username;
17 | this.displayName = user.displayName;
18 | this.image = user.image;
19 | }
20 | }
21 |
22 | export interface Photo {
23 | id: string;
24 | url: string;
25 | isMain: boolean;
26 | }
27 |
28 | export interface UserActivity {
29 | id: string;
30 | title: string;
31 | category: string;
32 | date: Date;
33 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/errors/ServerError.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { observer } from "mobx-react-lite";
3 | import { Container, Header, Segment } from "semantic-ui-react";
4 | import { useStore } from "../../app/stores/store";
5 |
6 | export default observer(function ServerError() {
7 | const {commonStore} = useStore();
8 |
9 | return(
10 |
11 |
12 |
13 | {commonStore.error?.details &&
14 |
15 |
16 | {commonStore.error.details}
17 |
18 | }
19 |
20 | )
21 | })
--------------------------------------------------------------------------------
/Reactivities/API/Controllers/BuggyController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.AspNetCore.Mvc;
3 |
4 | namespace API.Controllers
5 | {
6 | public class BuggyController : BaseApiController
7 | {
8 | [HttpGet("not-found")]
9 | public ActionResult GetNotFound()
10 | {
11 | return NotFound();
12 | }
13 |
14 | [HttpGet("bad-request")]
15 | public ActionResult GetBadRequest()
16 | {
17 | return BadRequest("This is a bad request");
18 | }
19 |
20 | [HttpGet("server-error")]
21 | public ActionResult GetServerError()
22 | {
23 | throw new Exception("This is a server error");
24 | }
25 |
26 | [HttpGet("unauthorised")]
27 | public ActionResult GetUnauthorised()
28 | {
29 | return Unauthorized();
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/Reactivities/API/Controllers/PhotosController.cs:
--------------------------------------------------------------------------------
1 | using Application.Photos;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 | namespace API.Controllers
6 | {
7 | public class PhotosController : BaseApiController
8 | {
9 | [HttpPost]
10 | public async Task Add([FromForm] Add.Command command)
11 | {
12 | return HandleResult(await Mediator.Send(command));
13 | }
14 |
15 | [HttpDelete("{id}")]
16 | public async Task Delete(string id)
17 | {
18 | return HandleResult(await Mediator.Send(new Delete.Command { Id = id }));
19 | }
20 |
21 | [HttpPost("{id}/setMain")]
22 | public async Task SetMain(string id)
23 | {
24 | return HandleResult(await Mediator.Send(new SetMain.Command { Id = id }));
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Reactivities/API/Controllers/FollowController.cs:
--------------------------------------------------------------------------------
1 | using Application.Followers;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 | namespace API.Controllers
6 | {
7 | [Route("api/[controller]")]
8 | [ApiController]
9 | public class FollowController : BaseApiController
10 | {
11 | [HttpPost("{username}")]
12 | public async Task Follow(string username)
13 | {
14 | return HandleResult(await Mediator.Send(new FollowToggle.Command { TargetUsername = username }));
15 | }
16 |
17 | [HttpGet("{username}")]
18 | public async Task GetFollowings(string username, string predicate)
19 | {
20 | return HandleResult(await Mediator.Send(new List.Query
21 | {
22 | Username = username,
23 | Predicate = predicate
24 | }));
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Reactivities/API/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:54041",
8 | "sslPort": 44381
9 | }
10 | },
11 | "profiles": {
12 | "API": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "launchUrl": "swagger",
17 | "applicationUrl": "https://localhost:7246;http://localhost:5246",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | },
22 | "IIS Express": {
23 | "commandName": "IISExpress",
24 | "launchBrowser": true,
25 | "launchUrl": "swagger",
26 | "environmentVariables": {
27 | "ASPNETCORE_ENVIRONMENT": "Development"
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/activities/dashboard/ActivityList.tsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import { Header } from "semantic-ui-react";
3 | import { useStore } from "../../../app/stores/store";
4 | import { observer } from "mobx-react-lite";
5 | import ActivityListItem from "./ActivityListItem";
6 |
7 |
8 | export default observer(function ActivityList() {
9 | const {activityStore} = useStore();
10 | const {groupedActivities} = activityStore;
11 |
12 | return (
13 | <>
14 | {groupedActivities.map(([group, activities]) =>
15 |
16 |
19 | {activities.map(activity => (
20 |
21 | ))}
22 |
23 | )}
24 | >
25 |
26 | )
27 |
28 | })
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/common/form/MyDateInput.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useField } from 'formik';
3 | import { Form, Label } from 'semantic-ui-react';
4 | import DatePicker, { ReactDatePickerProps } from 'react-datepicker';
5 |
6 | interface Props {
7 | placeholder: string;
8 | name: string;
9 | label?: string;
10 | }
11 |
12 | export default function MyDateInput(props: Partial) {
13 | const[field, meta, helpers] = useField(props.name!);
14 | return (
15 |
16 | helpers.setValue(value)}
21 | />
22 | {meta.touched && meta.error ? (
23 |
24 | ) : null}
25 |
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/stores/store.ts:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react';
2 | import ActivityStore from './activityStore';
3 | import CommentStore from './commentStore';
4 | import CommonStore from './commonStore';
5 | import ModalStore from './modalStore';
6 | import ProfileStore from './profileStore';
7 | import UserStore from './userStore';
8 |
9 | interface Store {
10 | activityStore: ActivityStore;
11 | commonStore: CommonStore;
12 | userStore: UserStore;
13 | modalStore: ModalStore;
14 | profileStore: ProfileStore;
15 | commentStore: CommentStore
16 | }
17 |
18 | export const store: Store = {
19 | activityStore: new ActivityStore(),
20 | commonStore: new CommonStore(),
21 | userStore: new UserStore(),
22 | modalStore: new ModalStore(),
23 | profileStore: new ProfileStore(),
24 | commentStore: new CommentStore()
25 | }
26 |
27 | export const StoreContext = createContext(store);
28 |
29 | export function useStore() {
30 | return useContext(StoreContext);
31 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/stores/commonStore.ts:
--------------------------------------------------------------------------------
1 | import { makeAutoObservable, reaction } from "mobx";
2 | import { ServerError } from "../model/serverError";
3 |
4 |
5 | export default class CommonStore {
6 | error : ServerError | null = null;
7 | token: string | null = window.localStorage.getItem('jwt');
8 | appLoaded = false;
9 |
10 | constructor() {
11 | makeAutoObservable(this);
12 |
13 | reaction(
14 | () => this.token,
15 | token => {
16 | if(token) {
17 | window.localStorage.setItem('jwt', token)
18 | } else {
19 | window.localStorage.removeItem('jwt');
20 | }
21 | }
22 | )
23 | }
24 |
25 | setServerError = (error: ServerError) => {
26 | this.error = error;
27 | }
28 |
29 | setToken = (token: string | null) => {
30 | this.token = token;
31 | }
32 |
33 | setAppLoaded = () => {
34 | this.appLoaded = true;
35 | }
36 | }
--------------------------------------------------------------------------------
/Reactivities/API/Controllers/ProfilesController.cs:
--------------------------------------------------------------------------------
1 |
2 | using Application.Profiles;
3 | using MediatR;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace API.Controllers
8 | {
9 | public class ProfilesController : BaseApiController
10 | {
11 | [HttpGet("{username}")]
12 | public async Task GetProfile(string username)
13 | {
14 | return HandleResult(await Mediator.Send(new Details.Query { Username = username }));
15 | }
16 |
17 | [HttpPut]
18 | public async Task> Edit(Edit.Command command)
19 | {
20 | return await Mediator.Send(command);
21 | }
22 |
23 | [HttpGet("{username}/activities")]
24 | public async Task GetUserActivities(string username,
25 | string predicate)
26 | {
27 | return HandleResult(await Mediator.Send(new ListActivities.Query { Username = username, Predicate = predicate }));
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/layout/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #EAEAEA !important;
3 | }
4 |
5 | .ui.inverted.top.fixed.menu {
6 | background-image: linear-gradient(135deg, rgb(24,42,115) 0%, rgb(33, 138, 174) 64%, rgb(32,167,172) 89%) !important;
7 | }
8 |
9 | .react-calendar {
10 | width: 100%;
11 | border: 0 1px 2px 0 rgba(34, 36, 38, .15);
12 | }
13 |
14 | .react-datepicker-wrapper {
15 | width: 100%;
16 | }
17 |
18 |
19 | /*home page styles*/
20 |
21 | .masthead {
22 | display: flex;
23 | align-items: center;
24 | background-image: linear-gradient(
25 | 135deg,
26 | rgb(24, 42, 115) 0%,
27 | rgb(33, 138, 174) 69%,
28 | rgb(32, 167, 172) 89%
29 | ) !important;
30 | height: 100vh;
31 | }
32 |
33 | .masthead .ui.menu .ui.button,
34 | .ui.menu a.ui.inverted.button {
35 | margin-left: 0.5em;
36 | }
37 |
38 | .masthead h1.ui.header {
39 | font-size: 4em;
40 | font-weight: normal;
41 | }
42 |
43 | .masthead h2 {
44 | font-size: 1.7em;
45 | font-weight: normal;
46 | }
47 |
48 | /*end home page styles*/
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | "main.css": "/static/css/main.88462492.chunk.css",
4 | "main.js": "/static/js/main.7932bfcb.chunk.js",
5 | "main.js.map": "/static/js/main.7932bfcb.chunk.js.map",
6 | "runtime~main.js": "/static/js/runtime~main.a8a9905a.js",
7 | "runtime~main.js.map": "/static/js/runtime~main.a8a9905a.js.map",
8 | "static/css/2.98ec9736.chunk.css": "/static/css/2.98ec9736.chunk.css",
9 | "static/js/2.a96d4760.chunk.js": "/static/js/2.a96d4760.chunk.js",
10 | "static/js/2.a96d4760.chunk.js.map": "/static/js/2.a96d4760.chunk.js.map",
11 | "index.html": "/index.html",
12 | "precache-manifest.aa7497f6b9965e8bb998a85b893d1346.js": "/precache-manifest.aa7497f6b9965e8bb998a85b893d1346.js",
13 | "service-worker.js": "/service-worker.js",
14 | "static/css/2.98ec9736.chunk.css.map": "/static/css/2.98ec9736.chunk.css.map",
15 | "static/css/main.88462492.chunk.css.map": "/static/css/main.88462492.chunk.css.map",
16 | "static/media/semantic.min.css": "/static/media/outline-icons.ef60a4f6.woff"
17 | }
18 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/common/form/MySelectInput.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useField } from 'formik';
3 | import { Label, Select, Form } from 'semantic-ui-react';
4 |
5 | interface Props {
6 | placeholder: string;
7 | name: string;
8 | options: any;
9 | label?: string;
10 | }
11 |
12 | export default function MySelectInput(props: Props) {
13 | const [field, meta, helpers] = useField(props.name);
14 | return (
15 |
16 |
17 |
29 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/Reactivities/API/SignalR/ChatHub.cs:
--------------------------------------------------------------------------------
1 | using Application.Comments;
2 | using MediatR;
3 | using Microsoft.AspNetCore.SignalR;
4 |
5 | namespace API.SignalR
6 | {
7 | public class ChatHub : Hub
8 | {
9 | private readonly IMediator mediator;
10 |
11 | public ChatHub(IMediator _mediator)
12 | {
13 | mediator = _mediator;
14 | }
15 |
16 | public async Task SendComment(Create.Command command)
17 | {
18 | var comment = await mediator.Send(command);
19 |
20 | await Clients.Group(command.ActivityId.ToString())
21 | .SendAsync("ReceiveComment", comment.Value);
22 | }
23 |
24 | public override async Task OnConnectedAsync()
25 | {
26 | var httpContext = Context.GetHttpContext();
27 | var activityId = httpContext.Request.Query["activityId"];
28 | await Groups.AddToGroupAsync(Context.ConnectionId, activityId);
29 | var result = await mediator.Send(new List.Query { ActivityId = Guid.Parse(activityId)});
30 | await Clients.Caller.SendAsync("LoadComments", result.Value);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Reactivities/API/Controllers/WeatherForecastController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 |
3 | namespace API.Controllers
4 | {
5 | [ApiController]
6 | [Route("[controller]")]
7 | public class WeatherForecastController : ControllerBase
8 | {
9 | private static readonly string[] Summaries = new[]
10 | {
11 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
12 | };
13 |
14 | private readonly ILogger _logger;
15 |
16 | public WeatherForecastController(ILogger logger)
17 | {
18 | _logger = logger;
19 | }
20 |
21 | [HttpGet(Name = "GetWeatherForecast")]
22 | public IEnumerable Get()
23 | {
24 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast
25 | {
26 | Date = DateTime.Now.AddDays(index),
27 | TemperatureC = Random.Shared.Next(-20, 55),
28 | Summary = Summaries[Random.Shared.Next(Summaries.Length)]
29 | })
30 | .ToArray();
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/css/main.88462492.chunk.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["styles.css"],"names":[],"mappings":"AAAA,KACE,kCACF,CAEA,4BACE,kFACF,CAEA,gBACE,UAAW,CACX,2BACF,CAEA,0BACE,UACF,CAKA,UACE,YAAa,CACb,kBAAmB,CACnB,kFAKY,CACZ,YACF,CAEA,4DAEE,gBACF,CAEA,uBACE,aAAc,CACd,eACF,CAEA,aACE,eAAgB,CAChB,eACF","file":"main.88462492.chunk.css","sourcesContent":["body {\n background-color: #EAEAEA !important;\n}\n\n.ui.inverted.top.fixed.menu {\n background-image: linear-gradient(135deg, rgb(24,42,115) 0%, rgb(33, 138, 174) 64%, rgb(32,167,172) 89%) !important;\n}\n\n.react-calendar {\n width: 100%;\n border: 0 1px 2px 0 rgba(34, 36, 38, .15);\n}\n\n.react-datepicker-wrapper {\n width: 100%;\n}\n\n\n/*home page styles*/\n\n.masthead {\n display: flex;\n align-items: center;\n background-image: linear-gradient(\n 135deg,\n rgb(24, 42, 115) 0%,\n rgb(33, 138, 174) 69%,\n rgb(32, 167, 172) 89%\n ) !important;\n height: 100vh;\n}\n\n.masthead .ui.menu .ui.button,\n.ui.menu a.ui.inverted.button {\n margin-left: 0.5em;\n}\n\n.masthead h1.ui.header {\n font-size: 4em;\n font-weight: normal;\n}\n\n.masthead h2 {\n font-size: 1.7em;\n font-weight: normal;\n}\n\n/*end home page styles*/"]}
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/common/imageUpload/PhotoWidgetDropzone.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback} from 'react'
2 | import {useDropzone} from 'react-dropzone'
3 | import { Header, Icon } from 'semantic-ui-react';
4 |
5 | interface Props {
6 | setFiles: (files: any) => void;
7 | }
8 |
9 | export default function PhotoWidgetDropzone({setFiles}: Props) {
10 | const dzStyles = {
11 | border: 'dashed 3px #eee',
12 | borderColor: '#eee',
13 | borderRadius: '5px',
14 | paddingTop: '30px',
15 | textAlign: 'center' as 'center',
16 | height: 200
17 | }
18 |
19 | const dzActive = {
20 | borderColor: 'green'
21 | }
22 |
23 | const onDrop = useCallback(acceptedFiles => {
24 | setFiles(acceptedFiles.map((file: any) => Object.assign(file, {
25 | preview: URL.createObjectURL(file)
26 | })))
27 | }, [setFiles])
28 |
29 | const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
30 |
31 | return (
32 |
33 |
34 |
35 |
36 |
37 | )
38 | }
--------------------------------------------------------------------------------
/Reactivities/Application/Core/PagedList.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Application.Core
9 | {
10 | public class PagedList : List
11 | {
12 | public PagedList(IEnumerable items, int count, int pageNumber, int pageSize)
13 | {
14 | CurrentPage = pageNumber;
15 | TotalPages = (int)Math.Ceiling(count / (double)pageSize);
16 | PageSize = pageSize;
17 | TotalCount = count;
18 | AddRange(items);
19 | }
20 |
21 | public int CurrentPage { get; set; }
22 | public int TotalPages { get; set; }
23 | public int PageSize { get; set; }
24 | public int TotalCount { get; set; }
25 |
26 | public static async Task> CreateAsync(IQueryable source, int pageNumber, int pageSize)
27 | {
28 | var count = await source.CountAsync();
29 | var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
30 | return new PagedList(items, count, pageNumber, pageSize);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/profiles/ProfileCard.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react-lite";
2 | import React from "react";
3 | import { Link } from "react-router-dom";
4 | import {Card, Image, Icon} from 'semantic-ui-react';
5 | import { Profile } from "../../app/model/profile";
6 | import FollowButton from "./FollowButton";
7 |
8 | interface Props {
9 | profile: Profile
10 | }
11 |
12 | export default observer(function ProfileCard({profile}: Props) {
13 |
14 | function truncate(str: string | undefined) {
15 | if(str) {
16 | return str.length > 40 ? str.substring(0, 37) + '...' : str;
17 | }
18 | }
19 |
20 | return (
21 |
22 |
23 |
24 | {profile.displayName}
25 | {truncate(profile.bio)}
26 |
27 |
28 |
29 | {profile.followersCount} followers
30 |
31 |
32 |
33 | );
34 | });
--------------------------------------------------------------------------------
/Reactivities/client-app/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import 'semantic-ui-css/semantic.min.css';
4 | import 'react-calendar/dist/Calendar.css';
5 | import 'react-toastify/dist/ReactToastify.min.css';
6 | import 'react-datepicker/dist/react-datepicker.css';
7 | import './app/layout/styles.css';
8 | import App from './app/layout/App';
9 | import { store, StoreContext } from './app/stores/store';
10 | import { Router } from 'react-router-dom';
11 | import { createBrowserHistory } from 'history';
12 | import ScrollToTop from './app/layout/ScrollToTop';
13 |
14 | export const history = createBrowserHistory();
15 |
16 | ReactDOM.render(
17 |
18 |
19 |
20 |
21 |
22 | ,
23 | document.getElementById('root')
24 | );
25 |
26 |
27 |
28 | // function createBrowserHistory() {
29 | // throw new Error('Function not implemented.');
30 | // }
31 | // If you want to start measuring performance in your app, pass a function
32 | // to log results (for example: reportWebVitals(console.log))
33 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
34 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/profiles/ProfileFollowings.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from 'mobx-react-lite';
2 | import React from 'react';
3 | import { Card, Grid, Header, Tab } from 'semantic-ui-react';
4 | import { useStore } from '../../app/stores/store';
5 | import ProfileCard from './ProfileCard';
6 |
7 | export default observer(function ProfileFollowings() {
8 | const {profileStore} = useStore();
9 | const {profile, followings, activeTab} = profileStore;
10 |
11 | return(
12 | // loading={loadFollowings}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {followings.map(profile => (
22 |
23 | ))}
24 |
25 |
26 |
27 |
28 | )
29 | })
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/model/activity.ts:
--------------------------------------------------------------------------------
1 | import { Profile } from "./profile";
2 |
3 | export interface Activity {
4 | id: string;
5 | title: string;
6 | description: string;
7 | category: string;
8 | date: Date | null;
9 | city: string;
10 | venue: string;
11 | hostUsername: string;
12 | isCancelled: boolean;
13 | isGoing: boolean;
14 | isHost: boolean;
15 | host?: Profile;
16 | attendees: Profile[];
17 | }
18 |
19 | export class Activity implements Activity {
20 | constructor(init?: ActivityFormValues) {
21 | Object.assign(this, init);
22 | }
23 | }
24 |
25 | export class ActivityFormValues {
26 | id?: string = undefined;
27 | title: string = '';
28 | category: string = '';
29 | description: string = '';
30 | date: Date | null = null;
31 | city: string = '';
32 | venue: string = '';
33 |
34 | constructor(activity?: ActivityFormValues) {
35 | if(activity) {
36 | this.id = activity.id;
37 | this.title = activity.title;
38 | this.category = activity.category;
39 | this.description = activity.description;
40 | this.date = activity.date;
41 | this.venue = activity.venue;
42 | this.city = activity.city;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/profiles/ProfilePage.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react-lite";
2 | import React, { useEffect } from "react";
3 | import { useParams } from "react-router-dom";
4 | import { Grid } from "semantic-ui-react";
5 | import LoadingComponent from "../../app/layout/LoadingComponent";
6 | import { useStore } from "../../app/stores/store";
7 | import ProfileContent from "./ProfileContent";
8 | import ProfileHeader from "./ProfileHeader";
9 |
10 | export default observer(function ProfilePage() {
11 | const {username} = useParams<{username: string}>();
12 | const {profileStore} = useStore();
13 | const {loadingProfile, loadProfile, profile, setActiveTab} = profileStore;
14 |
15 | useEffect(() => {
16 | loadProfile(username);
17 | return () => {
18 | setActiveTab(0);
19 | }
20 | }, [loadProfile, username, setActiveTab])
21 |
22 | if (loadingProfile) return
23 |
24 | return (
25 |
26 |
27 | {profile &&
28 | }
29 |
30 | {profile &&
31 | }
32 |
33 |
34 | )
35 | })
--------------------------------------------------------------------------------
/Reactivities/API/API.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/Delete.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using MediatR;
3 | using Persistence;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Application.Activities
11 | {
12 | public class Delete
13 | {
14 | public class Command : IRequest>
15 | {
16 | public Guid Id { get; set; }
17 | }
18 |
19 | public class Handler : IRequestHandler>
20 | {
21 | private readonly DataContext context;
22 |
23 | public Handler(DataContext _context)
24 | {
25 | context = _context;
26 | }
27 |
28 | public async Task> Handle(Command request, CancellationToken cancellationToken)
29 | {
30 | var activity = await context.Activities.FindAsync(request.Id);
31 |
32 | //if(activity == null) return null;
33 |
34 | context.Remove(activity);
35 |
36 | var result = await context.SaveChangesAsync() > 0;
37 |
38 | if (!result) return Result.Failure("Failed to delete the activity");
39 |
40 | return Result.Success(Unit.Value);
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15 |
16 | importScripts(
17 | "/precache-manifest.aa7497f6b9965e8bb998a85b893d1346.js"
18 | );
19 |
20 | self.addEventListener('message', (event) => {
21 | if (event.data && event.data.type === 'SKIP_WAITING') {
22 | self.skipWaiting();
23 | }
24 | });
25 |
26 | workbox.core.clientsClaim();
27 |
28 | /**
29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30 | * requests for URLs in the manifest.
31 | * See https://goo.gl/S9QRab
32 | */
33 | self.__precacheManifest = [].concat(self.__precacheManifest || []);
34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35 |
36 | workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
37 |
38 | blacklist: [/^\/_/,/\/[^/]+\.[^/]+$/],
39 | });
40 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/profiles/ProfileContent.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react-lite";
2 | import React from "react";
3 | import { Tab } from "semantic-ui-react";
4 | import { Profile } from "../../app/model/profile";
5 | import ProfilePhotos from "./ProfilePhotos";
6 | import ProfileDescription from './ProfileDescription';
7 | import ProfileFollowings from "./ProfileFollowings";
8 | import { useStore } from "../../app/stores/store";
9 | import ProfileActivities from "./ProfileActivities";
10 |
11 | interface Props {
12 | profile: Profile
13 | }
14 |
15 | export default observer(function ProfileContent({profile}: Props) {
16 | const {profileStore} = useStore();
17 |
18 | const panes = [
19 | {menuItem: 'About', render: () => },
20 | {menuItem: 'Photos', render: () => },
21 | {menuItem: 'Events', render: () => },
22 | {menuItem: 'Followers', render: () => },
23 | {menuItem: 'Following', render: () => }
24 | ];
25 |
26 | return (
27 | profileStore.setActiveTab(data.activeIndex)}
32 | />
33 | )
34 | })
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/profiles/ProfileDescription.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Tab, Grid, Header, Button } from 'semantic-ui-react';
3 | import { observer } from 'mobx-react-lite';
4 | import { Profile } from '../../app/model/profile';
5 | import { useStore } from '../../app/stores/store';
6 | import ProfileEditForm from './ProfileEditForm';
7 |
8 | interface Props {
9 | profile: Profile
10 | }
11 |
12 | export default observer(function ProfileDescription({profile}: Props){
13 | const {profileStore: {isCurrentUser}} = useStore();
14 | const [editMode, setEditMode] = useState(false);
15 | return (
16 |
17 |
18 |
19 |
24 | {isCurrentUser && (
25 |
33 |
34 | {editMode ? (
35 |
36 | ) : (
37 | {profile!.bio}
38 | )}
39 |
40 |
41 |
42 | );
43 |
44 | });
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Reactivities/API/Services/TokenService.cs:
--------------------------------------------------------------------------------
1 | using Domain;
2 | using Microsoft.IdentityModel.Tokens;
3 | using System.IdentityModel.Tokens.Jwt;
4 | using System.Security.Claims;
5 | using System.Text;
6 |
7 | namespace API.Services
8 | {
9 | public class TokenService
10 | {
11 | private readonly IConfiguration config;
12 |
13 | public TokenService(IConfiguration _config)
14 | {
15 | config = _config;
16 | }
17 | public string CreateToken(AppUser user)
18 | {
19 | var claims = new List
20 | {
21 | new Claim(ClaimTypes.Name, user.UserName),
22 | new Claim(ClaimTypes.NameIdentifier, user.Id),
23 | new Claim(ClaimTypes.Email, user.Email),
24 | };
25 |
26 | var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenKey"]));
27 | var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
28 |
29 | var tokenDescriptor = new SecurityTokenDescriptor
30 | {
31 | Subject = new ClaimsIdentity(claims),
32 | Expires = DateTime.Now.AddDays(7),
33 | SigningCredentials = creds
34 | };
35 |
36 | var tokenHandler = new JwtSecurityTokenHandler();
37 | var token = tokenHandler.CreateToken(tokenDescriptor);
38 |
39 | return tokenHandler.WriteToken(token);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/activities/dashboard/ActivityFilters.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from 'mobx-react-lite';
2 | import React from 'react';
3 | import Calendar from 'react-calendar';
4 | import { Header, Menu } from 'semantic-ui-react';
5 | import { useStore } from '../../../app/stores/store';
6 |
7 | export default observer(function ActivityFilters() {
8 | const {activityStore: {predicate, setPredicate}} = useStore();
9 | return (
10 |
11 | <>
12 |
28 | setPredicate('startDate', date as Date)}
30 | value={predicate.get('startDate') || new Date()}
31 | />
32 | >
33 | )
34 | })
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/static/js/runtime~main.a8a9905a.js:
--------------------------------------------------------------------------------
1 | !function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c
24 |
25 |
27 |
28 |
29 |
35 |
36 | );
37 | })
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/activities/dashboard/AcivityListItemAttendee.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react-lite";
2 | import React from "react";
3 | import { Link } from "react-router-dom";
4 | import { Image, List, Popup } from "semantic-ui-react";
5 | import { Profile } from "../../../app/model/profile";
6 | import ProfileCard from "../../profiles/ProfileCard";
7 |
8 | interface Props {
9 | attendees: Profile[];
10 | }
11 |
12 | export default observer(function ActivityListItemAttendee({attendees}: Props) {
13 | const styles = {
14 | borderColor: 'orange',
15 | borderWidth: 3
16 | }
17 |
18 | return (
19 |
20 | {attendees.map(attendee => (
21 |
26 |
30 |
31 | }
32 | >
33 |
34 |
35 |
36 |
37 |
38 | ))}
39 |
40 | )
41 |
42 | })
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/users/LoginForm.tsx:
--------------------------------------------------------------------------------
1 | import { ErrorMessage, Formik } from "formik";
2 | import React from "react";
3 | import { Button, Form, Header, Label } from "semantic-ui-react";
4 | import MyTextInput from "../../app/common/form/MyTextInput";
5 | import { useStore } from '../../app/stores/store';
6 | import { observer } from 'mobx-react-lite';
7 |
8 | export default observer(function LoginForm() {
9 | const {userStore} = useStore();
10 | return (
11 | userStore.login(values).catch(error =>
14 | setErrors({error: 'Invalid email or password'}))}>
15 | {({handleSubmit, isSubmitting, errors}) => (
16 |
26 | )}
27 |
28 | )
29 | })
--------------------------------------------------------------------------------
/Reactivities/Application/Comments/List.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using AutoMapper;
3 | using AutoMapper.QueryableExtensions;
4 | using MediatR;
5 | using Microsoft.EntityFrameworkCore;
6 | using Persistence;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace Application.Comments
14 | {
15 | public class List
16 | {
17 | public class Query : IRequest>>
18 | {
19 | public Guid ActivityId { get; set; }
20 | }
21 |
22 |
23 | public class Handler : IRequestHandler>>
24 | {
25 | private readonly DataContext context;
26 | private readonly IMapper mapper;
27 |
28 | public Handler(DataContext _context, IMapper _mapper)
29 | {
30 | context = _context;
31 | mapper = _mapper;
32 | }
33 |
34 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
35 | {
36 | var comments = await context.Comments
37 | .Where(x => x.Activity.Id == request.ActivityId)
38 | .OrderByDescending(x => x.CreatedAt)
39 | .ProjectTo(mapper.ConfigurationProvider)
40 | .ToListAsync();
41 |
42 | return Result>.Success(comments);
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/profiles/ProfileHeader.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react-lite";
2 | import React from "react";
3 | import { Divider, Grid, Header, Item, Segment, Statistic } from "semantic-ui-react";
4 | import { Profile } from "../../app/model/profile";
5 | import FollowButton from "./FollowButton";
6 |
7 | interface Props {
8 | profile: Profile;
9 | }
10 |
11 | export default observer(function ProfileHeader({profile}: Props) {
12 | return (
13 |
14 |
15 |
16 |
17 | -
18 |
19 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | })
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/activities/dashboard/ActivityListItemPlaceholder.tsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { Segment, Button, Placeholder } from 'semantic-ui-react';
3 |
4 | export default function ActivityListItemPlaceholder() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/profiles/ProfileEditForm.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { observer } from 'mobx-react-lite';
3 | import { Form, Button, Header} from 'semantic-ui-react';
4 | import { ErrorMessage, Formik } from 'formik';
5 | import MyTextInput from '../../app/common/form/MyTextInput';
6 | import * as Yup from 'yup';
7 | import ValidationErrors from "../errors/ValidationErrors";
8 | import { useStore } from '../../app/stores/store';
9 |
10 | export default observer(function ProfileEditForm() {
11 | const {profileStore} = useStore();
12 |
13 | return (
14 | profileStore.updateProfile(values).catch(error =>
17 | setErrors({error}))}
18 | validationSchema={Yup.object({
19 | displayName: Yup.string().required(),
20 | bio: Yup.string().required(),
21 | })}
22 | >
23 | {({handleSubmit, isSubmitting, errors, isValid, dirty}) => (
24 |
33 | )}
34 |
35 | );
36 |
37 | });
38 |
--------------------------------------------------------------------------------
/Reactivities/API/Controllers/BaseApiController.cs:
--------------------------------------------------------------------------------
1 | using API.Extensions;
2 | using Application.Core;
3 | using MediatR;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.Extensions.DependencyInjection;
6 |
7 | namespace API.Controllers
8 | {
9 | [ApiController]
10 | [Route("api/[controller]")]
11 | public class BaseApiController : ControllerBase
12 | {
13 | private IMediator mediator;
14 |
15 | protected IMediator Mediator => mediator ??= HttpContext.RequestServices.GetService();
16 |
17 | protected ActionResult HandleResult(Result result)
18 | {
19 | if (result == null) return NotFound();
20 | if (result.IsSuccess && result.Value != null)
21 | return Ok(result.Value);
22 | if (result.IsSuccess && result.Value == null)
23 | return NotFound();
24 |
25 | return BadRequest(result.Error);
26 | }
27 |
28 | protected ActionResult HandlePagedResult(Result> result)
29 | {
30 | if (result == null) return NotFound();
31 | if (result.IsSuccess && result.Value != null)
32 | {
33 | Response.AddPaginationHeader(result.Value.CurrentPage, result.Value.PageSize,
34 | result.Value.TotalCount, result.Value.TotalPages);
35 |
36 | return Ok(result.Value);
37 | }
38 | if (result.IsSuccess && result.Value == null)
39 | return NotFound();
40 |
41 | return BadRequest(result.Error);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Reactivities/Application/Profiles/Details.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using AutoMapper;
4 | using AutoMapper.QueryableExtensions;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 | using Persistence;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 |
14 | namespace Application.Profiles
15 | {
16 | public class Details
17 | {
18 | public class Query : IRequest>
19 | {
20 | public string Username { get; set; }
21 | }
22 |
23 | public class Handler : IRequestHandler>
24 | {
25 | private readonly DataContext context;
26 | private readonly IMapper mapper;
27 | private readonly IUserAccessor userAccessor;
28 |
29 | public Handler(DataContext _context, IMapper _mapper, IUserAccessor _userAccessor)
30 | {
31 | context = _context;
32 | mapper = _mapper;
33 | userAccessor = _userAccessor;
34 | }
35 | public async Task> Handle(Query request, CancellationToken cancellationToken)
36 | {
37 | var user = await context.Users
38 | .ProjectTo(mapper.ConfigurationProvider, new { currentUsername = userAccessor.GetUsername() })
39 | .SingleOrDefaultAsync(x => x.Username == request.Username);
40 |
41 |
42 | return Result.Success(user);
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/activities/details/ActivityDetails.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { Grid } from 'semantic-ui-react';
3 | import { useStore } from '../../../app/stores/store';
4 | import LoadingComponent from '../../../app/layout/LoadingComponent';
5 | import { useParams } from 'react-router';
6 | import { observer } from 'mobx-react-lite';
7 | import ActivityDetailedInfo from './ActivityDetailedInfo';
8 | import ActivityDetailedChat from './ActivityDetailedChat';
9 | import ActivityDetailedHeader from './ActivityDetailedHeader';
10 | import ActivityDetailedSidebar from './ActivityDetailedSidebar';
11 |
12 |
13 | export default observer(function ActivityDetails() {
14 | const {activityStore} = useStore();
15 | const {selectedActivity: activity, loadActivity, loadingInitial, clearSelectedActivity} = activityStore;
16 | const {id} = useParams<{id: string}>();
17 |
18 | useEffect(() => {
19 | if(id) loadActivity(id);
20 | return () => clearSelectedActivity();
21 | }, [id, loadActivity, clearSelectedActivity])
22 |
23 | if(loadingInitial || !activity) return ;
24 |
25 | return (
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | )
37 | })
--------------------------------------------------------------------------------
/Reactivities/API/Middleware/ExceptionMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using System.Net;
3 | using System.Text.Json;
4 |
5 | namespace API.Middleware
6 | {
7 | public class ExceptionMiddleware
8 | {
9 | private readonly RequestDelegate next;
10 | private readonly ILogger logger;
11 | private readonly IHostEnvironment env;
12 |
13 | public ExceptionMiddleware(RequestDelegate next, ILogger logger, IHostEnvironment env)
14 | {
15 | this.next = next;
16 | this.logger = logger;
17 | this.env = env;
18 | }
19 |
20 | public async Task Invoke(HttpContext context)
21 | {
22 | try
23 | {
24 | await next(context);
25 | }
26 | catch (Exception ex)
27 | {
28 | logger.LogError(ex, ex.Message);
29 | context.Response.ContentType = "application/json";
30 | context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
31 |
32 | var response = env.IsDevelopment()
33 | ? new AppException(context.Response.StatusCode, ex.Message, ex.StackTrace?.ToString())
34 | : new AppException(context.Response.StatusCode, "Server Error");
35 |
36 | var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
37 |
38 | var json = JsonSerializer.Serialize(response, options);
39 |
40 | await context.Response.WriteAsync(json);
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/Details.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using AutoMapper;
4 | using AutoMapper.QueryableExtensions;
5 | using Domain;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 | using Persistence;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 | using System.Threading.Tasks;
14 |
15 | namespace Application.Activities
16 | {
17 | public class Details
18 | {
19 | public class Query : IRequest>
20 | {
21 | public Guid Id { get; set; }
22 | }
23 |
24 | public class Handler : IRequestHandler>
25 | {
26 | private readonly DataContext context;
27 | private readonly IMapper mapper;
28 | private readonly IUserAccessor userAccessor;
29 |
30 | public Handler(DataContext _context, IMapper _mapper, IUserAccessor _userAccessor)
31 | {
32 | context = _context;
33 | mapper = _mapper;
34 | userAccessor = _userAccessor;
35 | }
36 |
37 | public async Task> Handle(Query request, CancellationToken cancellationToken)
38 | {
39 | var activity = await context.Activities
40 | .ProjectTo(mapper.ConfigurationProvider, new { currentUsername = userAccessor.GetUsername()})
41 | .FirstOrDefaultAsync(x => x.Id == request.Id);
42 |
43 | return Result.Success(activity);
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/layout/NavBar.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from 'mobx-react-lite';
2 | import React from 'react';
3 | import { Link, NavLink } from 'react-router-dom';
4 | import { Button, Container, Dropdown, Menu, Image } from 'semantic-ui-react';
5 | import { useStore } from '../stores/store';
6 |
7 | export default observer(function NavBar() {
8 | const {userStore: {user, logout}} = useStore();
9 | return (
10 |
32 | )
33 | })
--------------------------------------------------------------------------------
/Reactivities/Domain/obj/Domain.csproj.nuget.g.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | True
5 | NuGet
6 | $(MSBuildThisFileDirectory)project.assets.json
7 | $(UserProfile)\.nuget\packages\
8 | C:\Users\Itdev\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages
9 | PackageReference
10 | 6.1.0
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Reactivities/Application/obj/Application.csproj.nuget.g.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | True
5 | NuGet
6 | $(MSBuildThisFileDirectory)project.assets.json
7 | $(UserProfile)\.nuget\packages\
8 | C:\Users\Itdev\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages
9 | PackageReference
10 | 6.1.0
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Reactivities/Persistence/obj/Persistence.csproj.nuget.g.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | True
5 | NuGet
6 | $(MSBuildThisFileDirectory)project.assets.json
7 | $(UserProfile)\.nuget\packages\
8 | C:\Users\Itdev\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages
9 | PackageReference
10 | 6.1.0
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/home/HomePage.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from 'mobx-react-lite';
2 | import React from 'react';
3 | import { Link } from 'react-router-dom';
4 | import { Container, Header, Segment, Image, Button } from 'semantic-ui-react';
5 | import { useStore } from '../../app/stores/store';
6 | import LoginForm from '../users/LoginForm';
7 | import RegisterForm from '../users/RegisterForm';
8 |
9 | export default observer(function HomePage() {
10 | const {userStore, modalStore} = useStore();
11 | return (
12 |
13 |
14 |
15 |
16 | Reactivities
17 |
18 | {userStore.isLoggedIn ? (
19 | <>
20 |
23 | >
24 | ) : (
25 | <>
26 |
29 |
30 |
33 | >
34 |
35 | )}
36 |
37 |
38 |
39 |
40 | )
41 | })
--------------------------------------------------------------------------------
/Reactivities/Application/Profiles/Edit.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Application.Interfaces;
5 | using FluentValidation;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 | using Persistence;
9 |
10 | namespace Application.Profiles
11 | {
12 | public class Edit
13 | {
14 | public class Command : IRequest
15 | {
16 | public string DisplayName { get; set; }
17 | public string Bio { get; set; }
18 | }
19 |
20 | public class CommandValidator : AbstractValidator
21 | {
22 | public CommandValidator()
23 | {
24 | RuleFor(x => x.DisplayName).NotEmpty();
25 | }
26 | }
27 |
28 | public class Handler : IRequestHandler
29 | {
30 | private readonly DataContext _context;
31 | private readonly IUserAccessor _userAccessor;
32 | public Handler(DataContext context, IUserAccessor userAccessor)
33 | {
34 | _userAccessor = userAccessor;
35 | _context = context;
36 | }
37 |
38 | public async Task Handle(Command request, CancellationToken cancellationToken)
39 | {
40 | var user = await _context.Users.SingleOrDefaultAsync(x => x.UserName == _userAccessor.GetUsername());
41 |
42 | user.DisplayName = request.DisplayName ?? user.DisplayName;
43 | user.Bio = request.Bio ?? user.Bio;
44 |
45 | var success = await _context.SaveChangesAsync() > 0;
46 |
47 | if (success) return Unit.Value;
48 |
49 | throw new Exception("Problem saving changes");
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/Edit.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using AutoMapper;
3 | using Domain;
4 | using FluentValidation;
5 | using MediatR;
6 | using Persistence;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace Application.Activities
14 | {
15 | public class Edit
16 | {
17 | public class Command : IRequest>
18 | {
19 | public Activity Activity { get; set; }
20 | }
21 |
22 | public class CommandValidator : AbstractValidator
23 | {
24 | public CommandValidator()
25 | {
26 | RuleFor(x => x.Activity).SetValidator(new ActivityValidator());
27 | }
28 | }
29 |
30 | public class Handler : IRequestHandler>
31 | {
32 | private readonly DataContext context;
33 | private readonly IMapper mapper;
34 |
35 | public Handler(DataContext _context, IMapper _mapper)
36 | {
37 | context = _context;
38 | mapper = _mapper;
39 | }
40 |
41 | public async Task> Handle(Command request, CancellationToken cancellationToken)
42 | {
43 | var activity = await context.Activities.FindAsync(request.Activity.Id);
44 |
45 | if (activity == null) return null;
46 |
47 | mapper.Map(request.Activity, activity);
48 |
49 | var result = await context.SaveChangesAsync() > 0;
50 |
51 | if(!result) return Result.Failure("Failed to update activity");
52 |
53 | return Result.Success(Unit.Value);
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/Create.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using Domain;
4 | using FluentValidation;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 | using Persistence;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 |
14 | namespace Application.Activities
15 | {
16 | public class Create
17 | {
18 | public class Command: IRequest>
19 | {
20 | public Activity Activity { get; set; }
21 | }
22 |
23 | public class CommandValidator : AbstractValidator
24 | {
25 | public CommandValidator()
26 | {
27 | RuleFor(x => x.Activity).SetValidator(new ActivityValidator());
28 | }
29 | }
30 |
31 | public class Handler: IRequestHandler>
32 | {
33 | private readonly DataContext context;
34 | private readonly IUserAccessor userAccessor;
35 |
36 | public Handler(DataContext _context, IUserAccessor _userAccessor)
37 | {
38 | context = _context;
39 | userAccessor = _userAccessor;
40 | }
41 |
42 | public async Task> Handle(Command request, CancellationToken cancellationToken)
43 | {
44 | var user = await context.Users.FirstOrDefaultAsync(x => x.UserName == userAccessor.GetUsername());
45 | context.Activities.Add(request.Activity);
46 | var result = await context.SaveChangesAsync() > 0;
47 |
48 | if (!result) return Result.Failure("Failed to create activity");
49 |
50 | return Result.Success(Unit.Value);
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Reactivities/Application/Photos/SetMain.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using MediatR;
4 | using Microsoft.EntityFrameworkCore;
5 | using Persistence;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace Application.Photos
13 | {
14 | public class SetMain
15 | {
16 | public class Command : IRequest>
17 | {
18 | public string Id { get; set; }
19 | }
20 |
21 | public class Handler : IRequestHandler>
22 | {
23 | private readonly DataContext context;
24 | private readonly IUserAccessor userAccessor;
25 |
26 | public Handler(DataContext _context, IUserAccessor _userAccessor)
27 | {
28 | context = _context;
29 | userAccessor = _userAccessor;
30 | }
31 | public async Task> Handle(Command request, CancellationToken cancellationToken)
32 | {
33 | var user = await context.Users.Include(p => p.Photos)
34 | .FirstOrDefaultAsync(x => x.UserName == userAccessor.GetUsername());
35 |
36 | if (user == null) return null;
37 |
38 | var photo = user.Photos.FirstOrDefault(x => x.Id == request.Id);
39 |
40 | if(photo == null) return null;
41 |
42 | var currentMain = user.Photos.FirstOrDefault(x => x.IsMain);
43 |
44 | if(currentMain != null) currentMain.IsMain = false;
45 |
46 | photo.IsMain = true;
47 |
48 | var success = await context.SaveChangesAsync() > 0; return Result.Success(Unit.Value);
49 |
50 | return Result.Failure("Problem setting main photo");
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Reactivities/Infrastructure/Security/IsHostRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.EntityFrameworkCore;
4 | using Persistence;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Security.Claims;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace Infrastructure.Security
13 | {
14 | public class IsHostRequirement : IAuthorizationRequirement
15 | {
16 | }
17 |
18 | public class IsHostRequirementHandler : AuthorizationHandler
19 | {
20 | private readonly DataContext dbContext;
21 | private readonly IHttpContextAccessor httpContextAccessor;
22 |
23 | public IsHostRequirementHandler(DataContext _dbContext, IHttpContextAccessor _httpContextAccessor)
24 | {
25 | dbContext = _dbContext;
26 | httpContextAccessor = _httpContextAccessor;
27 | }
28 | protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsHostRequirement requirement)
29 | {
30 | var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
31 |
32 | if(userId == null) return Task.CompletedTask; ;
33 |
34 | var activityid = Guid.Parse(httpContextAccessor.HttpContext?.Request.RouteValues
35 | .SingleOrDefault(x => x.Key == "id").Value?.ToString());
36 |
37 | var attendee = dbContext.ActivityAttendees
38 | .AsNoTracking()
39 | .SingleOrDefaultAsync(x => x.AppUserId == userId && x.ActivityId == activityid)
40 | .Result;
41 |
42 |
43 | if(attendee == null) return Task.CompletedTask;
44 |
45 | if (attendee.IsHost) context.Succeed(requirement);
46 |
47 | return Task.CompletedTask;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Reactivities/client-app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/stores/userStore.ts:
--------------------------------------------------------------------------------
1 | import { makeAutoObservable, runInAction } from "mobx";
2 | import { history } from "../..";
3 | import agent from "../api/agent";
4 | import { User, UserFormValues } from "../model/user";
5 | import { store } from "./store";
6 |
7 | export default class UserStore {
8 | user: User | null = null;
9 |
10 | constructor() {
11 | makeAutoObservable(this)
12 | }
13 |
14 | get isLoggedIn() {
15 | return !!this.user;
16 | }
17 |
18 | login = async (creads: UserFormValues) => {
19 | try {
20 | const user = await agent.Account.login(creads);
21 | store.commonStore.setToken(user.token);
22 | runInAction(() => this.user = user);
23 | history.push('/activities');
24 | store.modalStore.closeModal();
25 | } catch (error) {
26 | throw error;
27 | }
28 | }
29 |
30 | logout = () => {
31 | store.commonStore.setToken(null);
32 | window.localStorage.removeItem('jwt');
33 | this.user = null;
34 | history.push('/');
35 | }
36 |
37 | getUser = async () => {
38 | try {
39 | const user = await agent.Account.current();
40 | runInAction(() => this.user = user);
41 | } catch(error) {
42 | console.log(error);
43 | }
44 | }
45 |
46 | register = async (creds: UserFormValues) => {
47 | try {
48 | const user = await agent.Account.register(creds);
49 | store.commonStore.setToken(user.token);
50 | runInAction(() => this.user = user);
51 | history.push('/activities');
52 | store.modalStore.closeModal();
53 | } catch (error) {
54 | throw error;
55 | }
56 | }
57 |
58 | setImage = (image: string) => {
59 | if (this.user) this.user.image = image;
60 | }
61 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/activities/details/ActivityDetailedInfo.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from 'mobx-react-lite';
2 | import React from 'react'
3 | import {Segment, Grid, Icon} from 'semantic-ui-react'
4 | import { Activity } from '../../../app/model/activity';
5 | import {format} from 'date-fns';
6 |
7 | interface Props {
8 | activity: Activity
9 | }
10 |
11 | export default observer(function ActivityDetailedInfo({activity}: Props) {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {activity.description}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {format(activity.date!, 'dd MM yyyy h:mm aa')}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | {activity.venue}, {activity.city}
43 |
44 |
45 |
46 |
47 | )
48 | })
--------------------------------------------------------------------------------
/Reactivities/API/Controllers/ActivitiesController.cs:
--------------------------------------------------------------------------------
1 | using Application.Activities;
2 | using Application.Core;
3 | using Domain;
4 | using MediatR;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.EntityFrameworkCore;
8 | using Persistence;
9 |
10 | namespace API.Controllers
11 | {
12 | public class ActivitiesController : BaseApiController
13 | {
14 | [HttpGet]
15 | public async Task GetActivities([FromQuery]ActivityParams param)
16 | {
17 | return HandlePagedResult( await Mediator.Send(new List.Query { Params = param}));
18 | }
19 |
20 | [Authorize]
21 | [HttpGet("{id}")]
22 | public async Task GetActivity(Guid id)
23 | {
24 | return HandleResult(await Mediator.Send(new Details.Query { Id = id }));
25 | }
26 |
27 | [HttpPost]
28 | public async Task CreateActivity([FromBody]Activity activity)
29 | {
30 | return HandleResult(await Mediator.Send(new Create.Command { Activity = activity}));
31 | }
32 |
33 | [Authorize(Policy = "IsActivityHost")]
34 | [HttpPut("{id}")]
35 | public async Task EditActivity(Guid id, Activity activity)
36 | {
37 | activity.Id = id;
38 | return HandleResult(await Mediator.Send(new Edit.Command { Activity = activity }));
39 | }
40 |
41 | [Authorize(Policy = "IsActivityHost")]
42 | [HttpDelete("{id}")]
43 | public async Task DeleteActivity(Guid id)
44 | {
45 | return HandleResult(await Mediator.Send(new Delete.Command { Id = id}));
46 | }
47 |
48 | [HttpPost("{id}/attend")]
49 | public async Task Attend(Guid id)
50 | {
51 | return HandleResult(await Mediator.Send(new UpdateAttendance.Command { Id = id }));
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Reactivities/Application/Profiles/ListActivities.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using AutoMapper;
3 | using AutoMapper.QueryableExtensions;
4 | using MediatR;
5 | using Microsoft.EntityFrameworkCore;
6 | using Persistence;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace Application.Profiles
14 | {
15 | public class ListActivities
16 | {
17 | public class Query : IRequest>>
18 | {
19 | public string Username { get; set; }
20 | public string Predicate { get; set; }
21 | }
22 |
23 | public class Handler : IRequestHandler>>
24 | {
25 | private readonly DataContext context;
26 | private readonly IMapper mapper;
27 |
28 | public Handler(DataContext _context, IMapper _mapper)
29 | {
30 | context = _context;
31 | mapper = _mapper;
32 | }
33 |
34 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
35 | {
36 | var query = context.ActivityAttendees
37 | .Where(u => u.AppUser.UserName == request.Username)
38 | .OrderBy(a => a.Activity.Date)
39 | .ProjectTo(mapper.ConfigurationProvider)
40 | .AsQueryable();
41 |
42 | query = request.Predicate switch
43 | {
44 | "past" => query.Where(a => a.Date <= DateTime.Now),
45 | "hosting" => query.Where(a => a.HostUsername == request.Username),
46 | _ => query.Where(a => a.Date >= DateTime.Now)
47 | };
48 |
49 | var activities = await query.ToListAsync();
50 |
51 | return Result>.Success(activities);
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/users/RegisterForm.tsx:
--------------------------------------------------------------------------------
1 | import { ErrorMessage, Formik } from "formik";
2 | import React from "react";
3 | import { Button, Form, Header} from "semantic-ui-react";
4 | import MyTextInput from "../../app/common/form/MyTextInput";
5 | import { useStore } from '../../app/stores/store';
6 | import { observer } from 'mobx-react-lite';
7 | import * as Yup from 'yup';
8 | import ValidationErrors from "../errors/ValidationErrors";
9 |
10 | export default observer(function RegisterForm() {
11 | const {userStore} = useStore();
12 | return (
13 | userStore.register(values).catch(error =>
16 | setErrors({error}))}
17 | validationSchema={Yup.object({
18 | displayName: Yup.string().required(),
19 | username: Yup.string().required(),
20 | email: Yup.string().required().email(),
21 | password: Yup.string().required()
22 | })}
23 | >
24 | {({handleSubmit, isSubmitting, errors, isValid, dirty}) => (
25 |
36 | )}
37 |
38 | )
39 | })
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/index.html:
--------------------------------------------------------------------------------
1 | React App
--------------------------------------------------------------------------------
/Reactivities/Application/Photos/Add.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using Domain;
4 | using MediatR;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.EntityFrameworkCore;
7 | using Persistence;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 |
14 | namespace Application.Photos
15 | {
16 | public class Add
17 | {
18 | public class Command : IRequest>
19 | {
20 | public IFormFile File { get; set; }
21 | }
22 |
23 | public class Handler : IRequestHandler>
24 | {
25 | private readonly DataContext context;
26 | private readonly IPhotoAccessor photoAccessor;
27 | private readonly IUserAccessor userAccessor;
28 |
29 | public Handler(DataContext _context, IPhotoAccessor _photoAccessor, IUserAccessor _userAccessor)
30 | {
31 | context = _context;
32 | photoAccessor = _photoAccessor;
33 | userAccessor = _userAccessor;
34 | }
35 | public async Task> Handle(Command request, CancellationToken cancellationToken)
36 | {
37 | var user = await context.Users.Include(p => p.Photos)
38 | .FirstOrDefaultAsync(x => x.UserName == userAccessor.GetUsername());
39 |
40 | if (user == null) return null;
41 |
42 | var photoUploadResult = await photoAccessor.AddPhoto(request.File);
43 |
44 | var photo = new Photo
45 | {
46 | Url = photoUploadResult.Url,
47 | Id = photoUploadResult.PublicId
48 | };
49 |
50 | if (!user.Photos.Any(x => x.IsMain)) photo.IsMain = true;
51 |
52 | user.Photos.Add(photo);
53 |
54 | var result = await context.SaveChangesAsync() > 0;
55 |
56 | if (result) return Result.Success(photo);
57 |
58 | return Result.Failure("Problem adding Photo");
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/errors/TestError.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import {Button, Header, Segment} from "semantic-ui-react";
3 | import axios from 'axios';
4 | import ValidationErrors from './ValidationErrors';
5 |
6 | export default function TestErrors() {
7 | const baseUrl = 'process.env.REACT_APP_API_URL';
8 | const [errors, setErrors] = useState(null);
9 |
10 | function handleNotFound() {
11 | axios.get(baseUrl + 'buggy/not-found').catch(err => console.log(err.response));
12 | }
13 |
14 | function handleBadRequest() {
15 | axios.get(baseUrl + 'buggy/bad-request').catch(err => console.log(err.response));
16 | }
17 |
18 | function handleServerError() {
19 | axios.get(baseUrl + 'buggy/server-error').catch(err => console.log(err.response));
20 | }
21 |
22 | function handleUnauthorised() {
23 | axios.get(baseUrl + 'buggy/unauthorised').catch(err => console.log(err.response));
24 | }
25 |
26 | function handleBadGuid() {
27 | axios.get(baseUrl + 'activities/notaguid').catch(err => console.log(err));
28 | }
29 |
30 | function handleValidationError() {
31 | axios.post(baseUrl + 'activities', {}).catch(err => setErrors(err));
32 | }
33 |
34 | return (
35 | <>
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | {errors &&
48 |
49 | }
50 | >
51 | )
52 | }
--------------------------------------------------------------------------------
/Reactivities/Application/Photos/Delete.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using MediatR;
4 | using Microsoft.EntityFrameworkCore;
5 | using Persistence;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace Application.Photos
13 | {
14 | public class Delete
15 | {
16 | public class Command : IRequest>
17 | {
18 | public string Id { get; set; }
19 | }
20 |
21 | public class Handler : IRequestHandler>
22 | {
23 | private readonly DataContext context;
24 | private readonly IPhotoAccessor photoAccessor;
25 | private readonly IUserAccessor userAccessor;
26 |
27 | public Handler(DataContext _context, IPhotoAccessor _photoAccessor, IUserAccessor _userAccessor )
28 | {
29 | context = _context;
30 | photoAccessor = _photoAccessor;
31 | userAccessor = _userAccessor;
32 | }
33 | public async Task> Handle(Command request, CancellationToken cancellationToken)
34 | {
35 | var user = await context.Users.Include(p => p.Photos)
36 | .FirstOrDefaultAsync(x => x.UserName == userAccessor.GetUsername());
37 |
38 | if (user == null) return null;
39 |
40 | var photo = user.Photos.FirstOrDefault(x => x.Id == request.Id);
41 |
42 | if(photo == null) return null;
43 |
44 | if (photo.IsMain) return Result.Failure("You cannot delete your main photo.");
45 |
46 | var result = await photoAccessor.DeletePhoto(photo.Id);
47 |
48 | if (result == null) return Result.Failure("Problem deleting photo from Cloudinary");
49 |
50 | user.Photos.Remove(photo);
51 |
52 | var success = await context.SaveChangesAsync() > 0;
53 |
54 | if (success) return Result.Success(Unit.Value);
55 |
56 | return Result.Failure("Problem deleting photo from API");
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Reactivities/Infrastructure/Photos/PhotoAccessor.cs:
--------------------------------------------------------------------------------
1 | using Application.Interfaces;
2 | using Application.Photos;
3 | using CloudinaryDotNet;
4 | using CloudinaryDotNet.Actions;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.Extensions.Options;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace Infrastructure.Photos
14 | {
15 | public class PhotoAccessor : IPhotoAccessor
16 | {
17 | private readonly Cloudinary cloudinary;
18 | public PhotoAccessor(IOptions config)
19 | {
20 | var account = new Account(
21 | config.Value.CloudName,
22 | config.Value.ApiKey,
23 | config.Value.ApiSecret
24 | );
25 | cloudinary = new Cloudinary(account);
26 | }
27 | public async Task AddPhoto(IFormFile file)
28 | {
29 | if(file.Length > 0)
30 | {
31 | await using var stream = file.OpenReadStream();
32 | var uploadParams = new ImageUploadParams
33 | {
34 | File = new FileDescription(file.FileName, stream),
35 | Transformation = new Transformation().Height(500).Width(500).Crop("fill")
36 | };
37 |
38 | var uploadResult = await cloudinary.UploadAsync(uploadParams);
39 |
40 | if(uploadResult.Error != null)
41 | {
42 | throw new Exception(uploadResult.Error.Message);
43 | }
44 |
45 | return new PhotoUploadResult
46 | {
47 | PublicId = uploadResult.PublicId,
48 | Url = uploadResult.SecureUrl.ToString()
49 | };
50 | }
51 |
52 | return null;
53 | }
54 |
55 | public async Task DeletePhoto(string publicId)
56 | {
57 | var deleteParams = new DeletionParams(publicId);
58 | var result = await cloudinary.DestroyAsync(deleteParams);
59 | return result.Result == "ok" ? result.Result : null;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/common/imageUpload/PhotoUploadWidget.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Button, Grid, Header } from "semantic-ui-react";
3 | import PhotoWidgetCropper from "./PhotoWidgetCropper";
4 | import PhotoWidgetDropzone from "./PhotoWidgetDropzone";
5 |
6 | interface Props {
7 | loading: boolean;
8 | uploadPhoto: (file: Blob) => void;
9 | }
10 |
11 | export default function PhotoUploadWidget({loading, uploadPhoto}: Props) {
12 | const [files, setFiles] = useState([]);
13 | const [cropper, setCropper] = useState();
14 |
15 | function onCrop() {
16 | if(cropper) {
17 | cropper.getCroppedCanvas().toBlob(blob => uploadPhoto(blob!));
18 | }
19 | }
20 |
21 | useEffect(() => {
22 | return () => {
23 | files.forEach((file: any) => URL.revokeObjectURL(file.preview))
24 | }
25 | }, [files])
26 |
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | {files && files.length > 0 && (
37 |
38 | )}
39 |
40 |
41 |
42 |
43 | {files && files.length > 0 &&
44 | <>
45 |
46 |
47 |
48 |
50 | >}
51 |
52 |
53 | )
54 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/activities/dashboard/ActivityDashboard.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from 'mobx-react-lite';
2 | import React, { useEffect, useState } from 'react';
3 | import InfiniteScroll from 'react-infinite-scroller';
4 | import { Grid, Loader } from 'semantic-ui-react';
5 | import { PagingParams } from '../../../app/model/pagination';
6 | import { useStore } from '../../../app/stores/store';
7 | import ActivityFilters from './ActivityFilters';
8 | import ActivityList from './ActivityList';
9 | import ActivityListItemPlaceholder from './ActivityListItemPlaceholder';
10 |
11 | export default observer(function ActivityDashboard() {
12 | const {activityStore} = useStore();
13 | const {loadActivities, activityRegistry, setPagingParams, pagination} = activityStore;
14 | const [loadingNext, setLoadingNext] = useState(false);
15 |
16 | function handleGetNext() {
17 | setLoadingNext(true);
18 | setPagingParams(new PagingParams(pagination!.currentPage + 1))
19 | loadActivities().then(() => setLoadingNext(false));
20 | }
21 |
22 | useEffect(() => {
23 | if(activityRegistry.size <= 1) loadActivities();
24 | }, [activityRegistry.size, loadActivities])
25 |
26 |
27 |
28 | return (
29 |
30 |
31 | {activityStore.loadingInitial && !loadingNext ? (
32 | <>
33 |
34 |
35 | >
36 | ) : (
37 |
40 |
41 |
42 |
43 | )}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | )
53 | })
--------------------------------------------------------------------------------
/Reactivities/client-app/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/Reactivities/client-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@microsoft/signalr": "^6.0.8",
7 | "@types/cropperjs": "^1.1.5",
8 | "@types/jest": "24.0.15",
9 | "@types/node": "12.6.8",
10 | "@types/react": "16.8.23",
11 | "@types/react-calendar": "^3.1.2",
12 | "@types/react-datepicker": "^3.1.2",
13 | "@types/react-dom": "16.8.4",
14 | "@types/react-infinite-scroller": "^1.2.1",
15 | "@types/react-router-dom": "^5.3.3",
16 | "@types/react-widgets": "^4.3.3",
17 | "@types/revalidate": "^1.1.2",
18 | "@types/uuid": "^3.4.5",
19 | "axios": "^0.19.0",
20 | "date-fns": "^2.16.1",
21 | "final-form": "^4.18.2",
22 | "formik": "^2.2.6",
23 | "history": "^4.10.1",
24 | "mobx": "^6.6.1",
25 | "mobx-react-lite": "^3.4.0",
26 | "moment": "^2.29.4",
27 | "react": "^16.8.6",
28 | "react-calendar": "^3.2.1",
29 | "react-cropper": "^2.1.4",
30 | "react-datepicker": "^3.3.0",
31 | "react-dom": "^16.8.6",
32 | "react-dropzone": "^10.2.2",
33 | "react-final-form": "^6.3.0",
34 | "react-infinite-scroller": "^1.2.6",
35 | "react-router-dom": "^5.2.0",
36 | "react-scripts": "3.0.1",
37 | "react-toastify": "^5.3.2",
38 | "react-widgets": "^4.4.11",
39 | "react-widgets-date-fns": "^4.0.26",
40 | "revalidate": "^1.2.0",
41 | "semantic-ui-css": "^2.4.1",
42 | "semantic-ui-react": "^0.87.3",
43 | "typescript": "^3.9.7",
44 | "uuid": "^3.3.2",
45 | "yup": "^0.32.8"
46 | },
47 | "scripts": {
48 | "start": "react-scripts start",
49 | "build": "react-scripts build",
50 | "postbuild": "mv build ../API/wwwroot",
51 | "test": "react-scripts test",
52 | "eject": "react-scripts eject"
53 | },
54 | "eslintConfig": {
55 | "extends": "react-app"
56 | },
57 | "browserslist": {
58 | "production": [
59 | ">0.2%",
60 | "not dead",
61 | "not op_mini all"
62 | ],
63 | "development": [
64 | "last 1 chrome version",
65 | "last 1 firefox version",
66 | "last 1 safari version"
67 | ]
68 | },
69 | "devDependencies": {
70 | "@types/react-cropper": "^1.3.2",
71 | "@types/yup": "^0.29.11",
72 | "web-vitals": "^2.1.4"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Reactivities/Application/Followers/FollowToggle.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using Domain;
4 | using MediatR;
5 | using Microsoft.EntityFrameworkCore;
6 | using Persistence;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace Application.Followers
14 | {
15 | public class FollowToggle
16 | {
17 | public class Command : IRequest>
18 | {
19 | public string TargetUsername { get; set; }
20 | }
21 |
22 | public class Handler : IRequestHandler>
23 | {
24 | private readonly DataContext context;
25 | private readonly IUserAccessor userAccessor;
26 |
27 | public Handler(DataContext _context, IUserAccessor _userAccessor)
28 | {
29 | context = _context;
30 | userAccessor = _userAccessor;
31 | }
32 | public async Task> Handle(Command request, CancellationToken cancellationToken)
33 | {
34 | var observer = await context.Users.FirstOrDefaultAsync(x => x.UserName == userAccessor.GetUsername());
35 |
36 | var target = await context.Users.FirstOrDefaultAsync(x => x.UserName == request.TargetUsername);
37 |
38 | if (target == null) return null;
39 |
40 | var following = await context.UserFollowings.FindAsync(observer.Id, target.Id);
41 |
42 | if(following == null)
43 | {
44 | following = new UserFollowing
45 | {
46 | Observer = observer,
47 | Target = target
48 | };
49 | context.UserFollowings.Add(following);
50 | }
51 | else
52 | {
53 | context.UserFollowings.Remove(following);
54 | }
55 |
56 | var success = await context.SaveChangesAsync() > 0;
57 |
58 | if (success) return Result.Success(Unit.Value);
59 |
60 | return Result.Failure("Failed to update following");
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/activities/details/ActivityDetailedSidebar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Segment, List, Label, Item, Image } from 'semantic-ui-react'
3 | import { Link } from 'react-router-dom'
4 | import { observer } from 'mobx-react-lite'
5 | import { Activity } from '../../../app/model/activity'
6 |
7 | interface Props {
8 | activity: Activity;
9 | }
10 |
11 | export default observer(function ActivityDetailedSidebar ({activity: {attendees, host}} : Props) {
12 | if (!attendees) return null;
13 | return (
14 | <>
15 |
23 | {attendees.length} {attendees.length === 1 ? 'Person' : 'People'} going
24 |
25 |
26 |
27 | {attendees.map(attendee => (
28 | -
29 | {attendee.username === host?.username &&
30 | }
37 |
38 |
39 |
40 | {attendee.displayName}
41 |
42 | {
43 | attendee.following &&
44 | Following
45 | }
46 |
47 |
48 | ))}
49 |
50 |
51 | >
52 |
53 | )
54 | })
--------------------------------------------------------------------------------
/Reactivities/Persistence/DataContext.cs:
--------------------------------------------------------------------------------
1 | using Domain;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Microsoft.EntityFrameworkCore;
8 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
9 |
10 | namespace Persistence
11 | {
12 | public class DataContext : IdentityDbContext
13 | {
14 | public DataContext(DbContextOptions options) : base(options)
15 | {
16 | }
17 | public DbSet Activities { get; set; }
18 |
19 | public DbSet ActivityAttendees { get; set; }
20 |
21 | public DbSet Photos { get; set; }
22 | public DbSet Comments { get; set; }
23 |
24 | public DbSet UserFollowings { get; set; }
25 |
26 | protected override void OnModelCreating(ModelBuilder builder)
27 | {
28 | base.OnModelCreating(builder);
29 |
30 | builder.Entity(x => x.HasKey(aa => new { aa.AppUserId, aa.ActivityId }));
31 |
32 | builder.Entity()
33 | .HasOne(u => u.AppUser)
34 | .WithMany(a => a.Activities)
35 | .HasForeignKey(aa => aa.AppUserId);
36 |
37 |
38 | builder.Entity()
39 | .HasOne(u => u.Activity)
40 | .WithMany(a => a.Attendees)
41 | .HasForeignKey(aa => aa.ActivityId);
42 |
43 | builder.Entity()
44 | .HasOne(a => a.Activity)
45 | .WithMany(c => c.Comments)
46 | .OnDelete(DeleteBehavior.Cascade);
47 |
48 | builder.Entity(b =>
49 | {
50 | b.HasKey(k => new {k.ObserverId, k.TargetId });
51 |
52 | b.HasOne(o => o.Observer)
53 | .WithMany(f => f.Followings)
54 | .HasForeignKey(o => o.ObserverId)
55 | .OnDelete(DeleteBehavior.Cascade);
56 |
57 | b.HasOne(o => o.Target)
58 | .WithMany(f => f.Followers)
59 | .HasForeignKey(o => o.TargetId)
60 | .OnDelete(DeleteBehavior.Cascade);
61 | });
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/stores/commentStore.ts:
--------------------------------------------------------------------------------
1 | import { HubConnection, HubConnectionBuilder, LogLevel } from "@microsoft/signalr";
2 | import { makeAutoObservable, runInAction } from "mobx";
3 | import { ChatComment } from "../model/comment";
4 | import { store } from "./store";
5 |
6 | export default class CommentStore {
7 | comments: ChatComment[] = [];
8 | hubConnection: HubConnection | null = null;
9 |
10 | constructor() {
11 | makeAutoObservable(this);
12 | }
13 |
14 | createHubConnection = (activityId: string) => {
15 | if(store.activityStore.selectedActivity) {
16 | this.hubConnection = new HubConnectionBuilder()
17 | .withUrl(process.env.REACT_APP_CHAT_URL + '?activityId=' + activityId, {
18 | accessTokenFactory: () => store.userStore.user?.token!
19 | })
20 | .withAutomaticReconnect()
21 | .configureLogging(LogLevel.Information)
22 | .build();
23 |
24 | this.hubConnection?.start().catch(error => console.log('Error establishing the connection'));
25 |
26 | this.hubConnection?.on('LoadComments', (comments: ChatComment[]) => {
27 | runInAction(() => {
28 | comments.forEach(comment => {
29 | comment.createdAt = comment.createdAt;
30 | })
31 | this.comments = comments;
32 | });
33 | })
34 |
35 | this.hubConnection?.on('ReceiveComment', (comment: ChatComment) => {
36 | runInAction(() => {
37 | comment.createdAt = comment.createdAt;
38 | this.comments.unshift(comment)
39 | });
40 | })
41 | }
42 | }
43 |
44 | stopHubConnection = () => {
45 | this.hubConnection?.stop().catch(error => console.log('Error stopping connection: ', error));
46 | }
47 |
48 | clearComments = () => {
49 | this.comments = [];
50 | this.stopHubConnection();
51 | }
52 |
53 | addComment = async (values: any) => {
54 | values.activityId = store.activityStore.selectedActivity?.id;
55 | try {
56 | await this.hubConnection?.invoke('SendComment', values);
57 | } catch (error) {
58 | console.log(error);
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/List.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using AutoMapper;
4 | using AutoMapper.QueryableExtensions;
5 | using Domain;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 | using Persistence;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 | using System.Threading.Tasks;
14 |
15 | namespace Application.Activities
16 | {
17 | public class List
18 | {
19 | public class Query: IRequest>>
20 | {
21 | public ActivityParams Params { get; set; }
22 | }
23 |
24 | public class Handler : IRequestHandler>>
25 | {
26 | private readonly DataContext context;
27 | private readonly IMapper mapper;
28 | private readonly IUserAccessor userAccessor;
29 |
30 | public Handler(DataContext _context, IMapper _mapper, IUserAccessor _userAccessor)
31 | {
32 | context = _context;
33 | mapper = _mapper;
34 | userAccessor = _userAccessor;
35 | }
36 |
37 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
38 | {
39 | var query = context.Activities
40 | .Where(d => d.Date >= request.Params.StartDate)
41 | .OrderBy(d => d.Date )
42 | .ProjectTo(mapper.ConfigurationProvider,
43 | new { currentUsername = userAccessor.GetUsername()})
44 | .AsQueryable();
45 |
46 | if(request.Params.IsGoing && !request.Params.IsHost)
47 | {
48 | query = query.Where(x => x.Attendees.Any(a => a.Username == userAccessor.GetUsername()));
49 | }
50 |
51 | if(request.Params.IsHost && !request.Params.IsGoing)
52 | {
53 | query = query.Where(x => x.HostUsername == userAccessor.GetUsername());
54 | }
55 |
56 | return Result>.Success(
57 | await PagedList.CreateAsync(query, request.Params.PageNumber,
58 | request.Params.PageSize));
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Reactivities/Application/Comments/Create.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using AutoMapper;
4 | using Domain;
5 | using FluentValidation;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 | using Persistence;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 | using System.Threading.Tasks;
14 |
15 | namespace Application.Comments
16 | {
17 | public class Create
18 | {
19 | public class Command : IRequest>
20 | {
21 | public string Body { get; set; }
22 | public Guid ActivityId { get; set; }
23 | }
24 |
25 | public class CommandValidator : AbstractValidator
26 | {
27 | public CommandValidator()
28 | {
29 | RuleFor(x => x.Body).NotEmpty();
30 | }
31 | }
32 |
33 | public class Handler : IRequestHandler>
34 | {
35 | private readonly DataContext context;
36 | private readonly IMapper mapper;
37 | private readonly IUserAccessor userAccessor;
38 |
39 | public Handler(DataContext _context, IMapper _mapper, IUserAccessor _userAccessor)
40 | {
41 | context = _context;
42 | mapper = _mapper;
43 | userAccessor = _userAccessor;
44 | }
45 | public async Task> Handle(Command request, CancellationToken cancellationToken)
46 | {
47 | var activity = await context.Activities.FindAsync(request.ActivityId);
48 |
49 | if (activity == null) return null;
50 |
51 | var user = await context.Users.Include(p => p.Photos)
52 | .SingleOrDefaultAsync(x => x.UserName == userAccessor.GetUsername());
53 |
54 | var comment = new Comment
55 | {
56 | Author = user,
57 | Activity = activity,
58 | Body = request.Body
59 | };
60 |
61 | activity.Comments.Add(comment);
62 |
63 | var success = await context.SaveChangesAsync() > 0;
64 |
65 | if (success) return Result.Success(mapper.Map(comment));
66 |
67 | return Result.Failure("Failed to add comment");
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Reactivities/Application/Followers/List.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using AutoMapper;
4 | using AutoMapper.QueryableExtensions;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 | using Persistence;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 |
14 | namespace Application.Followers
15 | {
16 | public class List
17 | {
18 | public class Query : IRequest>>
19 | {
20 | public string Predicate { get; set; }
21 | public string Username { get; set; }
22 | }
23 |
24 | public class Handler : IRequestHandler>>
25 | {
26 | private readonly DataContext context;
27 | private readonly IMapper mapper;
28 | private readonly IUserAccessor userAccessor;
29 |
30 | public Handler(DataContext _context, IMapper _mapper, IUserAccessor _userAccessor)
31 | {
32 | context = _context;
33 | mapper = _mapper;
34 | userAccessor = _userAccessor;
35 | }
36 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
37 | {
38 | var profiles = new List();
39 |
40 | switch(request.Predicate)
41 | {
42 | case "followers":
43 | profiles = await context.UserFollowings.Where(x => x.Target.UserName == request.Username)
44 | .Select(u => u.Observer)
45 | .ProjectTo(mapper.ConfigurationProvider, new { currentUsername = userAccessor.GetUsername()})
46 | .ToListAsync();
47 | break;
48 |
49 | case "following":
50 | profiles = await context.UserFollowings.Where(x => x.Observer.UserName == request.Username)
51 | .Select(u => u.Target)
52 | .ProjectTo(mapper.ConfigurationProvider, new { currentUsername = userAccessor.GetUsername() })
53 | .ToListAsync();
54 | break;
55 | }
56 |
57 | return Result>.Success(profiles);
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Reactivities/API/Extensions/IdentityServiceExtensions.cs:
--------------------------------------------------------------------------------
1 | using API.Services;
2 | using Domain;
3 | using Infrastructure.Security;
4 | using Microsoft.AspNetCore.Authentication.JwtBearer;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Microsoft.AspNetCore.Identity;
7 | using Microsoft.IdentityModel.Tokens;
8 | using Persistence;
9 | using System.Text;
10 |
11 | namespace API.Extensions
12 | {
13 | public static class IdentityServiceExtensions
14 | {
15 | public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration config)
16 | {
17 | services.AddIdentityCore(opt =>
18 | {
19 | opt.Password.RequireNonAlphanumeric = false;
20 | }).AddEntityFrameworkStores()
21 | .AddSignInManager>();
22 |
23 | var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenKey"]));
24 |
25 | services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
26 | .AddJwtBearer(opt =>
27 | {
28 | opt.TokenValidationParameters = new TokenValidationParameters
29 | {
30 | ValidateIssuerSigningKey = true,
31 | IssuerSigningKey = key,
32 | ValidateIssuer = false,
33 | ValidateAudience = false
34 | };
35 |
36 | opt.Events = new JwtBearerEvents
37 | {
38 | OnMessageReceived = context =>
39 | {
40 | var accessToken = context.Request.Query["access_token"];
41 | var path = context.HttpContext.Request.Path;
42 | if(!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/chat")))
43 | {
44 | context.Token = accessToken;
45 | }
46 | return Task.CompletedTask;
47 | }
48 | };
49 | });
50 |
51 | services.AddAuthorization(opt =>
52 | {
53 | opt.AddPolicy("IsActivityHost", policy =>
54 | {
55 | policy.Requirements.Add(new IsHostRequirement());
56 | });
57 | });
58 | services.AddTransient();
59 | services.AddScoped();
60 |
61 | return services;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Reactivities/Domain/obj/Domain.csproj.nuget.dgspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "format": 1,
3 | "restore": {
4 | "C:\\SelfStudy\\Projects\\ReactDotNet\\Reactivities\\Domain\\Domain.csproj": {}
5 | },
6 | "projects": {
7 | "C:\\SelfStudy\\Projects\\ReactDotNet\\Reactivities\\Domain\\Domain.csproj": {
8 | "version": "1.0.0",
9 | "restore": {
10 | "projectUniqueName": "C:\\SelfStudy\\Projects\\ReactDotNet\\Reactivities\\Domain\\Domain.csproj",
11 | "projectName": "Domain",
12 | "projectPath": "C:\\SelfStudy\\Projects\\ReactDotNet\\Reactivities\\Domain\\Domain.csproj",
13 | "packagesPath": "C:\\Users\\Itdev\\.nuget\\packages\\",
14 | "outputPath": "C:\\SelfStudy\\Projects\\ReactDotNet\\Reactivities\\Domain\\obj\\",
15 | "projectStyle": "PackageReference",
16 | "fallbackFolders": [
17 | "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
18 | ],
19 | "configFilePaths": [
20 | "C:\\Users\\Itdev\\AppData\\Roaming\\NuGet\\NuGet.Config",
21 | "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
22 | "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
23 | ],
24 | "originalTargetFrameworks": [
25 | "net6.0"
26 | ],
27 | "sources": {
28 | "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
29 | "https://api.nuget.org/v3/index.json": {}
30 | },
31 | "frameworks": {
32 | "net6.0": {
33 | "targetAlias": "net6.0",
34 | "projectReferences": {}
35 | }
36 | },
37 | "warningProperties": {
38 | "warnAsError": [
39 | "NU1605"
40 | ]
41 | }
42 | },
43 | "frameworks": {
44 | "net6.0": {
45 | "targetAlias": "net6.0",
46 | "dependencies": {
47 | "Microsoft.AspNetCore.Identity.EntityFrameworkCore": {
48 | "target": "Package",
49 | "version": "[6.0.6, )"
50 | }
51 | },
52 | "imports": [
53 | "net461",
54 | "net462",
55 | "net47",
56 | "net471",
57 | "net472",
58 | "net48"
59 | ],
60 | "assetTargetFallback": true,
61 | "warn": true,
62 | "frameworkReferences": {
63 | "Microsoft.NETCore.App": {
64 | "privateAssets": "all"
65 | }
66 | },
67 | "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\6.0.202\\RuntimeIdentifierGraph.json"
68 | }
69 | }
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/Reactivities/client-app/src/app/layout/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { Container} from 'semantic-ui-react';
3 | import NavBar from './NavBar';
4 | import ActivityDashboard from '../../features/activities/dashboard/ActivityDashboard';
5 | import { observer } from 'mobx-react-lite';
6 | import { Route, Switch, useLocation } from 'react-router-dom';
7 | import HomePage from '../../features/home/HomePage';
8 | import ActivityForm from '../../features/activities/form/ActivityForm';
9 | import ActivityDetails from '../../features/activities/details/ActivityDetails';
10 | import TestErrors from '../../features/errors/TestError';
11 | import { ToastContainer } from 'react-toastify';
12 | import NotFound from '../../features/errors/NotFound';
13 | import ServerError from '../../features/errors/ServerError';
14 | import { useStore } from '../stores/store';
15 | import LoadingComponent from './LoadingComponent';
16 | import ModalContainer from '../common/modals/ModalContainer';
17 | import ProfilePage from '../../features/profiles/ProfilePage';
18 | import PrivateRoute from './PrivateRoute';
19 |
20 | function App() {
21 | const location = useLocation();
22 | const {commonStore, userStore } = useStore();
23 |
24 | useEffect(() => {
25 | if(commonStore.token) {
26 | userStore.getUser().finally(() => commonStore.setAppLoaded());
27 | } else {
28 | commonStore.setAppLoaded();
29 | }
30 | }, [commonStore, userStore] )
31 |
32 | if(!commonStore.appLoaded) return
33 | return (
34 | <>
35 |
36 |
37 |
38 | (
41 | <>
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | >
55 | )}
56 | />
57 |
58 | >
59 | );
60 | }
61 |
62 | export default observer(App);
63 |
--------------------------------------------------------------------------------
/Reactivities/Application/Activities/UpdateAttendance.cs:
--------------------------------------------------------------------------------
1 | using Application.Core;
2 | using Application.Interfaces;
3 | using Domain;
4 | using MediatR;
5 | using Microsoft.EntityFrameworkCore;
6 | using Persistence;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace Application.Activities
14 | {
15 | public class UpdateAttendance
16 | {
17 | public class Command: IRequest>
18 | {
19 | public Guid Id { get; set; }
20 | }
21 |
22 | public class Handler : IRequestHandler>
23 | {
24 | private readonly DataContext context;
25 | private readonly IUserAccessor userAccessor;
26 |
27 | public Handler(DataContext _context, IUserAccessor _userAccessor)
28 | {
29 | context = _context;
30 | userAccessor = _userAccessor;
31 | }
32 | public async Task> Handle(Command request, CancellationToken cancellationToken)
33 | {
34 | var activity = await context.Activities
35 | .Include(a => a.Attendees).ThenInclude(u => u.AppUser)
36 | .SingleOrDefaultAsync(x => x.Id == request.Id);
37 |
38 | if (activity == null) return null;
39 |
40 | var user = await context.Users.FirstOrDefaultAsync(x =>
41 | x.UserName == userAccessor.GetUsername());
42 |
43 | if (user == null) return null;
44 |
45 | var hostUsername = activity.Attendees.FirstOrDefault(x => x.IsHost)?.AppUser.UserName;
46 |
47 | var attendance = activity.Attendees.FirstOrDefault(x => x.AppUser.UserName == user.UserName);
48 |
49 | if (attendance != null && hostUsername == user.UserName)
50 | activity.IsCancelled = !activity.IsCancelled;
51 |
52 | if (attendance != null && hostUsername != user.UserName)
53 | activity.Attendees.Remove(attendance);
54 |
55 | if (attendance == null)
56 | {
57 | attendance = new ActivityAttendee
58 | {
59 | AppUser = user,
60 | Activity = activity,
61 | IsHost = false
62 | };
63 |
64 | activity.Attendees.Add(attendance);
65 | }
66 |
67 | var result = await context.SaveChangesAsync() > 0;
68 |
69 | return result ? Result.Success(Unit.Value) : Result.Failure("Problem updating attendance...");
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Reactivities/API/Program.cs:
--------------------------------------------------------------------------------
1 | using API.Extensions;
2 | using API.Middleware;
3 | using API.SignalR;
4 | using Application.Activities;
5 | using Application.Core;
6 | using Domain;
7 | using MediatR;
8 | using Microsoft.AspNetCore.Identity;
9 | using Microsoft.EntityFrameworkCore;
10 | using Persistence;
11 |
12 | var builder = WebApplication.CreateBuilder(args);
13 |
14 | // Add services to the container.
15 |
16 | builder.Services.AddApplicationServices(builder.Configuration);
17 | builder.Services.AddIdentityServices(builder.Configuration);
18 |
19 | var app = builder.Build();
20 |
21 | AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
22 |
23 | app.UseMiddleware();
24 |
25 | app.UseXContentTypeOptions();
26 | app.UseReferrerPolicy(opt => opt.NoReferrer());
27 | app.UseXXssProtection(opt => opt.EnabledWithBlockMode());
28 | app.UseXfo(opt => opt.Deny());
29 | app.UseCsp(opt => opt
30 | .BlockAllMixedContent()
31 | .StyleSources(s => s.Self().CustomSources("https://fonts.googleapis.com"))
32 | .FontSources(s => s.Self().CustomSources("https://fonts.gstatic.com", "data:"))
33 | .FormActions(s => s.Self())
34 | .FrameAncestors(s => s.Self())
35 | .ImageSources(s => s.Self().CustomSources("https://res.cloudinary.com"))
36 | .ScriptSources(s => s.Self().CustomSources("sha256-5As4+3YpY62+l38PsxCEkjB1R4YtyktBtRScTJ3fyLU=", "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
37 | "sha256-tVFibyLEbUGj+pO/ZSi96c01jJCvzWilvI5Th+wLeGE=")));
38 |
39 | // Configure the HTTP request pipeline.
40 | if (app.Environment.IsDevelopment())
41 | {
42 | app.UseSwagger();
43 | app.UseSwaggerUI();
44 | }
45 | else
46 | {
47 | app.Use(async (context, next) =>
48 | {
49 | context.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000");
50 | await next.Invoke();
51 | });
52 | }
53 |
54 | //app.UseHttpsRedirection();
55 |
56 | app.UseDefaultFiles();
57 | app.UseStaticFiles();
58 |
59 | app.UseCors("CorsPolicy");
60 |
61 | app.UseAuthentication();
62 | app.UseAuthorization();
63 |
64 | app.MapControllers();
65 |
66 | app.MapHub("/chat");
67 |
68 | app.MapFallbackToController("Index", "Fallback");
69 |
70 | using var scope = app.Services.CreateScope();
71 | var services = scope.ServiceProvider;
72 | var loggerFactory = services.GetRequiredService();
73 | try
74 | {
75 | var context = services.GetRequiredService();
76 | var userManager = services.GetRequiredService>();
77 | await context.Database.MigrateAsync();
78 | await Seed.SeedData(context, userManager);
79 | }
80 | catch (Exception ex)
81 | {
82 | var logger = loggerFactory.CreateLogger();
83 | logger.LogError(ex, "An error occurred during migration");
84 | }
85 |
86 | await app.RunAsync();
87 |
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/precache-manifest.6a292ab25a73b8d93b860646affa8d63.js:
--------------------------------------------------------------------------------
1 | self.__precacheManifest = (self.__precacheManifest || []).concat([
2 | {
3 | "revision": "54b14628480284bbb7f2fdad65e71142",
4 | "url": "/index.html"
5 | },
6 | {
7 | "revision": "b74bed876713cb11afc0",
8 | "url": "/static/css/2.98ec9736.chunk.css"
9 | },
10 | {
11 | "revision": "6060e2e284c145b82b6a",
12 | "url": "/static/css/main.88462492.chunk.css"
13 | },
14 | {
15 | "revision": "b74bed876713cb11afc0",
16 | "url": "/static/js/2.7c4c3785.chunk.js"
17 | },
18 | {
19 | "revision": "6060e2e284c145b82b6a",
20 | "url": "/static/js/main.9d03ff96.chunk.js"
21 | },
22 | {
23 | "revision": "42ac5946195a7306e2a5",
24 | "url": "/static/js/runtime~main.a8a9905a.js"
25 | },
26 | {
27 | "revision": "13db00b7a34fee4d819ab7f9838cc428",
28 | "url": "/static/media/brand-icons.13db00b7.eot"
29 | },
30 | {
31 | "revision": "a046592bac8f2fd96e994733faf3858c",
32 | "url": "/static/media/brand-icons.a046592b.woff"
33 | },
34 | {
35 | "revision": "a1a749e89f578a49306ec2b055c073da",
36 | "url": "/static/media/brand-icons.a1a749e8.svg"
37 | },
38 | {
39 | "revision": "c5ebe0b32dc1b5cc449a76c4204d13bb",
40 | "url": "/static/media/brand-icons.c5ebe0b3.ttf"
41 | },
42 | {
43 | "revision": "e8c322de9658cbeb8a774b6624167c2c",
44 | "url": "/static/media/brand-icons.e8c322de.woff2"
45 | },
46 | {
47 | "revision": "9c74e172f87984c48ddf5c8108cabe67",
48 | "url": "/static/media/flags.9c74e172.png"
49 | },
50 | {
51 | "revision": "0ab54153eeeca0ce03978cc463b257f7",
52 | "url": "/static/media/icons.0ab54153.woff2"
53 | },
54 | {
55 | "revision": "8e3c7f5520f5ae906c6cf6d7f3ddcd19",
56 | "url": "/static/media/icons.8e3c7f55.eot"
57 | },
58 | {
59 | "revision": "962a1bf31c081691065fe333d9fa8105",
60 | "url": "/static/media/icons.962a1bf3.svg"
61 | },
62 | {
63 | "revision": "b87b9ba532ace76ae9f6edfe9f72ded2",
64 | "url": "/static/media/icons.b87b9ba5.ttf"
65 | },
66 | {
67 | "revision": "faff92145777a3cbaf8e7367b4807987",
68 | "url": "/static/media/icons.faff9214.woff"
69 | },
70 | {
71 | "revision": "701ae6abd4719e9c2ada3535a497b341",
72 | "url": "/static/media/outline-icons.701ae6ab.eot"
73 | },
74 | {
75 | "revision": "82f60bd0b94a1ed68b1e6e309ce2e8c3",
76 | "url": "/static/media/outline-icons.82f60bd0.svg"
77 | },
78 | {
79 | "revision": "ad97afd3337e8cda302d10ff5a4026b8",
80 | "url": "/static/media/outline-icons.ad97afd3.ttf"
81 | },
82 | {
83 | "revision": "cd6c777f1945164224dee082abaea03a",
84 | "url": "/static/media/outline-icons.cd6c777f.woff2"
85 | },
86 | {
87 | "revision": "ef60a4f6c25ef7f39f2d25a748dbecfe",
88 | "url": "/static/media/outline-icons.ef60a4f6.woff"
89 | }
90 | ]);
--------------------------------------------------------------------------------
/Reactivities/API/wwwroot/precache-manifest.aa7497f6b9965e8bb998a85b893d1346.js:
--------------------------------------------------------------------------------
1 | self.__precacheManifest = (self.__precacheManifest || []).concat([
2 | {
3 | "revision": "29e16c8995fde2d985b8ff7a9a8705bb",
4 | "url": "/index.html"
5 | },
6 | {
7 | "revision": "541d49619be568e03f11",
8 | "url": "/static/css/2.98ec9736.chunk.css"
9 | },
10 | {
11 | "revision": "de3025162066656e2ca9",
12 | "url": "/static/css/main.88462492.chunk.css"
13 | },
14 | {
15 | "revision": "541d49619be568e03f11",
16 | "url": "/static/js/2.a96d4760.chunk.js"
17 | },
18 | {
19 | "revision": "de3025162066656e2ca9",
20 | "url": "/static/js/main.7932bfcb.chunk.js"
21 | },
22 | {
23 | "revision": "42ac5946195a7306e2a5",
24 | "url": "/static/js/runtime~main.a8a9905a.js"
25 | },
26 | {
27 | "revision": "13db00b7a34fee4d819ab7f9838cc428",
28 | "url": "/static/media/brand-icons.13db00b7.eot"
29 | },
30 | {
31 | "revision": "a046592bac8f2fd96e994733faf3858c",
32 | "url": "/static/media/brand-icons.a046592b.woff"
33 | },
34 | {
35 | "revision": "a1a749e89f578a49306ec2b055c073da",
36 | "url": "/static/media/brand-icons.a1a749e8.svg"
37 | },
38 | {
39 | "revision": "c5ebe0b32dc1b5cc449a76c4204d13bb",
40 | "url": "/static/media/brand-icons.c5ebe0b3.ttf"
41 | },
42 | {
43 | "revision": "e8c322de9658cbeb8a774b6624167c2c",
44 | "url": "/static/media/brand-icons.e8c322de.woff2"
45 | },
46 | {
47 | "revision": "9c74e172f87984c48ddf5c8108cabe67",
48 | "url": "/static/media/flags.9c74e172.png"
49 | },
50 | {
51 | "revision": "0ab54153eeeca0ce03978cc463b257f7",
52 | "url": "/static/media/icons.0ab54153.woff2"
53 | },
54 | {
55 | "revision": "8e3c7f5520f5ae906c6cf6d7f3ddcd19",
56 | "url": "/static/media/icons.8e3c7f55.eot"
57 | },
58 | {
59 | "revision": "962a1bf31c081691065fe333d9fa8105",
60 | "url": "/static/media/icons.962a1bf3.svg"
61 | },
62 | {
63 | "revision": "b87b9ba532ace76ae9f6edfe9f72ded2",
64 | "url": "/static/media/icons.b87b9ba5.ttf"
65 | },
66 | {
67 | "revision": "faff92145777a3cbaf8e7367b4807987",
68 | "url": "/static/media/icons.faff9214.woff"
69 | },
70 | {
71 | "revision": "701ae6abd4719e9c2ada3535a497b341",
72 | "url": "/static/media/outline-icons.701ae6ab.eot"
73 | },
74 | {
75 | "revision": "82f60bd0b94a1ed68b1e6e309ce2e8c3",
76 | "url": "/static/media/outline-icons.82f60bd0.svg"
77 | },
78 | {
79 | "revision": "ad97afd3337e8cda302d10ff5a4026b8",
80 | "url": "/static/media/outline-icons.ad97afd3.ttf"
81 | },
82 | {
83 | "revision": "cd6c777f1945164224dee082abaea03a",
84 | "url": "/static/media/outline-icons.cd6c777f.woff2"
85 | },
86 | {
87 | "revision": "ef60a4f6c25ef7f39f2d25a748dbecfe",
88 | "url": "/static/media/outline-icons.ef60a4f6.woff"
89 | }
90 | ]);
--------------------------------------------------------------------------------
/Reactivities/Application/Core/MappinngProfiles.cs:
--------------------------------------------------------------------------------
1 | using Application.Activities;
2 | using Application.Comments;
3 | using AutoMapper;
4 | using Domain;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace Application.Core
12 | {
13 | public class MappinngProfiles : Profile
14 | {
15 | public MappinngProfiles()
16 | {
17 | string currentUsername = null;
18 |
19 | CreateMap();
20 | CreateMap()
21 | .ForMember(d => d.HostUsername, o => o.MapFrom(s => s.Attendees
22 | .FirstOrDefault(x => x.IsHost).AppUser.UserName));
23 | CreateMap()
24 | .ForMember(d => d.DisplayName, o => o.MapFrom(s => s.AppUser.DisplayName))
25 | .ForMember(d => d.Username, o => o.MapFrom(s => s.AppUser.UserName))
26 | .ForMember(d => d.Bio, o => o.MapFrom(s => s.AppUser.Bio))
27 | .ForMember(d => d.Image, o => o.MapFrom(s => s.AppUser.Photos.FirstOrDefault(x => x.IsMain).Url))
28 | .ForMember(d => d.FollowersCount, o => o.MapFrom(s => s.AppUser.Followers.Count))
29 | .ForMember(d => d.FollowingsCount, o => o.MapFrom(s => s.AppUser.Followings.Count))
30 | .ForMember(d => d.Following, o => o.MapFrom(s => s.AppUser.Followers.Any(x => x.Observer.UserName == currentUsername))); ;
31 |
32 | CreateMap()
33 | .ForMember(d => d.Image, o => o.MapFrom(s => s.Photos.FirstOrDefault(x => x.IsMain).Url))
34 | .ForMember(d => d.FollowersCount, o => o.MapFrom(s => s.Followers.Count))
35 | .ForMember(d => d.FollowingsCount, o => o.MapFrom(s => s.Followings.Count))
36 | .ForMember(d => d.Following, o => o.MapFrom(s => s.Followers.Any(x => x.Observer.UserName == currentUsername)));
37 |
38 | CreateMap()
39 | .ForMember(d => d.DisplayName, o => o.MapFrom(s => s.Author.DisplayName))
40 | .ForMember(d => d.Username, o => o.MapFrom(s => s.Author.UserName))
41 | .ForMember(d => d.Image, o => o.MapFrom(s => s.Author.Photos.FirstOrDefault(x => x.IsMain).Url));
42 |
43 | CreateMap()
44 | .ForMember(d => d.Id, o => o.MapFrom(s => s.Activity.Id))
45 | .ForMember(d => d.Date, o => o.MapFrom(s => s.Activity.Date))
46 | .ForMember(d => d.Title, o => o.MapFrom(s => s.Activity.Title))
47 | .ForMember(d => d.Category, o => o.MapFrom(s => s.Activity.Category))
48 | .ForMember(d => d.HostUsername, o => o.MapFrom(s => s.Activity.Attendees.FirstOrDefault(x => x.IsHost).AppUser.UserName));
49 |
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Reactivities/API/obj/API.csproj.nuget.g.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | True
5 | NuGet
6 | $(MSBuildThisFileDirectory)project.assets.json
7 | $(UserProfile)\.nuget\packages\
8 | C:\Users\Itdev\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages
9 | PackageReference
10 | 6.1.0
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | C:\Users\Itdev\.nuget\packages\microsoft.extensions.apidescription.server\3.0.0
24 | C:\Users\Itdev\.nuget\packages\microsoft.entityframeworkcore.tools\6.0.6
25 |
26 |
--------------------------------------------------------------------------------
/Reactivities/client-app/src/features/profiles/ProfileActivities.tsx:
--------------------------------------------------------------------------------
1 | import React, { SyntheticEvent, useEffect } from "react";
2 | import { observer } from "mobx-react-lite";
3 | import { useStore } from "../../app/stores/store";
4 | import { Card, Grid, Header, Tab, TabProps, Image } from "semantic-ui-react";
5 | import { UserActivity } from "../../app/model/profile";
6 | import { Link } from "react-router-dom";
7 | import { format } from "date-fns";
8 |
9 | const panes = [
10 | {menuItem: 'Future Events', pane: {key: 'future'}},
11 | {menuItem: 'Past Events', pane: {key: 'past'}},
12 | {menuItem: 'Hosting', pane: {key: 'hosting'}}
13 | ]
14 |
15 | export default observer(function ProfileActivities() {
16 | const {profileStore} = useStore();
17 | const {
18 | loadUserActivities,
19 | profile,
20 | loadingActivities, userActivities } = profileStore;
21 |
22 | useEffect(() => {
23 | loadUserActivities(profile!.username);
24 | }, [loadUserActivities, profile])
25 |
26 | const handleTabChange = (e: SyntheticEvent, data: TabProps) => {
27 | loadUserActivities(profile!.username, panes[data.activeIndex as number].pane.key);
28 | };
29 |
30 | return (
31 |
32 |
33 |
34 |
36 |
37 | handleTabChange(e, data)}/>
39 |
40 |
41 | {userActivities.map((activity: UserActivity) => {
42 | return (
43 |
44 |
46 |
47 |
48 | {activity.title}
49 |
50 | {format(new Date(activity.date), 'do LLL')}
51 | {format(new Date(activity.date), 'h:mm a')}
52 |
53 |
54 |
55 | );
56 | })}
57 |
58 |
59 |
60 |
61 | );
62 |
63 | });
--------------------------------------------------------------------------------
/Reactivities/Reactivities.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.1.32421.90
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "API", "API\API.csproj", "{0C8B3BFC-003A-4FA0-948B-63D48ED19CB3}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain", "Domain\Domain.csproj", "{1DE4E78A-48AA-46F0-8A78-ABE238A58ED2}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "Application\Application.csproj", "{038D5002-9136-471D-A896-F058FC18E4AD}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence", "Persistence\Persistence.csproj", "{18E80C28-571D-4901-8A53-DB649AE04F59}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{9B8C739C-5210-4413-8455-776C8E867187}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {0C8B3BFC-003A-4FA0-948B-63D48ED19CB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {0C8B3BFC-003A-4FA0-948B-63D48ED19CB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {0C8B3BFC-003A-4FA0-948B-63D48ED19CB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {0C8B3BFC-003A-4FA0-948B-63D48ED19CB3}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {1DE4E78A-48AA-46F0-8A78-ABE238A58ED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {1DE4E78A-48AA-46F0-8A78-ABE238A58ED2}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {1DE4E78A-48AA-46F0-8A78-ABE238A58ED2}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {1DE4E78A-48AA-46F0-8A78-ABE238A58ED2}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {038D5002-9136-471D-A896-F058FC18E4AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {038D5002-9136-471D-A896-F058FC18E4AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {038D5002-9136-471D-A896-F058FC18E4AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {038D5002-9136-471D-A896-F058FC18E4AD}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {18E80C28-571D-4901-8A53-DB649AE04F59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {18E80C28-571D-4901-8A53-DB649AE04F59}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {18E80C28-571D-4901-8A53-DB649AE04F59}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {18E80C28-571D-4901-8A53-DB649AE04F59}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {9B8C739C-5210-4413-8455-776C8E867187}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {9B8C739C-5210-4413-8455-776C8E867187}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {9B8C739C-5210-4413-8455-776C8E867187}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {9B8C739C-5210-4413-8455-776C8E867187}.Release|Any CPU.Build.0 = Release|Any CPU
42 | EndGlobalSection
43 | GlobalSection(SolutionProperties) = preSolution
44 | HideSolutionNode = FALSE
45 | EndGlobalSection
46 | GlobalSection(ExtensibilityGlobals) = postSolution
47 | SolutionGuid = {11D4863B-B583-4F01-AF64-18CA58B8F4A5}
48 | EndGlobalSection
49 | EndGlobal
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ReactDotNet
2 | ASP.NET Web API with React JS Framework
3 |
4 | # ASP WEB API commands used
5 | -dotnet new sln
6 | -dotnet new webapi -n API
7 | -dotnet new classlib -n Application
8 | -dotnet new classlib -n Domain
9 | -dotnet new classlib -n Persistence
10 |
11 | # ADD Proj dependencies
12 | -dotnet sln
13 | -dotnet sln list
14 |
15 | -dotnet sln add API/API.csproj
16 | -dotnet sln add Application
17 | -dotnet sln add Persistence
18 | -dotnet sln add Domain
19 |
20 | -cd API -> dotnet add reference ../Application
21 | -cd ../ cd Application -> dotnet add reference ../Persistence && dotnet add reference ../Domain
22 | -cd ../ cd Persistence -> dotnet add reference ../Domain
23 |
24 |
25 | # GUIDES
26 | https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60-samples?view=aspnetcore-5.0
27 |
28 | EF CORE COMMAND FOR VS 2022
29 | /*
30 | Add-Migration
31 | Bundle-Migration
32 | Drop-Database
33 | Get-DbContext
34 | Get-Migration
35 | Optimize-DbContext
36 | Remove-Migration
37 | Scaffold-DbContext
38 | Script-DbContext
39 | Script-Migration
40 | Update-Database */
41 |
42 | -Add-Migration InitialMigrate -OutputDir Data/Migrations
43 | -Update-Database
44 |
45 | #Revert Migration After applied in Db
46 | -Update-Database -Migration 0
47 | Update-Database -Migration 0 -p Infrastructure -s API
48 |
49 | Add-Migration InitialCreate -p Infrastructure -s API -o Data/Migrations
50 | Update-Database -p Infrastructure -s API
51 |
52 | Add-Migration IdentityAdded -p Persistence -s API
53 | Remove-Migration -p Persistence -s API
54 |
55 | jwt.io => DECODE TOKEN JWT
56 |
57 |
58 | # REACT
59 | npx create-react-app client-app --use-npm --template typescript
60 | npx create-react-app client-app
61 | npm start
62 |
63 | # DOWNGRADE
64 | npm install -–save react@17.0.2 react-dom@17.0.2
65 | https://exerror.com/how-to-downgrade-react-version-18-to-17/
66 |
67 | # REACT PKG
68 | npm install axios
69 | npm install semantic-ui-react semantic-ui-css
70 | npm install mobx mobx-react-lite
71 | npm install react-router-dom@5.2.0
72 | npm install history@4.3.4
73 | npm install react-calendar@3.2.1
74 | npm install @types/react-calendar@3.1.2
75 | npm install react-toastify@5.3.2
76 | npm install formik@2.2.6
77 | npm install yup@0.32.8
78 | npm install @types/yup@0.29.11 --save-dev
79 | npm install react-datepicker@3.3.0
80 | npm install @types/react-datepicker@3.1.2
81 | npm install react-datepicker --legacy-peer-deps
82 |
83 |
84 | npm ls date-fns
85 | npm install date-fns@2.16.1
86 |
87 | npm install --save react-dropzone
88 | npm install --save react-cropper --legacy-peer-deeps
89 |
90 | npm install @microsoft/signalr
91 |
92 |
93 | https://vertabelo.com
94 | dotnet ef migrations script -o test.sql -p Persistence -s API
95 |
96 | npm install react-infinite-scroller --legacy-peer-deps
97 |
98 |
99 | # FOR PRODUCTION
100 | npm run build
101 |
102 | docker run --name dev -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=secret -p 5432:5432 -d postgres:latest
103 |
104 | https://hub.docker.com/_/postgres
105 |
106 | Add-Migration PGInitial -p Persistence -s API
107 |
108 |
109 |
110 | git add .
111 | git commit -m "Heroku PROD"
112 | git push heroku main
113 |
114 |
115 | https://reactivitiesudemy.herokuapp.com/
116 |
--------------------------------------------------------------------------------
/Reactivities/Domain/obj/project.nuget.cache:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "dgSpecHash": "HTXIA5U4qlIljDLY2SsZX57R0REL1HsZRuOYytipETSxzVu+4Uqo/JKyGchzy3xcGktQ6SrIsP+w9BCFky6a4g==",
4 | "success": true,
5 | "projectFilePath": "C:\\SelfStudy\\Projects\\ReactDotNet\\Reactivities\\Domain\\Domain.csproj",
6 | "expectedPackageFiles": [
7 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.aspnetcore.cryptography.internal\\6.0.6\\microsoft.aspnetcore.cryptography.internal.6.0.6.nupkg.sha512",
8 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.aspnetcore.cryptography.keyderivation\\6.0.6\\microsoft.aspnetcore.cryptography.keyderivation.6.0.6.nupkg.sha512",
9 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.aspnetcore.identity.entityframeworkcore\\6.0.6\\microsoft.aspnetcore.identity.entityframeworkcore.6.0.6.nupkg.sha512",
10 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.entityframeworkcore\\6.0.6\\microsoft.entityframeworkcore.6.0.6.nupkg.sha512",
11 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.entityframeworkcore.abstractions\\6.0.6\\microsoft.entityframeworkcore.abstractions.6.0.6.nupkg.sha512",
12 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.entityframeworkcore.analyzers\\6.0.6\\microsoft.entityframeworkcore.analyzers.6.0.6.nupkg.sha512",
13 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.entityframeworkcore.relational\\6.0.6\\microsoft.entityframeworkcore.relational.6.0.6.nupkg.sha512",
14 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.caching.abstractions\\6.0.0\\microsoft.extensions.caching.abstractions.6.0.0.nupkg.sha512",
15 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.caching.memory\\6.0.1\\microsoft.extensions.caching.memory.6.0.1.nupkg.sha512",
16 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.configuration.abstractions\\6.0.0\\microsoft.extensions.configuration.abstractions.6.0.0.nupkg.sha512",
17 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.dependencyinjection\\6.0.0\\microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512",
18 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\6.0.0\\microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512",
19 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.identity.core\\6.0.6\\microsoft.extensions.identity.core.6.0.6.nupkg.sha512",
20 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.identity.stores\\6.0.6\\microsoft.extensions.identity.stores.6.0.6.nupkg.sha512",
21 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.logging\\6.0.0\\microsoft.extensions.logging.6.0.0.nupkg.sha512",
22 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.logging.abstractions\\6.0.0\\microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512",
23 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.options\\6.0.0\\microsoft.extensions.options.6.0.0.nupkg.sha512",
24 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.primitives\\6.0.0\\microsoft.extensions.primitives.6.0.0.nupkg.sha512",
25 | "C:\\Users\\Itdev\\.nuget\\packages\\system.collections.immutable\\6.0.0\\system.collections.immutable.6.0.0.nupkg.sha512",
26 | "C:\\Users\\Itdev\\.nuget\\packages\\system.diagnostics.diagnosticsource\\6.0.0\\system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512",
27 | "C:\\Users\\Itdev\\.nuget\\packages\\system.runtime.compilerservices.unsafe\\6.0.0\\system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
28 | ],
29 | "logs": []
30 | }
--------------------------------------------------------------------------------
/Reactivities/Persistence/obj/project.nuget.cache:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "dgSpecHash": "O1rhR4SzzjabEQAQH58KEmDG9ZCDF2GcIThHqu3cD5dJvwHcBEGBn3rGBQCHhEnad27a965wGQadAceZDd/8mw==",
4 | "success": true,
5 | "projectFilePath": "C:\\SelfStudy\\Projects\\ReactDotNet\\Reactivities\\Persistence\\Persistence.csproj",
6 | "expectedPackageFiles": [
7 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.aspnetcore.cryptography.internal\\6.0.6\\microsoft.aspnetcore.cryptography.internal.6.0.6.nupkg.sha512",
8 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.aspnetcore.cryptography.keyderivation\\6.0.6\\microsoft.aspnetcore.cryptography.keyderivation.6.0.6.nupkg.sha512",
9 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.aspnetcore.identity.entityframeworkcore\\6.0.6\\microsoft.aspnetcore.identity.entityframeworkcore.6.0.6.nupkg.sha512",
10 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.entityframeworkcore\\6.0.6\\microsoft.entityframeworkcore.6.0.6.nupkg.sha512",
11 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.entityframeworkcore.abstractions\\6.0.6\\microsoft.entityframeworkcore.abstractions.6.0.6.nupkg.sha512",
12 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.entityframeworkcore.analyzers\\6.0.6\\microsoft.entityframeworkcore.analyzers.6.0.6.nupkg.sha512",
13 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.entityframeworkcore.relational\\6.0.6\\microsoft.entityframeworkcore.relational.6.0.6.nupkg.sha512",
14 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.caching.abstractions\\6.0.0\\microsoft.extensions.caching.abstractions.6.0.0.nupkg.sha512",
15 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.caching.memory\\6.0.1\\microsoft.extensions.caching.memory.6.0.1.nupkg.sha512",
16 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.configuration.abstractions\\6.0.0\\microsoft.extensions.configuration.abstractions.6.0.0.nupkg.sha512",
17 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.dependencyinjection\\6.0.0\\microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512",
18 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\6.0.0\\microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512",
19 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.identity.core\\6.0.6\\microsoft.extensions.identity.core.6.0.6.nupkg.sha512",
20 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.identity.stores\\6.0.6\\microsoft.extensions.identity.stores.6.0.6.nupkg.sha512",
21 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.logging\\6.0.0\\microsoft.extensions.logging.6.0.0.nupkg.sha512",
22 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.logging.abstractions\\6.0.0\\microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512",
23 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.options\\6.0.0\\microsoft.extensions.options.6.0.0.nupkg.sha512",
24 | "C:\\Users\\Itdev\\.nuget\\packages\\microsoft.extensions.primitives\\6.0.0\\microsoft.extensions.primitives.6.0.0.nupkg.sha512",
25 | "C:\\Users\\Itdev\\.nuget\\packages\\npgsql\\6.0.5\\npgsql.6.0.5.nupkg.sha512",
26 | "C:\\Users\\Itdev\\.nuget\\packages\\npgsql.entityframeworkcore.postgresql\\6.0.5\\npgsql.entityframeworkcore.postgresql.6.0.5.nupkg.sha512",
27 | "C:\\Users\\Itdev\\.nuget\\packages\\system.collections.immutable\\6.0.0\\system.collections.immutable.6.0.0.nupkg.sha512",
28 | "C:\\Users\\Itdev\\.nuget\\packages\\system.diagnostics.diagnosticsource\\6.0.0\\system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512",
29 | "C:\\Users\\Itdev\\.nuget\\packages\\system.runtime.compilerservices.unsafe\\6.0.0\\system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
30 | ],
31 | "logs": []
32 | }
--------------------------------------------------------------------------------