diff --git a/functions/graphql/nexus-typegen.ts b/functions/graphql/nexus-typegen.ts
index a2b816f..b659fbe 100644
--- a/functions/graphql/nexus-typegen.ts
+++ b/functions/graphql/nexus-typegen.ts
@@ -36,6 +36,19 @@ export interface NexusGenInputs {
title: string; // String!
topicId: number; // Int!
}
+ UpdateProfileInput: { // input type
+ avatar?: string | null; // String
+ bio?: string | null; // String
+ email?: string | null; // String
+ github?: string | null; // String
+ jobTitle?: string | null; // String
+ lightning_address?: string | null; // String
+ linkedin?: string | null; // String
+ location?: string | null; // String
+ name?: string | null; // String
+ twitter?: string | null; // String
+ website?: string | null; // String
+ }
}
export interface NexusGenEnums {
@@ -53,6 +66,12 @@ export interface NexusGenScalars {
}
export interface NexusGenObjects {
+ Author: { // root type
+ avatar: string; // String!
+ id: number; // Int!
+ join_date: NexusGenScalars['Date']; // Date!
+ name: string; // String!
+ }
Award: { // root type
id: number; // Int!
image: string; // String!
@@ -73,7 +92,7 @@ export interface NexusGenObjects {
votes_count: number; // Int!
}
BountyApplication: { // root type
- author: NexusGenRootTypes['User']; // User!
+ author: NexusGenRootTypes['Author']; // Author!
date: string; // String!
id: number; // Int!
workplan: string; // String!
@@ -116,7 +135,7 @@ export interface NexusGenObjects {
}
Mutation: {};
PostComment: { // root type
- author: NexusGenRootTypes['User']; // User!
+ author: NexusGenRootTypes['Author']; // Author!
body: string; // String!
createdAt: NexusGenScalars['Date']; // Date!
id: number; // Int!
@@ -165,9 +184,19 @@ export interface NexusGenObjects {
}
User: { // root type
avatar: string; // String!
+ bio?: string | null; // String
+ email?: string | null; // String
+ github?: string | null; // String
id: number; // Int!
+ jobTitle?: string | null; // String
join_date: NexusGenScalars['Date']; // Date!
+ lightning_address?: string | null; // String
+ linkedin?: string | null; // String
+ location?: string | null; // String
name: string; // String!
+ role?: string | null; // String
+ twitter?: string | null; // String
+ website?: string | null; // String
}
Vote: { // root type
amount_in_sat: number; // Int!
@@ -193,6 +222,12 @@ export type NexusGenRootTypes = NexusGenInterfaces & NexusGenObjects & NexusGenU
export type NexusGenAllTypes = NexusGenRootTypes & NexusGenScalars & NexusGenEnums
export interface NexusGenFieldTypes {
+ Author: { // field return type
+ avatar: string; // String!
+ id: number; // Int!
+ join_date: NexusGenScalars['Date']; // Date!
+ name: string; // String!
+ }
Award: { // field return type
id: number; // Int!
image: string; // String!
@@ -203,7 +238,7 @@ export interface NexusGenFieldTypes {
Bounty: { // field return type
applicants_count: number; // Int!
applications: NexusGenRootTypes['BountyApplication'][]; // [BountyApplication!]!
- author: NexusGenRootTypes['User']; // User!
+ author: NexusGenRootTypes['Author']; // Author!
body: string; // String!
cover_image: string; // String!
createdAt: NexusGenScalars['Date']; // Date!
@@ -217,7 +252,7 @@ export interface NexusGenFieldTypes {
votes_count: number; // Int!
}
BountyApplication: { // field return type
- author: NexusGenRootTypes['User']; // User!
+ author: NexusGenRootTypes['Author']; // Author!
date: string; // String!
id: number; // Int!
workplan: string; // String!
@@ -268,10 +303,11 @@ export interface NexusGenFieldTypes {
confirmVote: NexusGenRootTypes['Vote']; // Vote!
createStory: NexusGenRootTypes['Story'] | null; // Story
donate: NexusGenRootTypes['Donation']; // Donation!
+ updateProfile: NexusGenRootTypes['User'] | null; // User
vote: NexusGenRootTypes['Vote']; // Vote!
}
PostComment: { // field return type
- author: NexusGenRootTypes['User']; // User!
+ author: NexusGenRootTypes['Author']; // Author!
body: string; // String!
createdAt: NexusGenScalars['Date']; // Date!
id: number; // Int!
@@ -309,12 +345,13 @@ export interface NexusGenFieldTypes {
me: NexusGenRootTypes['User'] | null; // User
newProjects: NexusGenRootTypes['Project'][]; // [Project!]!
popularTopics: NexusGenRootTypes['Topic'][]; // [Topic!]!
+ profile: NexusGenRootTypes['User'] | null; // User
projectsByCategory: NexusGenRootTypes['Project'][]; // [Project!]!
searchProjects: NexusGenRootTypes['Project'][]; // [Project!]!
}
Question: { // field return type
answers_count: number; // Int!
- author: NexusGenRootTypes['User']; // User!
+ author: NexusGenRootTypes['Author']; // Author!
body: string; // String!
comments: NexusGenRootTypes['PostComment'][]; // [PostComment!]!
createdAt: NexusGenScalars['Date']; // Date!
@@ -326,7 +363,7 @@ export interface NexusGenFieldTypes {
votes_count: number; // Int!
}
Story: { // field return type
- author: NexusGenRootTypes['User']; // User!
+ author: NexusGenRootTypes['Author']; // Author!
body: string; // String!
comments: NexusGenRootTypes['PostComment'][]; // [PostComment!]!
comments_count: number; // Int!
@@ -351,9 +388,19 @@ export interface NexusGenFieldTypes {
}
User: { // field return type
avatar: string; // String!
+ bio: string | null; // String
+ email: string | null; // String
+ github: string | null; // String
id: number; // Int!
+ jobTitle: string | null; // String
join_date: NexusGenScalars['Date']; // Date!
+ lightning_address: string | null; // String
+ linkedin: string | null; // String
+ location: string | null; // String
name: string; // String!
+ role: string | null; // String
+ twitter: string | null; // String
+ website: string | null; // String
}
Vote: { // field return type
amount_in_sat: number; // Int!
@@ -375,6 +422,12 @@ export interface NexusGenFieldTypes {
}
export interface NexusGenFieldTypeNames {
+ Author: { // field return type name
+ avatar: 'String'
+ id: 'Int'
+ join_date: 'Date'
+ name: 'String'
+ }
Award: { // field return type name
id: 'Int'
image: 'String'
@@ -385,7 +438,7 @@ export interface NexusGenFieldTypeNames {
Bounty: { // field return type name
applicants_count: 'Int'
applications: 'BountyApplication'
- author: 'User'
+ author: 'Author'
body: 'String'
cover_image: 'String'
createdAt: 'Date'
@@ -399,7 +452,7 @@ export interface NexusGenFieldTypeNames {
votes_count: 'Int'
}
BountyApplication: { // field return type name
- author: 'User'
+ author: 'Author'
date: 'String'
id: 'Int'
workplan: 'String'
@@ -450,10 +503,11 @@ export interface NexusGenFieldTypeNames {
confirmVote: 'Vote'
createStory: 'Story'
donate: 'Donation'
+ updateProfile: 'User'
vote: 'Vote'
}
PostComment: { // field return type name
- author: 'User'
+ author: 'Author'
body: 'String'
createdAt: 'Date'
id: 'Int'
@@ -491,12 +545,13 @@ export interface NexusGenFieldTypeNames {
me: 'User'
newProjects: 'Project'
popularTopics: 'Topic'
+ profile: 'User'
projectsByCategory: 'Project'
searchProjects: 'Project'
}
Question: { // field return type name
answers_count: 'Int'
- author: 'User'
+ author: 'Author'
body: 'String'
comments: 'PostComment'
createdAt: 'Date'
@@ -508,7 +563,7 @@ export interface NexusGenFieldTypeNames {
votes_count: 'Int'
}
Story: { // field return type name
- author: 'User'
+ author: 'Author'
body: 'String'
comments: 'PostComment'
comments_count: 'Int'
@@ -533,9 +588,19 @@ export interface NexusGenFieldTypeNames {
}
User: { // field return type name
avatar: 'String'
+ bio: 'String'
+ email: 'String'
+ github: 'String'
id: 'Int'
+ jobTitle: 'String'
join_date: 'Date'
+ lightning_address: 'String'
+ linkedin: 'String'
+ location: 'String'
name: 'String'
+ role: 'String'
+ twitter: 'String'
+ website: 'String'
}
Vote: { // field return type name
amount_in_sat: 'Int'
@@ -572,6 +637,9 @@ export interface NexusGenArgTypes {
donate: { // args
amount_in_sat: number; // Int!
}
+ updateProfile: { // args
+ data?: NexusGenInputs['UpdateProfileInput'] | null; // UpdateProfileInput
+ }
vote: { // args
amount_in_sat: number; // Int!
item_id: number; // Int!
@@ -614,6 +682,9 @@ export interface NexusGenArgTypes {
skip?: number | null; // Int
take: number | null; // Int
}
+ profile: { // args
+ id: number; // Int!
+ }
projectsByCategory: { // args
category_id: number; // Int!
skip?: number | null; // Int
diff --git a/functions/graphql/schema.graphql b/functions/graphql/schema.graphql
index fe070c0..da0b861 100644
--- a/functions/graphql/schema.graphql
+++ b/functions/graphql/schema.graphql
@@ -2,6 +2,13 @@
### Do not make changes to this file directly
+type Author {
+ avatar: String!
+ id: Int!
+ join_date: Date!
+ name: String!
+}
+
type Award {
id: Int!
image: String!
@@ -13,7 +20,7 @@ type Award {
type Bounty implements PostBase {
applicants_count: Int!
applications: [BountyApplication!]!
- author: User!
+ author: Author!
body: String!
cover_image: String!
createdAt: Date!
@@ -28,7 +35,7 @@ type Bounty implements PostBase {
}
type BountyApplication {
- author: User!
+ author: Author!
date: String!
id: Int!
workplan: String!
@@ -88,6 +95,7 @@ type Mutation {
confirmVote(payment_request: String!, preimage: String!): Vote!
createStory(data: StoryInputType): Story
donate(amount_in_sat: Int!): Donation!
+ updateProfile(data: UpdateProfileInput): User
vote(amount_in_sat: Int!, item_id: Int!, item_type: VOTE_ITEM_TYPE!): Vote!
}
@@ -109,7 +117,7 @@ interface PostBase {
}
type PostComment {
- author: User!
+ author: Author!
body: String!
createdAt: Date!
id: Int!
@@ -149,13 +157,14 @@ type Query {
me: User
newProjects(skip: Int = 0, take: Int = 50): [Project!]!
popularTopics: [Topic!]!
+ profile(id: Int!): User
projectsByCategory(category_id: Int!, skip: Int = 0, take: Int = 10): [Project!]!
searchProjects(search: String!, skip: Int = 0, take: Int = 50): [Project!]!
}
type Question implements PostBase {
answers_count: Int!
- author: User!
+ author: Author!
body: String!
comments: [PostComment!]!
createdAt: Date!
@@ -168,7 +177,7 @@ type Question implements PostBase {
}
type Story implements PostBase {
- author: User!
+ author: Author!
body: String!
comments: [PostComment!]!
comments_count: Int!
@@ -203,11 +212,35 @@ type Topic {
title: String!
}
+input UpdateProfileInput {
+ avatar: String
+ bio: String
+ email: String
+ github: String
+ jobTitle: String
+ lightning_address: String
+ linkedin: String
+ location: String
+ name: String
+ twitter: String
+ website: String
+}
+
type User {
avatar: String!
+ bio: String
+ email: String
+ github: String
id: Int!
+ jobTitle: String
join_date: Date!
+ lightning_address: String
+ linkedin: String
+ location: String
name: String!
+ role: String
+ twitter: String
+ website: String
}
enum VOTE_ITEM_TYPE {
diff --git a/functions/graphql/types/helpers.js b/functions/graphql/types/helpers.js
index ece6a9f..c1f108d 100644
--- a/functions/graphql/types/helpers.js
+++ b/functions/graphql/types/helpers.js
@@ -67,10 +67,21 @@ const paginationArgs = (args) => {
}
}
+const removeNulls = (obj) => {
+ let res = {};
+ for (const key in obj) {
+ if (obj[key] != null) {
+ res[key] = obj[key];
+ }
+ }
+ return res
+}
+
module.exports = {
getPaymetRequestForItem,
hexToUint8Array,
lightningAddressToLnurl,
getLnurlDetails,
- paginationArgs
+ paginationArgs,
+ removeNulls
}
diff --git a/functions/graphql/types/post.js b/functions/graphql/types/post.js
index 613e480..7a85f67 100644
--- a/functions/graphql/types/post.js
+++ b/functions/graphql/types/post.js
@@ -39,6 +39,16 @@ const Topic = objectType({
}
})
+const Author = objectType({
+ name: 'Author',
+ definition(t) {
+ t.nonNull.int('id');
+ t.nonNull.string('name');
+ t.nonNull.string('avatar');
+ t.nonNull.date('join_date');
+ }
+})
+
const allTopics = extendType({
type: "Query",
@@ -129,7 +139,7 @@ const Story = objectType({
}
})
t.nonNull.field('author', {
- type: "User",
+ type: "Author",
resolve: (parent) =>
prisma.story.findUnique({ where: { id: parent.id } }).user()
@@ -214,7 +224,7 @@ const BountyApplication = objectType({
t.nonNull.string('date');
t.nonNull.string('workplan');
t.nonNull.field('author', {
- type: "User"
+ type: "Author"
});
}
})
@@ -234,7 +244,7 @@ const Bounty = objectType({
type: "BountyApplication"
});
t.nonNull.field('author', {
- type: "User",
+ type: "Author",
resolve: (parent) => {
return prisma.bounty.findUnique({ where: { id: parent.id } }).user();
}
@@ -270,7 +280,7 @@ const Question = objectType({
});
t.nonNull.field('author', {
- type: "User",
+ type: "Author",
resolve: (parent) => {
return prisma.question.findUnique({ where: { id: parent.id } }).user();
}
@@ -285,7 +295,7 @@ const PostComment = objectType({
t.nonNull.date('createdAt');
t.nonNull.string('body');
t.nonNull.field('author', {
- type: "User"
+ type: "Author"
});
t.int('parentId');
t.nonNull.int('votes_count');
@@ -391,6 +401,7 @@ const getPostById = extendType({
module.exports = {
// Types
POST_TYPE,
+ Author,
Topic,
PostBase,
BountyApplication,
diff --git a/functions/graphql/types/users.js b/functions/graphql/types/users.js
index 224b74b..30e25e4 100644
--- a/functions/graphql/types/users.js
+++ b/functions/graphql/types/users.js
@@ -1,5 +1,7 @@
-const { objectType, extendType } = require("nexus");
+const { prisma } = require("../../prisma");
+const { objectType, extendType, intArg, nonNull, inputObjectType } = require("nexus");
const { getUserByPubKey } = require("../../auth/utils/helperFuncs");
+const { removeNulls } = require("./helpers");
@@ -10,6 +12,16 @@ const User = objectType({
t.nonNull.string('name');
t.nonNull.string('avatar');
t.nonNull.date('join_date');
+ t.string('role');
+ t.string('email')
+ t.string('jobTitle')
+ t.string('lightning_address')
+ t.string('website')
+ t.string('twitter')
+ t.string('github')
+ t.string('linkedin')
+ t.string('bio')
+ t.string('location')
}
})
@@ -27,10 +39,77 @@ const me = extendType({
}
})
+const profile = extendType({
+ type: "Query",
+ definition(t) {
+ t.field('profile', {
+ type: "User",
+ args: {
+ id: nonNull(intArg())
+ },
+ async resolve(parent, { id }) {
+ return prisma.user.findFirst({
+ where: { id }
+ })
+ }
+ })
+ }
+})
+
+const UpdateProfileInput = inputObjectType({
+ name: 'UpdateProfileInput',
+ definition(t) {
+ t.string('name');
+ t.string('avatar');
+ t.string('email')
+ t.string('jobTitle')
+ t.string('lightning_address')
+ t.string('website')
+ t.string('twitter')
+ t.string('github')
+ t.string('linkedin')
+ t.string('bio')
+ t.string('location')
+ }
+})
+
+const updateProfile = extendType({
+ type: 'Mutation',
+ definition(t) {
+ t.field('updateProfile', {
+ type: 'User',
+ args: { data: UpdateProfileInput },
+ async resolve(_root, args, ctx) {
+ const user = await getUserByPubKey(ctx.userPubKey);
+
+ // Do some validation
+ if (!user)
+ throw new Error("You have to login");
+ // TODO: validate new data
+
+
+ // Preprocess & insert
+
+ return prisma.user.update({
+ where: {
+ id: user.id,
+ },
+ data: removeNulls(args.data)
+ })
+ }
+ })
+ },
+})
+
+
+
module.exports = {
// Types
User,
-
+ UpdateProfileInput,
// Queries
me,
+ profile,
+ // Mutations
+ updateProfile,
}
\ No newline at end of file
diff --git a/prisma/migrations/20220604085918_add_new_cols_to_user/migration.sql b/prisma/migrations/20220604085918_add_new_cols_to_user/migration.sql
new file mode 100644
index 0000000..26cd661
--- /dev/null
+++ b/prisma/migrations/20220604085918_add_new_cols_to_user/migration.sql
@@ -0,0 +1,6 @@
+-- AlterTable
+ALTER TABLE "User" ADD COLUMN "bio" TEXT,
+ADD COLUMN "github" TEXT,
+ADD COLUMN "location" TEXT,
+ADD COLUMN "role" TEXT NOT NULL DEFAULT E'user',
+ADD COLUMN "twitter" TEXT;
diff --git a/prisma/migrations/20220604110334_add_more_cols_to_user_table/migration.sql b/prisma/migrations/20220604110334_add_more_cols_to_user_table/migration.sql
new file mode 100644
index 0000000..330d7b9
--- /dev/null
+++ b/prisma/migrations/20220604110334_add_more_cols_to_user_table/migration.sql
@@ -0,0 +1,3 @@
+-- AlterTable
+ALTER TABLE "User" ADD COLUMN "jobTitle" TEXT,
+ADD COLUMN "linkedin" TEXT;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 6022e5a..df119fb 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -38,13 +38,23 @@ model Vote {
model User {
id Int @id @default(autoincrement())
pubKey String? @unique
+ name String? @unique
+ avatar String?
+ role String @default("user")
+
- name String? @unique
email String?
- website String?
+ jobTitle String?
lightning_address String?
- avatar String?
- join_date DateTime @default(now())
+ website String?
+ twitter String?
+ github String?
+ linkedin String?
+ bio String?
+ location String?
+
+
+ join_date DateTime @default(now())
stories Story[]
questions Question[]
diff --git a/src/App.tsx b/src/App.tsx
index c225da3..24f8428 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -25,6 +25,7 @@ const HackathonsPage = React.lazy(() => import("./features/Hackathons/pages/Hack
const DonatePage = React.lazy(() => import("./features/Donations/pages/DonatePage/DonatePage"))
const LoginPage = React.lazy(() => import("./features/Auth/pages/LoginPage/LoginPage"))
const LogoutPage = React.lazy(() => import("./features/Auth/pages/LogoutPage/LogoutPage"))
+const ProfilePage = React.lazy(() => import("./features/Profiles/pages/ProfilePage/ProfilePage"))
function App() {
const { isWalletConnected } = useAppSelector(state => ({
@@ -77,6 +78,7 @@ function App() {