From 51dfcc4509da30076aceb254fe4ef1bd3f5c692e Mon Sep 17 00:00:00 2001 From: Jeremy Foster Date: Wed, 4 Sep 2024 17:39:45 -0700 Subject: [PATCH] Fix subscriptions --- .../admin/products/ProductSubRequests.tsx | 33 ++++++---- .../src/features/my-products/MyProducts.tsx | 60 +++++++++++++------ .../src/features/my-products/ProductCard.tsx | 10 +++- libs/net/dal/Services/ProductService.cs | 29 ++++++--- .../Models/Product/UserProductModel.cs | 7 +-- 5 files changed, 95 insertions(+), 44 deletions(-) diff --git a/app/editor/src/features/admin/products/ProductSubRequests.tsx b/app/editor/src/features/admin/products/ProductSubRequests.tsx index 8fec9f8e8..135d9624c 100644 --- a/app/editor/src/features/admin/products/ProductSubRequests.tsx +++ b/app/editor/src/features/admin/products/ProductSubRequests.tsx @@ -1,6 +1,6 @@ import { useFormikContext } from 'formik'; import React from 'react'; -import { IProductModel, ProductRequestStatusName, Show } from 'tno-core'; +import { Col, IProductModel, ProductRequestStatusName, Row, Show } from 'tno-core'; import * as styled from './styled'; import { UserApproveDeny } from './UserApproveDeny'; @@ -39,17 +39,26 @@ export const ProductSubRequests: React.FC = () => { {values.subscribers.map((user, index) => { if (user.status !== ProductRequestStatusName.RequestUnsubscribe) return null; return ( - - setFieldValue(`subscribers.${index}`, { - ...user, - status: ProductRequestStatusName.NA, - isSubscribed: !approve, - }) - } - /> + + + + setFieldValue(`subscribers.${index}`, { + ...user, + status: ProductRequestStatusName.NA, + isSubscribed: !approve, + }) + } + /> + + +

+ If this user is currently subscribed through a distribution list, you will need to + first remove them from the list and then approve this request. +

+ +
); })} { userId={userId} product={product} onToggleSubscription={(userProduct) => { + console.debug(userProduct); selectProduct(product, userProduct); toggle(); }} @@ -140,7 +141,7 @@ export const MyProducts: React.FC = () => { {products .filter((product) => // products which the user *IS NOT* unsubscribed to - product.subscribers.some( + product.subscribers.every( (s) => !s.isSubscribed && s.status !== ProductRequestStatusName.RequestSubscription, ), @@ -152,6 +153,7 @@ export const MyProducts: React.FC = () => { userId={userInfo?.id} product={product} onToggleSubscription={(userProduct) => { + console.debug(userProduct); selectProduct(product, userProduct); toggle(); }} @@ -163,27 +165,11 @@ export const MyProducts: React.FC = () => { { if (active) handleToggleSubscription(active.product, active.userProduct); toggle(); @@ -192,3 +178,39 @@ export const MyProducts: React.FC = () => { ); }; + +const modalBody = (active: ISelectedProduct) => { + if (active.userProduct.isSubscribed) { + switch (active.userProduct.status) { + case ProductRequestStatusName.RequestUnsubscribe: + return `Are you sure you wish to unsubscribe to ${active.product.name}`; + default: + return `Are you sure you wish to cancel your pending request to unsubscribe to ${active.product.name}`; + } + } else { + switch (active.userProduct.status) { + case ProductRequestStatusName.RequestSubscription: + return `Are you sure you wish to subscribe to ${active.product.name}`; + default: + return `Are you sure you wish to cancel your pending request to subscribe to ${active.product.name}`; + } + } +}; + +const modalConfirmText = (active: ISelectedProduct) => { + if (active.userProduct.isSubscribed) { + switch (active.userProduct.status) { + case ProductRequestStatusName.RequestUnsubscribe: + return `Yes, request to unsubscribe`; + default: + return `Yes, cancel my pending request to unsubscribe`; + } + } else { + switch (active.userProduct.status) { + case ProductRequestStatusName.RequestSubscription: + return `Yes, request to subscribe`; + default: + return `Yes, cancel my pending request to subscribe`; + } + } +}; diff --git a/app/subscriber/src/features/my-products/ProductCard.tsx b/app/subscriber/src/features/my-products/ProductCard.tsx index 1583f724d..07a17e1f6 100644 --- a/app/subscriber/src/features/my-products/ProductCard.tsx +++ b/app/subscriber/src/features/my-products/ProductCard.tsx @@ -33,7 +33,9 @@ export const ProductCard: React.FC = ({ }) => { const [{ userInfo }] = useApp(); - const userProduct: IUserProductModel = product.subscribers.find((s) => s.userId === userId) ?? { + let userProduct: IUserProductModel | undefined = product.subscribers.find( + (s) => s.userId === userId, + ) ?? { userId: userId ?? 0, productId: product.id, status: ProductRequestStatusName.NA, @@ -48,6 +50,12 @@ export const ProductCard: React.FC = ({ isEnabled: userInfo?.isEnabled ?? true, accountType: UserAccountTypeName.Direct, }; + // Check a distribution list to see if user is subscribed this way. + if (product.subscribers.length > 1 && !userProduct?.isSubscribed) { + const distribution = product.subscribers.find((s) => s.isSubscribed); + // Switch the subscription to the user from the distribution list. + if (distribution) userProduct = { ...distribution, userId: userId ?? 0 }; + } const isSubscribed = userProduct.isSubscribed; const isRequesting = !userProduct.isSubscribed && diff --git a/libs/net/dal/Services/ProductService.cs b/libs/net/dal/Services/ProductService.cs index 0d9517907..9a08d8da9 100644 --- a/libs/net/dal/Services/ProductService.cs +++ b/libs/net/dal/Services/ProductService.cs @@ -76,6 +76,7 @@ public IEnumerable FindPublic() /// public IEnumerable Find(ProductFilter filter) { + int[] distributionIds = Array.Empty(); var query = filter.IsAvailableToUserId.HasValue == true ? this.Context.Products.AsNoTracking() : this.Context.Products.Include(r => r.SubscribersManyToMany).ThenInclude(s => s.User).AsNoTracking(); @@ -91,7 +92,16 @@ public IEnumerable Find(ProductFilter filter) if (filter.SubscriberUserId.HasValue == true) query = query.Where(r => r.SubscribersManyToMany.Any(s => s.UserId == filter.SubscriberUserId.Value)); if (filter.IsAvailableToUserId.HasValue == true) - query = query.Where(r => r.IsPublic || r.SubscribersManyToMany.Any(s => s.UserId == filter.IsAvailableToUserId.Value)); + { + // Get all distribution lists this user is part of. + distributionIds = this.Context.UserDistributions + .Where(ud => ud.LinkedUserId == filter.IsAvailableToUserId.Value) + .Select(ud => ud.UserId).ToArray(); + + query = query.Where(r => r.IsPublic || + r.SubscribersManyToMany.Any(s => s.UserId == filter.IsAvailableToUserId.Value) || + r.SubscribersManyToMany.Any(s => distributionIds.Contains(s.UserId))); + } if (filter.Sort?.Any() == true) { @@ -126,19 +136,22 @@ public IEnumerable Find(ProductFilter filter) .FirstOrDefault(u => u.Id == filter.IsAvailableToUserId.Value) ?? throw new NoContentException(); // Fetch product subscribers only for the specified user. - var subscribers = this.Context.UserProducts.AsNoTracking().Where(up => up.UserId == filter.IsAvailableToUserId.Value).ToArray(); + var subscribers = this.Context.UserProducts + .AsNoTracking() + .Include(s => s.Product) + .Include(s => s.User) + .Include(s => s.User).ThenInclude(u => u!.ReportSubscriptionsManyToMany) + .Include(s => s.User).ThenInclude(u => u!.NotificationSubscriptionsManyToMany) + .Include(s => s.User).ThenInclude(u => u!.AVOverviewSubscriptionsManyToMany) + .Where(up => up.UserId == filter.IsAvailableToUserId.Value || distributionIds.Contains(up.UserId)) + .ToArray(); // For each product add the actual subscription records. foreach (var product in products) { product.SubscribersManyToMany.AddRange(subscribers.Where(s => s.ProductId == product.Id)); var subscriber = product.SubscribersManyToMany.FirstOrDefault(s => s.UserId == filter.IsAvailableToUserId.Value); - if (subscriber != null) - { - subscriber.Product = product; - subscriber.User = user; - } - else + if (!product.SubscribersManyToMany.Any()) product.SubscribersManyToMany.Add(new UserProduct(user, product)); } } diff --git a/libs/net/models/Areas/Subscriber/Models/Product/UserProductModel.cs b/libs/net/models/Areas/Subscriber/Models/Product/UserProductModel.cs index c8a205261..ddaac2b61 100644 --- a/libs/net/models/Areas/Subscriber/Models/Product/UserProductModel.cs +++ b/libs/net/models/Areas/Subscriber/Models/Product/UserProductModel.cs @@ -79,6 +79,7 @@ public UserProductModel() { } /// /// Creates a new instance of an UserProductModel, initializes with specified parameter. + /// An assumption is made in this model. If a subscription exists for either the user or a distribution group, we assume the user is subscribed. /// /// public UserProductModel(Entities.UserProduct entity) : base(entity) @@ -109,15 +110,13 @@ public UserProductModel(Entities.UserProduct entity) : base(entity) else if (entity.Product.ProductType == Entities.ProductType.Notification) { var subscription = entity.User.NotificationSubscriptionsManyToMany - .FirstOrDefault(s => s.UserId == entity.UserId && - s.NotificationId == entity.Product!.TargetProductId); + .FirstOrDefault(s => s.IsSubscribed && s.NotificationId == entity.Product!.TargetProductId); this.IsSubscribed = subscription?.IsSubscribed ?? false; } else if (entity.Product.ProductType == Entities.ProductType.EveningOverview) { var subscription = entity.User.AVOverviewSubscriptionsManyToMany - .FirstOrDefault(s => s.UserId == entity.UserId && - (int)s.TemplateType == entity.Product!.TargetProductId); + .FirstOrDefault(s => s.IsSubscribed && (int)s.TemplateType == entity.Product!.TargetProductId); this.IsSubscribed = subscription?.IsSubscribed ?? false; this.SendTo = subscription?.SendTo; }