1use crate::errors::GumError;
2use crate::state::{Post, Profile, Reaction, ReactionType, User};
3
4use anchor_lang::prelude::*;
5use std::convert::AsRef;
6use std::str::FromStr;
7
8use crate::constants::*;
9use crate::events::{ReactionDeleted, ReactionNew};
10use gpl_session::{session_auth_or, Session, SessionError, SessionToken};
11
12#[derive(Accounts, Session)]
14#[instruction(reaction_type: String)]
15pub struct CreateReaction<'info> {
16 #[account(
18 init,
19 seeds = [
20 REACTION_PREFIX_SEED.as_bytes(),
21 reaction_type.as_bytes(),
22 to_post.to_account_info().key.as_ref(),
23 from_profile.to_account_info().key.as_ref(),
24 ],
25 bump,
26 payer = authority,
27 space = Reaction::LEN
28 )]
29 pub reaction: Account<'info, Reaction>,
30 #[account(
31 seeds = [
32 POST_PREFIX_SEED.as_bytes(),
33 to_post.random_hash.as_ref(),
34 ],
35 bump,
36 )]
37 pub to_post: Account<'info, Post>,
38 #[account(
39 seeds = [
40 PROFILE_PREFIX_SEED.as_bytes(),
41 from_profile.namespace.as_ref().as_bytes(),
42 user.to_account_info().key.as_ref(),
43 ],
44 bump,
45 has_one = user,
46 )]
47 pub from_profile: Account<'info, Profile>,
48 #[account(
49 seeds = [
50 USER_PREFIX_SEED.as_bytes(),
51 user.random_hash.as_ref(),
52 ],
53 bump,
54 )]
55 pub user: Account<'info, User>,
56
57 #[session(
58 signer = authority,
59 authority = user.authority.key()
60 )]
61 pub session_token: Option<Account<'info, SessionToken>>,
62
63 #[account(mut)]
64 pub authority: Signer<'info>,
65
66 pub system_program: Program<'info, System>,
68}
69
70#[session_auth_or(
72 ctx.accounts.user.authority.key() == ctx.accounts.authority.key(),
73 GumError::UnauthorizedSigner
74)]
75pub fn create_reaction_handler(ctx: Context<CreateReaction>, reaction_type: String) -> Result<()> {
76 let reaction = &mut ctx.accounts.reaction;
77 reaction.reaction_type = ReactionType::from_str(&reaction_type).unwrap();
78 reaction.to_post = *ctx.accounts.to_post.to_account_info().key;
79 reaction.from_profile = *ctx.accounts.from_profile.to_account_info().key;
80
81 emit!(ReactionNew {
83 reaction: *reaction.to_account_info().key,
84 reaction_type: reaction.reaction_type,
85 user: *ctx.accounts.user.to_account_info().key,
86 to_post: *ctx.accounts.to_post.to_account_info().key,
87 from_profile: *ctx.accounts.from_profile.to_account_info().key,
88 timestamp: Clock::get()?.unix_timestamp,
89 });
90
91 Ok(())
92}
93
94#[derive(Accounts, Session)]
96pub struct DeleteReaction<'info> {
97 #[account(
98 mut,
99 seeds = [
100 REACTION_PREFIX_SEED.as_bytes(),
101 reaction.reaction_type.as_ref().as_bytes(),
102 reaction.to_post.as_ref(),
103 reaction.from_profile.as_ref(),
104 ],
105 bump,
106 has_one = to_post,
107 has_one = from_profile,
108 close = refund_receiver,
109 )]
110 pub reaction: Account<'info, Reaction>,
111 #[account(
112 seeds = [
113 POST_PREFIX_SEED.as_bytes(),
114 to_post.random_hash.as_ref(),
115 ],
116 bump,
117 )]
118 pub to_post: Account<'info, Post>,
119 #[account(
120 seeds = [
121 PROFILE_PREFIX_SEED.as_bytes(),
122 from_profile.namespace.as_ref().as_bytes(),
123 user.to_account_info().key.as_ref(),
124 ],
125 bump,
126 has_one = user,
127 )]
128 pub from_profile: Account<'info, Profile>,
129 #[account(
130 seeds = [
131 USER_PREFIX_SEED.as_bytes(),
132 user.random_hash.as_ref(),
133 ],
134 bump,
135 )]
136 pub user: Account<'info, User>,
137
138 #[session(
139 signer = authority,
140 authority = user.authority.key()
141 )]
142 pub session_token: Option<Account<'info, SessionToken>>,
143 #[account(mut)]
144 pub authority: Signer<'info>,
145
146 #[account(mut, constraint = refund_receiver.key() == user.authority)]
147 pub refund_receiver: SystemAccount<'info>,
148
149 pub system_program: Program<'info, System>,
151}
152
153#[session_auth_or(
155 ctx.accounts.user.authority.key() == ctx.accounts.authority.key(),
156 GumError::UnauthorizedSigner
157)]
158pub fn delete_reaction_handler(ctx: Context<DeleteReaction>) -> Result<()> {
159 emit!(ReactionDeleted {
161 reaction: *ctx.accounts.reaction.to_account_info().key,
162 reaction_type: ctx.accounts.reaction.reaction_type,
163 user: *ctx.accounts.user.to_account_info().key,
164 to_post: *ctx.accounts.to_post.to_account_info().key,
165 from_profile: *ctx.accounts.from_profile.to_account_info().key,
166 timestamp: Clock::get()?.unix_timestamp,
167 });
168 Ok(())
169}