diff --git a/apps/juxtaposition-ui/src/models/communities.ts b/apps/juxtaposition-ui/src/models/communities.ts index ee9898c9..5aadd6bf 100644 --- a/apps/juxtaposition-ui/src/models/communities.ts +++ b/apps/juxtaposition-ui/src/models/communities.ts @@ -8,6 +8,14 @@ enum COMMUNITY_TYPE { Private = 3 } +export interface IIconPaths { + 32: string; + 48: string; + 64: string; + 96: string; + 128: string; +} + export interface ICommunityPermissions { open: boolean; minimum_new_post_access_level: number; @@ -38,6 +46,7 @@ export interface ICommunity { icon: string; ctr_header?: string; wup_header?: string; + icon_paths?: IIconPaths; /** @deprecated Does not actually exist on any community. Use title_id */ title_ids?: string[]; // Does not exist on any community title_id: string[]; @@ -102,6 +111,29 @@ export const PermissionsSchema = new Schema({ } }); +const IconPathsSchema = new Schema({ + 32: { + type: String, + required: true + }, + 48: { + type: String, + required: true + }, + 64: { + type: String, + required: true + }, + 96: { + type: String, + required: true + }, + 128: { + type: String, + required: true + } +}); + /* Constraints here (default, required etc.) apply to new documents being added * See ICommunity for expected shape of query results * If you add default: or required:, please also update ICommunity and ICommunityInput! @@ -171,6 +203,7 @@ export const CommunitySchema = new Schema = { type: body.type, has_shop_page: body.has_shop_page, - platform_id: body.platform, + platform_id: Number(body.platform), icon: icons?.tgaBlob ?? oldCommunity.icon, + icon_paths: iconPaths, ctr_header: headers?.ctr ?? oldCommunity.ctr_header, wup_header: headers?.wup ?? oldCommunity.wup_header, title_id: body.title_ids, diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityListView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityListView.tsx index f05d23d8..389cd2e1 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityListView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityListView.tsx @@ -1,16 +1,15 @@ import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root'; -import { useUrl } from '@/services/juxt-web/views/common/hooks/useUrl'; import { T } from '@/services/juxt-web/views/common/components/T'; +import { CtrCommunityIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrCommunityIcon'; import type { ReactNode } from 'react'; import type { CommunityItemProps, CommunityListViewProps, CommunityOverviewViewProps } from '@/services/juxt-web/views/web/communityListView'; export function CtrCommunityItem(props: CommunityItemProps): ReactNode { - const url = useUrl(); const id = props.community.olive_community_id; return (
  • - +
    {props.community.name} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityView.tsx index 401da280..3d749c03 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityView.tsx @@ -3,6 +3,7 @@ import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root'; import { CtrPostListClosedView } from '@/services/juxt-web/views/ctr/postList'; import { useUrl } from '@/services/juxt-web/views/common/hooks/useUrl'; import { T } from '@/services/juxt-web/views/common/components/T'; +import { CtrCommunityIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrCommunityIcon'; import { CtrNavTab, CtrNavTabs, CtrNavTabsRow } from '@/services/juxt-web/views/ctr/components/ui/CtrNavTabs'; import type { ReactNode } from 'react'; import type { CommunityViewProps } from '@/services/juxt-web/views/web/communityView'; @@ -10,7 +11,7 @@ import type { CommunityViewProps } from '@/services/juxt-web/views/web/community export function CtrCommunityView(props: CommunityViewProps): ReactNode { const url = useUrl(); const community = props.community; - const { bannerUrl, imageId, legacy } = url.ctrHeader(community); + const { bannerUrl, legacy } = url.ctrHeader(community); return ( @@ -29,9 +30,7 @@ export function CtrCommunityView(props: CommunityViewProps): ReactNode { >

    - - - + {community.name} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrCommunityIcon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrCommunityIcon.tsx new file mode 100644 index 00000000..2a4e784b --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrCommunityIcon.tsx @@ -0,0 +1,21 @@ +import { CtrIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrIcon'; +import { useUrl } from '@/services/juxt-web/views/common/hooks/useUrl'; +import type { ReactNode } from 'react'; +import type { CommunityIconProps } from '@/services/juxt-web/views/web/components/ui/WebCommunityIcon'; + +export function CtrCommunityIcon(props: CommunityIconProps): ReactNode { + const url = useUrl(); + const imageId = props.community.parent ? props.community.parent : props.community.olive_community_id; + const iconUrl = props.community.icon_paths ? url.cdn(props.community.icon_paths[props.size]) : url.cdn(`/icons/${imageId}/${props.size}.png`); + const href = `/communities/${props.community.community_id}`; + + return ( + + + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrMiiIcon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrMiiIcon.tsx index 42eef521..2ff223ef 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrMiiIcon.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrMiiIcon.tsx @@ -5,7 +5,6 @@ import type { MiiIconProps } from '@/services/juxt-web/views/web/components/ui/W export function CtrMiiIcon(props: MiiIconProps): ReactNode { const url = useUrl(); - const miiUrl = props.face_url ?? url.cdn(`/mii/${props.pid}/normal_face.png`); const href = `/users/${props.pid}`; const type = props.type ?? 'mii-icon'; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityListView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityListView.tsx index 44f9588c..cae5b52a 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityListView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityListView.tsx @@ -1,17 +1,15 @@ import { PortalPageBody, PortalRoot } from '@/services/juxt-web/views/portal/root'; import { PortalNavBar } from '@/services/juxt-web/views/portal/navbar'; -import { useUrl } from '@/services/juxt-web/views/common/hooks/useUrl'; import { T } from '@/services/juxt-web/views/common/components/T'; +import { PortalCommunityIcon } from '@/services/juxt-web/views/portal/components/ui/PortalCommunityIcon'; import type { ReactNode } from 'react'; import type { CommunityItemProps, CommunityListViewProps, CommunityOverviewViewProps } from '@/services/juxt-web/views/web/communityListView'; export function PortalCommunityItem(props: CommunityItemProps): ReactNode { - const url = useUrl(); const id = props.community.olive_community_id; - const imageCommunityId = props.community.parent ? props.community.parent : id; return (
  • - +
    diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityView.tsx index 06877dfc..bcbfea36 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityView.tsx @@ -4,6 +4,7 @@ import { PortalNavBar } from '@/services/juxt-web/views/portal/navbar'; import { PortalPostListClosedView } from '@/services/juxt-web/views/portal/postList'; import { useUrl } from '@/services/juxt-web/views/common/hooks/useUrl'; import { T } from '@/services/juxt-web/views/common/components/T'; +import { PortalCommunityIcon } from '@/services/juxt-web/views/portal/components/ui/PortalCommunityIcon'; import { PortalUIIcon } from '@/services/juxt-web/views/portal/components/ui/PortalUIIcon'; import { PortalNavTab, PortalNavTabs, PortalNavTabsRow } from '@/services/juxt-web/views/portal/components/ui/PortalNavTabs'; import type { ReactNode } from 'react'; @@ -48,12 +49,7 @@ export function PortalCommunityView(props: CommunityViewProps): ReactNode {
    - + {community.icon_paths + ? ( + + ) + : ( + + )}
    diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebCommunityIcon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebCommunityIcon.tsx new file mode 100644 index 00000000..f7e8082c --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebCommunityIcon.tsx @@ -0,0 +1,28 @@ +import { WebIcon } from '@/services/juxt-web/views/web/components/ui/WebIcon'; +import { useUrl } from '@/services/juxt-web/views/common/hooks/useUrl'; +import type { ReactNode } from 'react'; +import type { InferSchemaType } from 'mongoose'; +import type { CommunitySchema } from '@/models/communities'; + +export type CommunityIconProps = { + community: InferSchemaType; + size: '32' | '48' | '64' | '96' | '128'; + className?: string; +}; + +export function WebCommunityIcon(props: CommunityIconProps): ReactNode { + const url = useUrl(); + const imageId = props.community.parent ? props.community.parent : props.community.olive_community_id; + const iconUrl = props.community.icon_paths ? url.cdn(props.community.icon_paths[props.size]) : url.cdn(`/icons/${imageId}/${props.size}.png`); + const href = `/communities/${props.community.community_id}`; + + return ( + + + ); +} diff --git a/apps/miiverse-api/src/models/community.ts b/apps/miiverse-api/src/models/community.ts index 99287ada..39ba4db6 100644 --- a/apps/miiverse-api/src/models/community.ts +++ b/apps/miiverse-api/src/models/community.ts @@ -2,7 +2,7 @@ import crypto from 'node:crypto'; import { Schema, model } from 'mongoose'; import { MongoError } from 'mongodb'; import { CommunityShotModes } from '@/types/mongoose/community'; -import type { ICommunity, ICommunityMethods, CommunityModel, ICommunityPermissions, HydratedCommunityDocument, ICommunityInput } from '@/types/mongoose/community'; +import type { ICommunity, ICommunityMethods, CommunityModel, ICommunityPermissions, HydratedCommunityDocument, ICommunityInput, IIconPaths } from '@/types/mongoose/community'; import type { CommunityData } from '@/types/miiverse/community'; const PermissionsSchema = new Schema({ @@ -24,6 +24,29 @@ const PermissionsSchema = new Schema({ } }); +const IconPathsSchema = new Schema({ + 32: { + type: String, + required: true + }, + 48: { + type: String, + required: true + }, + 64: { + type: String, + required: true + }, + 96: { + type: String, + required: true + }, + 128: { + type: String, + required: true + } +}); + /* Constraints here (default, required etc.) apply to new documents being added * See ICommunity for expected shape of query results * If you add default: or required:, please also update ICommunity and ICommunityInput! @@ -91,6 +114,7 @@ const CommunitySchema = new Schema