Skip to content

Commit

Permalink
fix: set editRequest attribute as necessary, allow users to edit thei…
Browse files Browse the repository at this point in the history
…r own pending requests, and show 'View Request' button on series pages (#1446)

* fix: set editRequest attribute for RequestModal

* fix: remove now-unneeded conditional

* fix(ui): only show 'View Request' for user's own requests if they don't have MANAGE_REQUESTS perm

* fix(ui): show edit button on request list for own requests & 'View Request' button on series pages

* fix(ui): do not show 'Request More' if user already has a pending request

* fix: address PR comments

* fix(lang): edit usercreatedfaileexisting string & generate translation key

* fix: users should always be able to view/edit their own requests even if their perms have changed

also fixed capitalization of 'Signing In...' string
  • Loading branch information
TheCatLady authored Apr 19, 2021
1 parent f13f1c9 commit 89455ad
Show file tree
Hide file tree
Showing 10 changed files with 246 additions and 182 deletions.
34 changes: 23 additions & 11 deletions server/routes/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,6 @@ requestRoutes.get('/:requestId', async (req, res, next) => {

requestRoutes.put<{ requestId: string }>(
'/:requestId',
isAuthenticated(Permission.MANAGE_REQUESTS),
async (req, res, next) => {
const requestRepository = getRepository(MediaRequest);
const userRepository = getRepository(User);
Expand All @@ -503,17 +502,30 @@ requestRoutes.put<{ requestId: string }>(
);

if (!request) {
return next({ status: 404, message: 'Request not found' });
return next({ status: 404, message: 'Request not found.' });
}

if (
(request.requestedBy.id !== req.user?.id ||
(req.body.mediaType !== 'tv' &&
!req.user?.hasPermission(Permission.REQUEST_ADVANCED))) &&
!req.user?.hasPermission(Permission.MANAGE_REQUESTS)
) {
return next({
status: 403,
message: 'You do not have permission to modify this request.',
});
}

let requestUser = req.user;

if (
req.body.userId &&
!(
req.user?.hasPermission(Permission.MANAGE_USERS) &&
req.user?.hasPermission(Permission.MANAGE_REQUESTS)
)
req.body.userId !== req.user?.id &&
!req.user?.hasPermission([
Permission.MANAGE_USERS,
Permission.MANAGE_REQUESTS,
])
) {
return next({
status: 403,
Expand Down Expand Up @@ -546,7 +558,7 @@ requestRoutes.put<{ requestId: string }>(

if (!requestedSeasons || requestedSeasons.length === 0) {
throw new Error(
'Missing seasons. If you want to cancel a tv request, use the DELETE method.'
'Missing seasons. If you want to cancel a series request, use the DELETE method.'
);
}

Expand Down Expand Up @@ -633,7 +645,7 @@ requestRoutes.delete('/:requestId', async (req, res, next) => {
) {
return next({
status: 401,
message: 'You do not have permission to remove this request',
message: 'You do not have permission to delete this request.',
});
}

Expand All @@ -642,7 +654,7 @@ requestRoutes.delete('/:requestId', async (req, res, next) => {
return res.status(204).send();
} catch (e) {
logger.error(e.message);
next({ status: 404, message: 'Request not found' });
next({ status: 404, message: 'Request not found.' });
}
});

Expand All @@ -668,7 +680,7 @@ requestRoutes.post<{
label: 'Media Request',
message: e.message,
});
next({ status: 404, message: 'Request not found' });
next({ status: 404, message: 'Request not found.' });
}
}
);
Expand Down Expand Up @@ -712,7 +724,7 @@ requestRoutes.post<{
label: 'Media Request',
message: e.message,
});
next({ status: 404, message: 'Request not found' });
next({ status: 404, message: 'Request not found.' });
}
}
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Login/LocalLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const messages = defineMessages({
validationemailrequired: 'You must provide a valid email address',
validationpasswordrequired: 'You must provide a password',
loginerror: 'Something went wrong while trying to sign in.',
signingin: 'Signing in…',
signingin: 'Signing In…',
signin: 'Sign In',
forgotpassword: 'Forgot Password?',
});
Expand Down
2 changes: 1 addition & 1 deletion src/components/PlexLoginButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import PlexOAuth from '../../utils/plex';

const messages = defineMessages({
signinwithplex: 'Sign In',
signingin: 'Signing in…',
signingin: 'Signing In…',
});

const plexOAuth = new PlexOAuth();
Expand Down
146 changes: 85 additions & 61 deletions src/components/RequestButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
XIcon,
} from '@heroicons/react/solid';
import axios from 'axios';
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import {
MediaRequestStatus,
Expand All @@ -23,19 +23,19 @@ const messages = defineMessages({
viewrequest: 'View Request',
viewrequest4k: 'View 4K Request',
requestmore: 'Request More',
requestmore4k: 'Request More 4K',
requestmore4k: 'Request More in 4K',
approverequest: 'Approve Request',
approverequest4k: 'Approve 4K Request',
declinerequest: 'Decline Request',
declinerequest4k: 'Decline 4K Request',
approverequests:
'Approve {requestCount} {requestCount, plural, one {Request} other {Requests}}',
'Approve {requestCount, plural, one {Request} other {{requestCount} Requests}}',
declinerequests:
'Decline {requestCount} {requestCount, plural, one {Request} other {Requests}}',
'Decline {requestCount, plural, one {Request} other {{requestCount} Requests}}',
approve4krequests:
'Approve {requestCount} 4K {requestCount, plural, one {Request} other {Requests}}',
'Approve {requestCount, plural, one {Request} other {{requestCount} 4K Requests}}',
decline4krequests:
'Decline {requestCount} 4K {requestCount, plural, one {Request} other {Requests}}',
'Decline {requestCount, plural, one {Request} other {{requestCount} 4K Requests}}',
});

interface ButtonOption {
Expand Down Expand Up @@ -64,26 +64,34 @@ const RequestButton: React.FC<RequestButtonProps> = ({
}) => {
const intl = useIntl();
const settings = useSettings();
const { hasPermission } = useUser();
const { user, hasPermission } = useUser();
const [showRequestModal, setShowRequestModal] = useState(false);
const [showRequest4kModal, setShowRequest4kModal] = useState(false);
const [editRequest, setEditRequest] = useState(false);

const activeRequest = media?.requests.find(
(request) => request.status === MediaRequestStatus.PENDING && !request.is4k
);
const active4kRequest = media?.requests.find(
(request) => request.status === MediaRequestStatus.PENDING && request.is4k
);

// All pending
// All pending requests
const activeRequests = media?.requests.filter(
(request) => request.status === MediaRequestStatus.PENDING && !request.is4k
);

const active4kRequests = media?.requests.filter(
(request) => request.status === MediaRequestStatus.PENDING && request.is4k
);

const activeRequest = useMemo(() => {
return activeRequests && activeRequests.length > 0
? activeRequests.find((request) => request.requestedBy.id === user?.id) ??
activeRequests[0]
: undefined;
}, [activeRequests, user]);

const active4kRequest = useMemo(() => {
return active4kRequests && active4kRequests.length > 0
? active4kRequests.find(
(request) => request.requestedBy.id === user?.id
) ?? active4kRequests[0]
: undefined;
}, [active4kRequests, user]);

const modifyRequest = async (
request: MediaRequest,
type: 'approve' | 'decline'
Expand Down Expand Up @@ -121,24 +129,7 @@ const RequestButton: React.FC<RequestButtonProps> = ({
id: 'request',
text: intl.formatMessage(globalMessages.request),
action: () => {
setShowRequestModal(true);
},
svg: <DownloadIcon className="w-5 h-5 mr-1" />,
});
}

if (
hasPermission(Permission.REQUEST) &&
mediaType === 'tv' &&
media &&
media.status !== MediaStatus.AVAILABLE &&
media.status !== MediaStatus.UNKNOWN &&
!isShowComplete
) {
buttons.push({
id: 'request-more',
text: intl.formatMessage(messages.requestmore),
action: () => {
setEditRequest(false);
setShowRequestModal(true);
},
svg: <DownloadIcon className="w-5 h-5 mr-1" />,
Expand All @@ -157,26 +148,7 @@ const RequestButton: React.FC<RequestButtonProps> = ({
id: 'request4k',
text: intl.formatMessage(globalMessages.request4k),
action: () => {
setShowRequest4kModal(true);
},
svg: <DownloadIcon className="w-5 h-5 mr-1" />,
});
}

if (
mediaType === 'tv' &&
(hasPermission(Permission.REQUEST_4K) ||
(mediaType === 'tv' && hasPermission(Permission.REQUEST_4K_TV))) &&
media &&
media.status4k !== MediaStatus.AVAILABLE &&
media.status4k !== MediaStatus.UNKNOWN &&
!is4kShowComplete &&
settings.currentSettings.series4kEnabled
) {
buttons.push({
id: 'request-more-4k',
text: intl.formatMessage(messages.requestmore4k),
action: () => {
setEditRequest(false);
setShowRequest4kModal(true);
},
svg: <DownloadIcon className="w-5 h-5 mr-1" />,
Expand All @@ -185,27 +157,34 @@ const RequestButton: React.FC<RequestButtonProps> = ({

if (
activeRequest &&
mediaType === 'movie' &&
hasPermission(Permission.REQUEST)
(activeRequest.requestedBy.id === user?.id ||
(activeRequests?.length === 1 &&
hasPermission(Permission.MANAGE_REQUESTS)))
) {
buttons.push({
id: 'active-request',
text: intl.formatMessage(messages.viewrequest),
action: () => setShowRequestModal(true),
action: () => {
setEditRequest(true);
setShowRequestModal(true);
},
svg: <InformationCircleIcon className="w-5 h-5 mr-1" />,
});
}

if (
active4kRequest &&
mediaType === 'movie' &&
(hasPermission(Permission.REQUEST_4K) ||
hasPermission(Permission.REQUEST_4K_MOVIE))
(active4kRequest.requestedBy.id === user?.id ||
(active4kRequests?.length === 1 &&
hasPermission(Permission.MANAGE_REQUESTS)))
) {
buttons.push({
id: 'active-4k-request',
text: intl.formatMessage(messages.viewrequest4k),
action: () => setShowRequest4kModal(true),
action: () => {
setEditRequest(true);
setShowRequest4kModal(true);
},
svg: <InformationCircleIcon className="w-5 h-5 mr-1" />,
});
}
Expand Down Expand Up @@ -320,6 +299,49 @@ const RequestButton: React.FC<RequestButtonProps> = ({
);
}

if (
mediaType === 'tv' &&
(!activeRequest || activeRequest.requestedBy.id !== user?.id) &&
hasPermission(Permission.REQUEST) &&
media &&
media.status !== MediaStatus.AVAILABLE &&
media.status !== MediaStatus.UNKNOWN &&
!isShowComplete
) {
buttons.push({
id: 'request-more',
text: intl.formatMessage(messages.requestmore),
action: () => {
setEditRequest(false);
setShowRequestModal(true);
},
svg: <DownloadIcon className="w-5 h-5 mr-1" />,
});
}

if (
mediaType === 'tv' &&
(!active4kRequest || active4kRequest.requestedBy.id !== user?.id) &&
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
type: 'or',
}) &&
media &&
media.status4k !== MediaStatus.AVAILABLE &&
media.status4k !== MediaStatus.UNKNOWN &&
!is4kShowComplete &&
settings.currentSettings.series4kEnabled
) {
buttons.push({
id: 'request-more-4k',
text: intl.formatMessage(messages.requestmore4k),
action: () => {
setEditRequest(false);
setShowRequest4kModal(true);
},
svg: <DownloadIcon className="w-5 h-5 mr-1" />,
});
}

const [buttonOne, ...others] = buttons;

if (!buttonOne) {
Expand All @@ -332,6 +354,7 @@ const RequestButton: React.FC<RequestButtonProps> = ({
tmdbId={tmdbId}
show={showRequestModal}
type={mediaType}
editRequest={editRequest ? activeRequest : undefined}
onComplete={() => {
onUpdate();
setShowRequestModal(false);
Expand All @@ -342,6 +365,7 @@ const RequestButton: React.FC<RequestButtonProps> = ({
tmdbId={tmdbId}
show={showRequest4kModal}
type={mediaType}
editRequest={editRequest ? active4kRequest : undefined}
is4k
onComplete={() => {
onUpdate();
Expand Down
Loading

0 comments on commit 89455ad

Please sign in to comment.