Skip to content

Commit

Permalink
Merge pull request #1135 from givepraise/add/current-community
Browse files Browse the repository at this point in the history
Current community
  • Loading branch information
kristoferlund authored Aug 18, 2023
2 parents f034661 + 12960a3 commit fb29d05
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 16 deletions.
25 changes: 23 additions & 2 deletions packages/api-types/out/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ export interface paths {
'/api/communities/isNameAvailable': {
get: operations['CommunityController_isNameAvailable'];
};
'/api/communities/current': {
get: operations['CommunityController_current'];
};
'/api/communities/{id}/discord/link': {
/** Link discord to community */
patch: operations['CommunityController_linkDiscord'];
Expand Down Expand Up @@ -987,6 +990,12 @@ export interface components {
isPublic: boolean;
/** @enum {string} */
discordLinkState: 'NOT_SET' | 'PENDING' | 'ACTIVE' | 'DEACTIVE';
/**
* @example {
* "attestations": true
* }
*/
features: Record<string, never>;
};
UpdateCommunityInputDto: {
/** @example banklessdao.givepraise.xyz */
Expand All @@ -1003,7 +1012,7 @@ export interface components {
*/
owners?: string[];
};
CommunityPaginatedResponseDto: {
CommunityFindAllResponseDto: {
/** @example 1200 */
totalDocs: number;
/** @example 10 */
Expand Down Expand Up @@ -2113,13 +2122,15 @@ export interface operations {
page: number;
sortColumn?: string;
sortType?: 'asc' | 'desc';
/** @example hostname.givepraise.xyz */
hostname?: string;
};
};
responses: {
/** @description All communities */
200: {
content: {
'application/json': components['schemas']['CommunityPaginatedResponseDto'];
'application/json': components['schemas']['CommunityFindAllResponseDto'];
};
};
};
Expand Down Expand Up @@ -2192,6 +2203,16 @@ export interface operations {
};
};
};
CommunityController_current: {
responses: {
/** @description Returns the current community, based on hostname */
200: {
content: {
'application/json': components['schemas']['Community'];
};
};
};
};
/** Link discord to community */
CommunityController_linkDiscord: {
requestBody: {
Expand Down
2 changes: 1 addition & 1 deletion packages/api/openapi.json

Large diffs are not rendered by default.

25 changes: 20 additions & 5 deletions packages/api/src/community/community.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Patch,
Post,
Query,
Req,
SerializeOptions,
UseInterceptors,
} from '@nestjs/common';
Expand All @@ -15,8 +16,7 @@ import { Permission } from '../auth/enums/permission.enum';
import { Community } from './schemas/community.schema';
import { MongooseClassSerializerInterceptor } from '../shared/interceptors/mongoose-class-serializer.interceptor';
import { ObjectIdPipe } from '../shared/pipes/object-id.pipe';
import { CommunityPaginatedResponseDto } from './dto/community-pagination-model.dto';
import { PaginatedQueryDto } from '../shared/dto/pagination-query.dto';
import { CommunityFindAllResponseDto } from './dto/find-all-response.dto';
import { ObjectId, Types } from 'mongoose';
import { Permissions } from '../auth/decorators/permissions.decorator';
import { CreateCommunityInputDto } from './dto/create-community-input.dto';
Expand All @@ -29,6 +29,9 @@ import { IsNameAvailableResponseDto } from './dto/is-name-available-response-dto
import { IsNameAvailableRequestDto } from './dto/is-name-available-request-dto';
import { EventLogService } from '../event-log/event-log.service';
import { EventLogTypeKey } from '../event-log/enums/event-log-type-key';
import { CommunityFindAllQueryDto } from './dto/find-all-query.dto';
import { Public } from '../shared/decorators/public.decorator';
import { Request } from 'express';

@Controller('communities')
@ApiTags('Communities')
Expand Down Expand Up @@ -78,12 +81,12 @@ export class CommunityController {
@ApiResponse({
status: 200,
description: 'All communities',
type: CommunityPaginatedResponseDto,
type: CommunityFindAllResponseDto,
})
@UseInterceptors(MongooseClassSerializerInterceptor(Community))
async findAll(
@Query() options: PaginatedQueryDto,
): Promise<CommunityPaginatedResponseDto> {
@Query() options: CommunityFindAllQueryDto,
): Promise<CommunityFindAllResponseDto> {
return this.communityService.findAllPaginated(options);
}

Expand All @@ -99,6 +102,18 @@ export class CommunityController {
return await this.communityService.isCommunityNameAvailable(options.name);
}

@Get('/current')
@Public()
@ApiResponse({
status: 200,
description: 'Returns the current community, based on hostname',
type: Community,
})
@UseInterceptors(MongooseClassSerializerInterceptor(Community))
async current(@Req() req: Request): Promise<Community> {
return this.communityService.findOne({ host: req.hostname });
}

@Get(':id')
@Permissions(Permission.CommunitiesView)
@ApiResponse({
Expand Down
11 changes: 6 additions & 5 deletions packages/api/src/community/community.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { InjectConnection, InjectModel } from '@nestjs/mongoose';
import { Connection, Types } from 'mongoose';
import { ApiException } from '../shared/exceptions/api-exception';
import { Community } from './schemas/community.schema';
import { PaginatedQueryDto } from '../shared/dto/pagination-query.dto';
import { CommunityPaginatedResponseDto } from './dto/community-pagination-model.dto';
import { CommunityFindAllResponseDto } from './dto/find-all-response.dto';
import { CreateCommunityInputDto } from './dto/create-community-input.dto';
import { UpdateCommunityInputDto } from './dto/update-community-input.dto';
import { LinkDiscordBotDto } from './dto/link-discord-bot.dto';
Expand All @@ -20,6 +19,7 @@ import { databaseExists } from '../database/utils/database-exists';
import { hostNameToDbName } from '../database/utils/host-name-to-db-name';
import { PaginateModel } from '../shared/interfaces/paginate-model.interface';
import { IsNameAvailableResponseDto } from './dto/is-name-available-response-dto';
import { CommunityFindAllQueryDto } from './dto/find-all-query.dto';

@Injectable()
export class CommunityService {
Expand Down Expand Up @@ -56,10 +56,11 @@ export class CommunityService {
* @returns
*/
async findAllPaginated(
options: PaginatedQueryDto,
): Promise<CommunityPaginatedResponseDto> {
options: CommunityFindAllQueryDto,
): Promise<CommunityFindAllResponseDto> {
const { sortColumn, sortType } = options;
const query = {} as any;
const { hostname } = options;
const query = options.hostname ? { hostname } : {};

// Sorting - defaults to descending
const sort =
Expand Down
13 changes: 13 additions & 0 deletions packages/api/src/community/dto/find-all-query.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger';
import { PaginatedQueryDto } from '../../shared/dto/pagination-query.dto';
import { IsOptional } from 'class-validator';

export class CommunityFindAllQueryDto extends PaginatedQueryDto {
@ApiProperty({
required: false,
type: 'string',
example: 'hostname.givepraise.xyz',
})
@IsOptional()
hostname?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Type } from 'class-transformer';
import { PaginatedResponseDto } from '../../shared/dto/paginated-response.dto';
import { Community } from '../schemas/community.schema';

export class CommunityPaginatedResponseDto extends PaginatedResponseDto {
export class CommunityFindAllResponseDto extends PaginatedResponseDto {
@ApiResponseProperty({
type: [Community],
})
Expand Down
16 changes: 16 additions & 0 deletions packages/api/src/community/schemas/community.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { isValidCommunityName } from '../utils/is-valid-community-name';
import { isValidOwners } from '../utils/is-valid-owners';
import { isValidHostname } from '../utils/is-valid-hostname';
import mongoosePaginate from 'mongoose-paginate-v2';
import { Features } from '../types/features.type';

export type CommunityDocument = Community & Document;

Expand Down Expand Up @@ -144,6 +145,21 @@ export class Community {
required: true,
})
discordLinkState: string;

@ApiProperty({
example: {
attestations: true,
},
})
@IsOptional()
@Prop({
type: Object,
required: false,
default: {
attestations: false,
},
})
features?: Features;
}

export const CommunitySchema = SchemaFactory.createForClass(Community);
Expand Down
6 changes: 6 additions & 0 deletions packages/api/src/community/types/features.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* The features object is used to enable or disable features for a community.
*/
export type Features = {
attestations: boolean;
};
26 changes: 26 additions & 0 deletions packages/frontend/src/model/community/community.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { AxiosError, AxiosResponse } from 'axios';
import { selector } from 'recoil';
import { ApiGet, isResponseOk } from '../api';
import { Community } from './dto/community.dto';

export const CurrentCommunityQuery = selector({
key: 'CurrentCommunity',
get: ({ get }): AxiosResponse<Community> | AxiosError => {
return get(ApiGet({ url: '/communities/current' })) as
| AxiosResponse<Community>
| AxiosError;
},
});

export const CurrentCommunity = selector({
key: 'AllReports',
get: ({ get }): Community | undefined => {
const response = get(CurrentCommunityQuery);

if (isResponseOk(response)) {
return response.data;
}

return;
},
});
3 changes: 3 additions & 0 deletions packages/frontend/src/model/community/dto/community.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { components } from 'api-types';

export type Community = components['schemas']['Community'];
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { Box } from '@/components/ui/Box';
import { SettingsSubgroup } from './SettingsSubgroup';
import ApplicationSettingsApiKeys from './ApplicationSettingsApiKeys';
import { Setting as SettingDto } from '@/model/settings/dto/setting.dto';
import { useRecoilValue } from 'recoil';
import { CurrentCommunity } from '../../../model/community/community';
import { Jazzicon } from '@ukstv/jazzicon-react';

interface Params {
settings: SettingDto[] | undefined;
Expand All @@ -16,13 +19,55 @@ export const ApplicationSettings = ({
settings,
parentOnSubmit,
}: Params): JSX.Element | null => {
const community = useRecoilValue(CurrentCommunity);
if (!settings) return null;

return (
<>
<Box className="mb-6">
<SettingsSubgroup header="Application Settings">
<SettingsForm settings={settings} parentOnSubmit={parentOnSubmit} />
<>
<div className="flex flex-col gap-4 mb-4">
<div>
<label className="block font-bold group">Creator address</label>
<div className="mb-2 text-sm text-warm-gray-400">
The creator address is the address that represents this
community onchain. To be able to create attestations or
distribute tokens from this address, it should be a{' '}
<a
href="https://safe.global"
target="_blank"
rel="noreferrer"
>
Safe
</a>{' '}
address.
</div>
<div>
<Jazzicon
address={community?.creator || ''}
className="inline-block w-4"
/>{' '}
{community?.creator}
</div>
</div>
<div>
<label className="block font-bold group">Owner addresses</label>
<div className="mb-2 text-sm text-warm-gray-400">
The owner addresses represent the identities that will allways
have adin permissions in this Praise community.
</div>
<div>
{community?.owners.map((owner) => (
<div key={owner}>
<Jazzicon address={owner} className="inline-block w-4" />{' '}
{owner}
</div>
))}
</div>
</div>
</div>
<SettingsForm settings={settings} parentOnSubmit={parentOnSubmit} />
</>
</SettingsSubgroup>
</Box>
<Box className="mb-6">
Expand Down

0 comments on commit fb29d05

Please sign in to comment.