Skip to content

Commit

Permalink
Merge pull request #1011 from 0x1-company/invite-profile
Browse files Browse the repository at this point in the history
  • Loading branch information
tomokisun authored Nov 22, 2023
2 parents 2a42e1e + c8f96ad commit 030095e
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 5 deletions.
4 changes: 4 additions & 0 deletions GraphQL/Queries/Profile.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ query CurrentUserProfile {
coinBalance
}
}
invitationCode {
id
code
}
friends {
...FriendFragment
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,6 @@ public extension AnalyticsClient {
case getHelp = "get_help"
case safetyCenter = "safety_center"
case requiredInviteFriend = "required_invite_friend"
case invitationCodeCopy = "invitation_code_copy"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public extension God {
public static let operationName: String = "CurrentUserProfile"
public static let operationDocument: ApolloAPI.OperationDocument = .init(
definition: .init(
#"query CurrentUserProfile { currentUser { __typename ...ProfileSectionFragment wallet { __typename id coinBalance } } friends { __typename ...FriendFragment } questionsOrderByVotedDesc(first: 3) { __typename id imageURL text { __typename ja } } }"#,
#"query CurrentUserProfile { currentUser { __typename ...ProfileSectionFragment wallet { __typename id coinBalance } } invitationCode { __typename id code } friends { __typename ...FriendFragment } questionsOrderByVotedDesc(first: 3) { __typename id imageURL text { __typename ja } } }"#,
fragments: [ProfileSectionFragment.self, FriendFragment.self]
))

Expand All @@ -21,12 +21,15 @@ public extension God {
public static var __parentType: ApolloAPI.ParentType { God.Objects.Query }
public static var __selections: [ApolloAPI.Selection] { [
.field("currentUser", CurrentUser.self),
.field("invitationCode", InvitationCode.self),
.field("friends", [Friend].self),
.field("questionsOrderByVotedDesc", [QuestionsOrderByVotedDesc].self, arguments: ["first": 3]),
] }

/// ログイン中ユーザーを取得
public var currentUser: CurrentUser { __data["currentUser"] }
/// 招待コードを取得
public var invitationCode: InvitationCode { __data["invitationCode"] }
/// フレンドの一覧
public var friends: [Friend] { __data["friends"] }
public var questionsOrderByVotedDesc: [QuestionsOrderByVotedDesc] { __data["questionsOrderByVotedDesc"] }
Expand Down Expand Up @@ -98,6 +101,25 @@ public extension God {
}
}

/// InvitationCode
///
/// Parent Type: `InvitationCode`
public struct InvitationCode: God.SelectionSet {
public let __data: DataDict
public init(_dataDict: DataDict) { __data = _dataDict }

public static var __parentType: ApolloAPI.ParentType { God.Objects.InvitationCode }
public static var __selections: [ApolloAPI.Selection] { [
.field("__typename", String.self),
.field("id", God.ID.self),
.field("code", String.self),
] }

public var id: God.ID { __data["id"] }
/// 招待コード
public var code: String { __data["code"] }
}

/// Friend
///
/// Parent Type: `User`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @generated
// This file was automatically generated and should not be edited.

import ApolloAPI

public extension God.Objects {
static let InvitationCode = Object(
typename: "InvitationCode",
implementedInterfaces: []
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public extension God {
case "Query": return God.Objects.Query
case "School": return God.Objects.School
case "Wallet": return God.Objects.Wallet
case "InvitationCode": return God.Objects.InvitationCode
case "Question": return God.Objects.Question
case "Store": return God.Objects.Store
case "StoreItem": return God.Objects.StoreItem
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @generated
// This file was automatically generated and should not be edited.

import ApolloTestSupport
import God

public class InvitationCode: MockObject {
public static let objectType: Object = God.Objects.InvitationCode
public static let _mockFields = MockFields()
public typealias MockValueCollectionType = Array<Mock<InvitationCode>>

public struct MockFields {
@Field<String>("code") public var code
@Field<God.ID>("id") public var id
}
}

public extension Mock where O == InvitationCode {
convenience init(
code: String? = nil,
id: God.ID? = nil
) {
self.init()
_setScalar(code, for: \.code)
_setScalar(id, for: \.id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class Query: MockObject {
@Field<[User]>("friends") public var friends
@Field<UserConnection>("friendsOfFriends") public var friendsOfFriends
@Field<InboxActivity>("inboxActivity") public var inboxActivity
@Field<InvitationCode>("invitationCode") public var invitationCode
@Field<ActivityConnection>("listActivities") public var listActivities
@Field<InboxActivityConnection>("listInboxActivities") public var listInboxActivities
@Field<[Question]>("questionsOrderByVotedDesc") public var questionsOrderByVotedDesc
Expand All @@ -42,6 +43,7 @@ public extension Mock where O == Query {
friends: [Mock<User>]? = nil,
friendsOfFriends: Mock<UserConnection>? = nil,
inboxActivity: Mock<InboxActivity>? = nil,
invitationCode: Mock<InvitationCode>? = nil,
listActivities: Mock<ActivityConnection>? = nil,
listInboxActivities: Mock<InboxActivityConnection>? = nil,
questionsOrderByVotedDesc: [Mock<Question>]? = nil,
Expand All @@ -62,6 +64,7 @@ public extension Mock where O == Query {
_setList(friends, for: \.friends)
_setEntity(friendsOfFriends, for: \.friendsOfFriends)
_setEntity(inboxActivity, for: \.inboxActivity)
_setEntity(invitationCode, for: \.invitationCode)
_setEntity(listActivities, for: \.listActivities)
_setEntity(listInboxActivities, for: \.listInboxActivities)
_setList(questionsOrderByVotedDesc, for: \.questionsOrderByVotedDesc)
Expand Down
1 change: 1 addition & 0 deletions Packages/GodPackage/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ let package = Package(
"ProfileShareFeature",
.product(name: "AsyncValue", package: "DependencyPackage"),
.product(name: "AnalyticsClient", package: "DependencyPackage"),
.product(name: "UIPasteboardClient", package: "CupertinoPackage"),
.product(name: "CachedAsyncImage", package: "swiftui-cached-async-image"),
]),
.target(name: "ProfileShareFeature", dependencies: [
Expand Down
30 changes: 30 additions & 0 deletions Packages/GodPackage/Sources/ProfileFeature/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@
}
}
},
"Copied the invitation code." : {
"localizations" : {
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "招待コードをコピーしました。"
}
}
}
},
"Does not go to my school" : {
"localizations" : {
"ja" : {
Expand Down Expand Up @@ -134,6 +144,16 @@
}
}
},
"Invite friend" : {
"localizations" : {
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "友だちを招待する"
}
}
}
},
"May be under 13 years old" : {
"localizations" : {
"ja" : {
Expand All @@ -144,6 +164,16 @@
}
}
},
"OK" : {
"localizations" : {
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "OK"
}
}
}
},
"Other" : {
"localizations" : {
"ja" : {
Expand Down
46 changes: 43 additions & 3 deletions Packages/GodPackage/Sources/ProfileFeature/Profile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ProfileEditFeature
import ProfileShareFeature
import ShopFeature
import Styleguide
import UIPasteboardClient
import SwiftUI

@Reducer
Expand All @@ -24,6 +25,7 @@ public struct ProfileLogic {
case editProfileButtonTapped
case shareProfileButtonTapped
case shopButtonTapped
case invitationCodeCopyButtonTapped
case friendButtonTapped(userId: String)
case friendEmptyButtonTapped
case profileResponse(TaskResult<God.CurrentUserProfileQuery.Data>)
Expand All @@ -32,6 +34,7 @@ public struct ProfileLogic {

@Dependency(\.analytics) var analytics
@Dependency(\.godClient) var godClient
@Dependency(\.pasteboard) var pasteboard

enum Cancel {
case profile
Expand Down Expand Up @@ -68,6 +71,24 @@ public struct ProfileLogic {
analytics.buttonClick(name: .shop)
state.destination = .shop()
return .none

case .invitationCodeCopyButtonTapped:
guard let code = state.profile?.invitationCode.code
else { return .none }

state.destination = .alert(
AlertState {
TextState("Copied the invitation code.", bundle: .module)
} actions: {
ButtonState(action: .confirmOkay) {
TextState("OK", bundle: .module)
}
}
)
return .run { _ in
analytics.buttonClick(name: .invitationCodeCopy)
pasteboard.string(code)
}

case let .friendButtonTapped(userId):
state.destination = .external(.init(userId: userId))
Expand Down Expand Up @@ -96,6 +117,10 @@ public struct ProfileLogic {
await currentUserRequest(send: send)
}
}

case .destination(.presented(.alert(.confirmOkay))):
state.destination = nil
return .none

case .destination(.dismiss):
state.destination = nil
Expand Down Expand Up @@ -127,13 +152,19 @@ public struct ProfileLogic {
case shop(ShopLogic.State = .init())
case profileShare(ProfileShareLogic.State = .init())
case external(ProfileExternalLogic.State)
case alert(AlertState<Action.Alert>)
}

public enum Action {
case profileEdit(ProfileEditLogic.Action)
case shop(ShopLogic.Action)
case profileShare(ProfileShareLogic.Action)
case external(ProfileExternalLogic.Action)
case alert(Alert)

public enum Alert: Equatable {
case confirmOkay
}
}

public var body: some Reducer<State, Action> {
Expand Down Expand Up @@ -180,10 +211,14 @@ public struct ProfileView: View {
}

Divider()

ShareShopSection(
InviteSection(
coinBalance: data.currentUser.wallet?.coinBalance ?? 0,
shareAction: {
code: data.invitationCode.code,
codeCopyAction: {
store.send(.invitationCodeCopyButtonTapped)
},
inviteFriendAction: {
store.send(.shareProfileButtonTapped)
},
shopAction: {
Expand Down Expand Up @@ -218,6 +253,11 @@ public struct ProfileView: View {
.navigationBarTitleDisplayMode(.inline)
.task { await store.send(.onTask).finish() }
.onAppear { store.send(.onAppear) }
.alert(
store: store.scope(state: \.$destination, action: ProfileLogic.Action.destination),
state: /ProfileLogic.Destination.State.alert,
action: ProfileLogic.Destination.Action.alert
)
.sheet(
store: store.scope(state: \.$destination, action: ProfileLogic.Action.destination),
state: /ProfileLogic.Destination.State.profileEdit,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import SwiftUI
import Styleguide

struct InviteSection: View {
let coinBalance: Int
let code: String
let codeCopyAction: () -> Void
let inviteFriendAction: () -> Void
let shopAction: () -> Void

var body: some View {
VStack(spacing: 16) {
VStack(spacing: 16) {
InviteTicket(code: code)
.onTapGesture(perform: codeCopyAction)

Button(action: inviteFriendAction) {
Label {
Text("Invite friend", bundle: .module)
} icon: {
Image(systemName: "square.and.arrow.up")
}
.font(.system(.title3, design: .rounded, weight: .bold))
.frame(height: 56)
.frame(maxWidth: .infinity)
.foregroundStyle(Color.white)
.background(Color.godBlack)
.clipShape(Capsule())
}
}
.padding(.vertical, 12)
.padding(.horizontal, 16)
.background(Color.white)
.cornerRadius(16)

Button(action: shopAction) {
HStack(spacing: 8) {
Text(coinBalance.description)
.font(.system(.title2, design: .rounded, weight: .bold))
.foregroundStyle(Color.godBlack)

Text("SHOP", bundle: .module)
.font(.system(.caption, design: .rounded, weight: .bold))
.frame(width: 57, height: 26)
.foregroundStyle(Color.white)
.background(Color.godYellow.gradient)
.clipShape(Capsule())
}
.frame(height: 52)
.frame(maxWidth: .infinity)
.background(Color(uiColor: UIColor.secondarySystemBackground))
.overlay(
RoundedRectangle(cornerRadius: 52 / 2)
.stroke(Color.godTextSecondaryLight, lineWidth: 1)
)
.overlay(alignment: .top) {
Text("COINS", bundle: .module)
.font(.system(.caption, design: .rounded, weight: .bold))
.padding(.horizontal, 8)
.foregroundStyle(Color.godTextSecondaryLight)
.background(Color(uiColor: UIColor.secondarySystemBackground))
.offset(y: -7)
}
}
}
.padding(.all, 16)
.background(Color(uiColor: UIColor.secondarySystemBackground))
.buttonStyle(HoldDownButtonStyle())
}
}

#Preview {
InviteSection(
coinBalance: 100,
code: "ABCDEF",
codeCopyAction: {},
inviteFriendAction: {},
shopAction: {}
)
}
Loading

0 comments on commit 030095e

Please sign in to comment.