add Invites page

This commit is contained in:
Akinwale Ariwodola 2020-05-11 19:38:21 +01:00
parent bc2e8b7ec0
commit b4599ec7c5
34 changed files with 1451 additions and 42 deletions

View file

@ -65,7 +65,6 @@ import java.io.BufferedReader;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.ConnectException; import java.net.ConnectException;
import java.nio.channels.Channel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -95,7 +94,7 @@ import io.lbry.browser.model.lbryinc.Reward;
import io.lbry.browser.model.lbryinc.Subscription; import io.lbry.browser.model.lbryinc.Subscription;
import io.lbry.browser.tasks.ClaimListResultHandler; import io.lbry.browser.tasks.ClaimListResultHandler;
import io.lbry.browser.tasks.ClaimListTask; import io.lbry.browser.tasks.ClaimListTask;
import io.lbry.browser.tasks.FetchRewardsTask; import io.lbry.browser.tasks.lbryinc.FetchRewardsTask;
import io.lbry.browser.tasks.LighthouseAutoCompleteTask; import io.lbry.browser.tasks.LighthouseAutoCompleteTask;
import io.lbry.browser.tasks.MergeSubscriptionsTask; import io.lbry.browser.tasks.MergeSubscriptionsTask;
import io.lbry.browser.tasks.ResolveTask; import io.lbry.browser.tasks.ResolveTask;
@ -115,6 +114,7 @@ import io.lbry.browser.ui.following.FollowingFragment;
import io.lbry.browser.ui.search.SearchFragment; import io.lbry.browser.ui.search.SearchFragment;
import io.lbry.browser.ui.settings.SettingsFragment; import io.lbry.browser.ui.settings.SettingsFragment;
import io.lbry.browser.ui.allcontent.AllContentFragment; import io.lbry.browser.ui.allcontent.AllContentFragment;
import io.lbry.browser.ui.wallet.InvitesFragment;
import io.lbry.browser.ui.wallet.RewardsFragment; import io.lbry.browser.ui.wallet.RewardsFragment;
import io.lbry.browser.ui.wallet.WalletFragment; import io.lbry.browser.ui.wallet.WalletFragment;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
@ -229,6 +229,16 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
private String pendingChannelUrl; private String pendingChannelUrl;
private boolean pendingFollowingReload; private boolean pendingFollowingReload;
// startup stages (to be able to determine how far a user made it if startup fails)
// and display a more useful message for troubleshooting
private static final int STARTUP_STAGE_INSTALL_ID_LOADED = 1;
private static final int STARTUP_STAGE_KNOWN_TAGS_LOADED = 2;
private static final int STARTUP_STAGE_EXCHANGE_RATE_LOADED = 3;
private static final int STARTUP_STAGE_USER_AUTHENTICATED = 4;
private static final int STARTUP_STAGE_NEW_INSTALL_DONE = 5;
private static final int STARTUP_STAGE_SUBSCRIPTIONS_LOADED = 6;
private static final int STARTUP_STAGE_SUBSCRIPTIONS_RESOLVED = 7;
private final List<Integer> supportedMenuItemIds = Arrays.asList( private final List<Integer> supportedMenuItemIds = Arrays.asList(
// find content // find content
NavMenuItem.ID_ITEM_FOLLOWING, NavMenuItem.ID_ITEM_FOLLOWING,
@ -241,7 +251,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
// wallet // wallet
NavMenuItem.ID_ITEM_WALLET, NavMenuItem.ID_ITEM_WALLET,
NavMenuItem.ID_ITEM_REWARDS, NavMenuItem.ID_ITEM_REWARDS,
NavMenuItem.ID_ITEM_INVITES,
NavMenuItem.ID_ITEM_SETTINGS NavMenuItem.ID_ITEM_SETTINGS
); );
@ -432,6 +442,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
case NavMenuItem.ID_ITEM_REWARDS: case NavMenuItem.ID_ITEM_REWARDS:
openFragment(RewardsFragment.class, true, NavMenuItem.ID_ITEM_REWARDS); openFragment(RewardsFragment.class, true, NavMenuItem.ID_ITEM_REWARDS);
break; break;
case NavMenuItem.ID_ITEM_INVITES:
openFragment(InvitesFragment.class, true, NavMenuItem.ID_ITEM_INVITES);
break;
case NavMenuItem.ID_ITEM_SETTINGS: case NavMenuItem.ID_ITEM_SETTINGS:
openFragment(SettingsFragment.class, true, NavMenuItem.ID_ITEM_SETTINGS); openFragment(SettingsFragment.class, true, NavMenuItem.ID_ITEM_SETTINGS);
@ -1471,6 +1484,11 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
if (Lbryio.currentUser == null) { if (Lbryio.currentUser == null) {
Lbryio.authenticate(context); Lbryio.authenticate(context);
} }
if (Lbryio.currentUser == null) {
throw new Exception("Did not retrieve authenticated user.");
}
Lbryio.newInstall(context); Lbryio.newInstall(context);
// (light) fetch subscriptions // (light) fetch subscriptions
@ -1546,8 +1564,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
} }
if (Lbryio.totalUnclaimedRewardAmount > 0) { if (Lbryio.totalUnclaimedRewardAmount > 0) {
((TextView) findViewById(R.id.floating_reward_value)).setText(Helper.shortCurrencyFormat(Lbryio.totalUnclaimedRewardAmount)); showFloatingUnclaimedRewards();
findViewById(R.id.floating_reward_container).setVisibility(View.VISIBLE);
} }
} }
@ -1558,6 +1575,11 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
public void showFloatingUnclaimedRewards() {
((TextView) findViewById(R.id.floating_reward_value)).setText(Helper.shortCurrencyFormat(Lbryio.totalUnclaimedRewardAmount));
findViewById(R.id.floating_reward_container).setVisibility(View.VISIBLE);
}
private void checkUrlIntent(Intent intent) { private void checkUrlIntent(Intent intent) {
if (intent != null) { if (intent != null) {
Uri data = intent.getData(); Uri data = intent.getData();

View file

@ -1,6 +1,5 @@
package io.lbry.browser; package io.lbry.browser;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.os.AsyncTask; import android.os.AsyncTask;
@ -8,27 +7,17 @@ import android.os.Bundle;
import android.view.View; import android.view.View;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.viewpager.widget.ViewPager;
import androidx.viewpager2.widget.ViewPager2; import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import io.lbry.browser.adapter.ClaimListAdapter;
import io.lbry.browser.adapter.VerificationPagerAdapter; import io.lbry.browser.adapter.VerificationPagerAdapter;
import io.lbry.browser.listener.SignInListener; import io.lbry.browser.listener.SignInListener;
import io.lbry.browser.listener.WalletSyncListener; import io.lbry.browser.listener.WalletSyncListener;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.lbryinc.User; import io.lbry.browser.model.lbryinc.User;
import io.lbry.browser.tasks.ClaimListResultHandler; import io.lbry.browser.tasks.lbryinc.FetchCurrentUserTask;
import io.lbry.browser.tasks.ClaimListTask;
import io.lbry.browser.tasks.FetchCurrentUserTask;
import io.lbry.browser.ui.channel.ChannelManagerFragment;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry;
import io.lbry.browser.utils.Lbryio; import io.lbry.browser.utils.Lbryio;
public class VerificationActivity extends FragmentActivity implements SignInListener, WalletSyncListener { public class VerificationActivity extends FragmentActivity implements SignInListener, WalletSyncListener {

View file

@ -0,0 +1,70 @@
package io.lbry.browser.adapter;
import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import io.lbry.browser.R;
import io.lbry.browser.model.Claim;
public class InlineChannelSpinnerAdapter extends ArrayAdapter<Claim> {
private List<Claim> channels;
private int layoutResourceId;
private LayoutInflater inflater;
public InlineChannelSpinnerAdapter(Context context, int resource, List<Claim> channels) {
super(context, resource, 0, channels);
inflater = LayoutInflater.from(context);
layoutResourceId = resource;
this.channels = new ArrayList<>(channels);
}
public void addPlaceholder(boolean includeAnonymous) {
Claim placeholder = new Claim();
placeholder.setPlaceholder(true);
insert(placeholder, 0);
channels.add(0, placeholder);
if (includeAnonymous) {
Claim anonymous = new Claim();
anonymous.setPlaceholderAnonymous(true);
insert(anonymous, 1);
channels.add(1, anonymous);
}
}
@Override
public View getDropDownView(int position, View view, ViewGroup parent) {
return createView(position, view, parent);
}
@Override
public View getView(int position, View view, ViewGroup parent) {
return createView(position, view, parent);
}
private View createView(int position, View convertView, ViewGroup parent){
View view = inflater.inflate(layoutResourceId, parent, false);
Context context = getContext();
Claim channel = getItem(position);
String name = channel.getName();
if (channel.isPlaceholder()) {
name = context.getString(R.string.create_a_channel);
} else if (channel.isPlaceholderAnonymous()) {
name = context.getString(R.string.anonymous);
}
TextView label = view.findViewById(R.id.channel_item_name);
label.setText(name);
return view;
}
}

View file

@ -0,0 +1,92 @@
package io.lbry.browser.adapter;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import io.lbry.browser.R;
import io.lbry.browser.model.lbryinc.Invitee;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryUri;
import lombok.Setter;
public class InviteeListAdapter extends RecyclerView.Adapter<InviteeListAdapter.ViewHolder> {
private Context context;
private List<Invitee> items;
public InviteeListAdapter(List<Invitee> invitees, Context context) {
this.context = context;
this.items = new ArrayList<>(invitees);
}
public void clear() {
items.clear();
notifyDataSetChanged();
}
public List<Invitee> getItems() {
return new ArrayList<>(items);
}
public void addHeader() {
Invitee header = new Invitee();
header.setHeader(true);
items.add(0, header);
}
public void addInvitees(List<Invitee> Invitees) {
for (Invitee tx : Invitees) {
if (!items.contains(tx)) {
items.add(tx);
}
}
notifyDataSetChanged();
}
public int getItemCount() {
return items != null ? items.size() : 0;
}
@Override
public InviteeListAdapter.ViewHolder onCreateViewHolder(ViewGroup root, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.list_item_invitee, root, false);
return new InviteeListAdapter.ViewHolder(v);
}
@Override
public void onBindViewHolder(InviteeListAdapter.ViewHolder vh, int position) {
Invitee item = items.get(position);
vh.emailView.setText(item.isHeader() ? context.getString(R.string.email) : item.getEmail());
vh.emailView.setTypeface(null, item.isHeader() ? Typeface.BOLD : Typeface.NORMAL);
String rewardText = context.getString(
item.isInviteRewardClaimed() ? R.string.claimed :
(item.isInviteRewardClaimable() ? R.string.claimable : R.string.unclaimable));
vh.rewardView.setText(item.isHeader() ? context.getString(R.string.reward) : rewardText);
vh.rewardView.setTypeface(null, item.isHeader() ? Typeface.BOLD : Typeface.NORMAL);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
protected TextView emailView;
protected TextView rewardView;
public ViewHolder(View v) {
super(v);
emailView = v.findViewById(R.id.invitee_email);
rewardView = v.findViewById(R.id.invitee_reward);
}
}
}

View file

@ -138,14 +138,13 @@ public class RewardListAdapter extends RecyclerView.Adapter<RewardListAdapter.Vi
if (!"?".equals(displayAmount)) { if (!"?".equals(displayAmount)) {
rewardAmount = Double.valueOf(displayAmount); rewardAmount = Double.valueOf(displayAmount);
} }
boolean hasTransaction = !Helper.isNullOrEmpty(reward.getTransactionId()); boolean hasTransaction = !Helper.isNullOrEmpty(reward.getTransactionId()) && reward.getTransactionId().length() > 7;
vh.iconClaimed.setVisibility(reward.isClaimed() ? View.VISIBLE : View.INVISIBLE); vh.iconClaimed.setVisibility(reward.isClaimed() ? View.VISIBLE : View.INVISIBLE);
vh.inputCustomCode.setVisibility(reward.isCustom() ? View.VISIBLE : View.GONE); vh.inputCustomCode.setVisibility(reward.isCustom() ? View.VISIBLE : View.GONE);
vh.buttonClaimCustom.setVisibility(reward.isCustom() ? View.VISIBLE : View.GONE); vh.buttonClaimCustom.setVisibility(reward.isCustom() ? View.VISIBLE : View.GONE);
vh.textTitle.setText(reward.getRewardTitle()); vh.textTitle.setText(reward.getRewardTitle());
vh.textDescription.setText(reward.getRewardDescription()); vh.textDescription.setText(reward.getRewardDescription());
vh.upTo.setVisibility(reward.shouldDisplayRange() ? View.VISIBLE : View.INVISIBLE); vh.upTo.setVisibility(reward.shouldDisplayRange() ? View.VISIBLE : View.GONE);
vh.textLbcValue.setText(reward.isCustom() ? "?" : Helper.LBC_CURRENCY_FORMAT.format(Helper.parseDouble(reward.getDisplayAmount(), 0))); vh.textLbcValue.setText(reward.isCustom() ? "?" : Helper.LBC_CURRENCY_FORMAT.format(Helper.parseDouble(reward.getDisplayAmount(), 0)));
vh.textLinkTransaction.setVisibility(hasTransaction ? View.VISIBLE : View.GONE); vh.textLinkTransaction.setVisibility(hasTransaction ? View.VISIBLE : View.GONE);
vh.textLinkTransaction.setText(hasTransaction ? reward.getTransactionId().substring(0, 7) : null); vh.textLinkTransaction.setText(hasTransaction ? reward.getTransactionId().substring(0, 7) : null);

View file

@ -51,6 +51,7 @@ public class Claim {
@EqualsAndHashCode.Include @EqualsAndHashCode.Include
private boolean placeholder; private boolean placeholder;
private boolean placeholderAnonymous;
private boolean featured; private boolean featured;
private boolean unresolved; // used for featured private boolean unresolved; // used for featured
private String address; private String address;

View file

@ -41,6 +41,7 @@ public class NavMenuItem {
private boolean group; private boolean group;
private int icon; private int icon;
private String title; private String title;
private String extraLabel;
private String name; // same as title, but only as en lang for events private String name; // same as title, but only as en lang for events
private List<NavMenuItem> items; private List<NavMenuItem> items;

View file

@ -0,0 +1,11 @@
package io.lbry.browser.model.lbryinc;
import lombok.Data;
@Data
public class Invitee {
private boolean header;
private String email;
private boolean inviteRewardClaimed;
private boolean inviteRewardClaimable;
}

View file

@ -1,4 +1,4 @@
package io.lbry.browser.tasks; package io.lbry.browser.tasks.lbryinc;
import android.content.Context; import android.content.Context;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;

View file

@ -1,4 +1,4 @@
package io.lbry.browser.tasks; package io.lbry.browser.tasks.lbryinc;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;

View file

@ -1,4 +1,4 @@
package io.lbry.browser.tasks; package io.lbry.browser.tasks.lbryinc;
import android.os.AsyncTask; import android.os.AsyncTask;

View file

@ -0,0 +1,72 @@
package io.lbry.browser.tasks.lbryinc;
import android.os.AsyncTask;
import android.view.View;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import io.lbry.browser.exceptions.LbryioRequestException;
import io.lbry.browser.exceptions.LbryioResponseException;
import io.lbry.browser.model.lbryinc.Invitee;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbryio;
public class FetchInviteStatusTask extends AsyncTask<Void, Void, List<Invitee>> {
private FetchInviteStatusHandler handler;
private View progressView;
private Exception error;
public FetchInviteStatusTask(View progressView, FetchInviteStatusHandler handler) {
this.progressView = progressView;
this.handler = handler;
}
protected void onPreExecute() {
Helper.setViewVisibility(progressView, View.VISIBLE);
}
protected List<Invitee> doInBackground(Void... params) {
List<Invitee> invitees = null;
try {
JSONObject status = (JSONObject) Lbryio.parseResponse(Lbryio.call("user", "invite_status", null, null));
JSONArray inviteesArray = status.getJSONArray("invitees");
invitees = new ArrayList<>();
for (int i = 0; i < inviteesArray.length(); i++) {
JSONObject inviteeObject = inviteesArray.getJSONObject(i);
Invitee invitee = new Invitee();
invitee.setEmail(Helper.getJSONString("email", null, inviteeObject));
invitee.setInviteRewardClaimable(Helper.getJSONBoolean("invite_reward_claimable", false, inviteeObject));
invitee.setInviteRewardClaimed(Helper.getJSONBoolean("invite_reward_claimed", false, inviteeObject));
if (!Helper.isNullOrEmpty(invitee.getEmail())) {
invitees.add(invitee);
}
}
} catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException ex) {
error = ex;
}
return invitees;
}
protected void onPostExecute(List<Invitee> invitees) {
Helper.setViewVisibility(progressView, View.GONE);
if (handler != null) {
if (invitees != null) {
handler.onSuccess(invitees);
} else {
handler.onError(error);
}
}
}
public interface FetchInviteStatusHandler {
void onSuccess(List<Invitee> invitees);
void onError(Exception error);
}
}

View file

@ -0,0 +1,57 @@
package io.lbry.browser.tasks.lbryinc;
import android.os.AsyncTask;
import android.view.View;
import org.json.JSONArray;
import org.json.JSONException;
import io.lbry.browser.exceptions.LbryioRequestException;
import io.lbry.browser.exceptions.LbryioResponseException;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbryio;
public class FetchReferralCodeTask extends AsyncTask<Void, Void, String> {
private FetchReferralCodeHandler handler;
private View progressView;
private Exception error;
public FetchReferralCodeTask(View progressView, FetchReferralCodeHandler handler) {
this.progressView = progressView;
this.handler = handler;
}
protected void onPreExecute() {
Helper.setViewVisibility(progressView, View.VISIBLE);
}
protected String doInBackground(Void... params) {
String referralCode = null;
try {
JSONArray results = (JSONArray) Lbryio.parseResponse(Lbryio.call("user_referral_code", "list", null, null));
if (results.length() > 0) {
referralCode = results.getString(0);
}
} catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException ex) {
error = ex;
}
return referralCode;
}
protected void onPostExecute(String referralCode) {
Helper.setViewVisibility(progressView, View.GONE);
if (handler != null) {
if (!Helper.isNullOrEmpty(referralCode)) {
handler.onSuccess(referralCode);
} else {
handler.onError(error);
}
}
}
public interface FetchReferralCodeHandler {
void onSuccess(String referralCode);
void onError(Exception error);
}
}

View file

@ -1,4 +1,4 @@
package io.lbry.browser.tasks; package io.lbry.browser.tasks.lbryinc;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.view.View; import android.view.View;

View file

@ -1,4 +1,4 @@
package io.lbry.browser.tasks; package io.lbry.browser.tasks.lbryinc;
import android.content.Context; import android.content.Context;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;

View file

@ -0,0 +1,55 @@
package io.lbry.browser.tasks.lbryinc;
import android.os.AsyncTask;
import android.view.View;
import java.util.HashMap;
import java.util.Map;
import io.lbry.browser.exceptions.LbryioRequestException;
import io.lbry.browser.exceptions.LbryioResponseException;
import io.lbry.browser.tasks.GenericTaskHandler;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbryio;
public class InviteByEmailTask extends AsyncTask<Void, Void, Boolean> {
private String email;
private View progressView;
private GenericTaskHandler handler;
private Exception error;
public InviteByEmailTask(String email, View progressView, GenericTaskHandler handler) {
this.email = email;
this.progressView = progressView;
this.handler = handler;
}
protected void onPreExecute() {
Helper.setViewVisibility(progressView, View.VISIBLE);
if (handler != null) {
handler.beforeStart();
}
}
protected Boolean doInBackground(Void... params) {
try {
Map<String, String> options = new HashMap<>();
options.put("email", email);
Lbryio.parseResponse(Lbryio.call("user", "invite", options, Helper.METHOD_POST, null));
} catch (LbryioRequestException | LbryioResponseException ex) {
error = ex;
return false;
}
return true;
}
protected void onPostExecute(Boolean result) {
Helper.setViewVisibility(progressView, View.GONE);
if (handler != null) {
if (result) {
handler.onSuccess();
} else {
handler.onError(error);
}
}
}
}

View file

@ -3,6 +3,7 @@ package io.lbry.browser.ui.channel;
import android.Manifest; import android.Manifest;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
@ -342,13 +343,10 @@ public class ChannelFormFragment extends BaseFragment implements WalletBalanceLi
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void showError(String message) { private void showError(String message) {
Context context = getContext(); Context context = getContext();
if (context != null) { if (context != null) {
Snackbar.make(getView(), message, Snackbar.LENGTH_LONG).setBackgroundTint( Snackbar.make(getView(), message, Snackbar.LENGTH_LONG).setBackgroundTint(Color.RED).show();
ContextCompat.getColor(context, R.color.red)).show();
} }
} }

View file

@ -33,10 +33,9 @@ import io.lbry.browser.R;
import io.lbry.browser.dialog.SendTipDialogFragment; import io.lbry.browser.dialog.SendTipDialogFragment;
import io.lbry.browser.exceptions.LbryUriException; import io.lbry.browser.exceptions.LbryUriException;
import io.lbry.browser.listener.FetchChannelsListener; import io.lbry.browser.listener.FetchChannelsListener;
import io.lbry.browser.listener.SdkStatusListener;
import io.lbry.browser.model.Claim; import io.lbry.browser.model.Claim;
import io.lbry.browser.model.lbryinc.Subscription; import io.lbry.browser.model.lbryinc.Subscription;
import io.lbry.browser.tasks.ChannelSubscribeTask; import io.lbry.browser.tasks.lbryinc.ChannelSubscribeTask;
import io.lbry.browser.tasks.ClaimListResultHandler; import io.lbry.browser.tasks.ClaimListResultHandler;
import io.lbry.browser.tasks.ResolveTask; import io.lbry.browser.tasks.ResolveTask;
import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.ui.BaseFragment;

View file

@ -37,10 +37,10 @@ import io.lbry.browser.dialog.DiscoverDialogFragment;
import io.lbry.browser.exceptions.LbryUriException; import io.lbry.browser.exceptions.LbryUriException;
import io.lbry.browser.model.Claim; import io.lbry.browser.model.Claim;
import io.lbry.browser.model.lbryinc.Subscription; import io.lbry.browser.model.lbryinc.Subscription;
import io.lbry.browser.tasks.ChannelSubscribeTask; import io.lbry.browser.tasks.lbryinc.ChannelSubscribeTask;
import io.lbry.browser.tasks.ClaimListResultHandler; import io.lbry.browser.tasks.ClaimListResultHandler;
import io.lbry.browser.tasks.ClaimSearchTask; import io.lbry.browser.tasks.ClaimSearchTask;
import io.lbry.browser.tasks.FetchSubscriptionsTask; import io.lbry.browser.tasks.lbryinc.FetchSubscriptionsTask;
import io.lbry.browser.tasks.ResolveTask; import io.lbry.browser.tasks.ResolveTask;
import io.lbry.browser.listener.ChannelItemSelectionListener; import io.lbry.browser.listener.ChannelItemSelectionListener;
import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.ui.BaseFragment;

View file

@ -0,0 +1,553 @@
package io.lbry.browser.ui.wallet;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatSpinner;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import io.lbry.browser.BuildConfig;
import io.lbry.browser.MainActivity;
import io.lbry.browser.R;
import io.lbry.browser.adapter.InlineChannelSpinnerAdapter;
import io.lbry.browser.adapter.InviteeListAdapter;
import io.lbry.browser.listener.SdkStatusListener;
import io.lbry.browser.listener.WalletBalanceListener;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.WalletBalance;
import io.lbry.browser.model.lbryinc.Invitee;
import io.lbry.browser.tasks.ClaimListResultHandler;
import io.lbry.browser.tasks.ClaimListTask;
import io.lbry.browser.tasks.GenericTaskHandler;
import io.lbry.browser.tasks.content.ChannelCreateUpdateTask;
import io.lbry.browser.tasks.content.ClaimResultHandler;
import io.lbry.browser.tasks.content.LogPublishTask;
import io.lbry.browser.tasks.lbryinc.FetchInviteStatusTask;
import io.lbry.browser.tasks.lbryinc.FetchReferralCodeTask;
import io.lbry.browser.tasks.lbryinc.InviteByEmailTask;
import io.lbry.browser.ui.BaseFragment;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry;
import io.lbry.browser.utils.LbryUri;
import io.lbry.browser.utils.Lbryio;
public class InvitesFragment extends BaseFragment implements SdkStatusListener, WalletBalanceListener {
private static final String INVITE_LINK_FORMAT = "https://lbry.tv/$/invite/%s:%s";
private boolean fetchingChannels;
private View layoutAccountDriver;
private View layoutSdkInitializing;
private TextView textLearnMoreLink;
private MaterialButton buttonGetStarted;
private View buttonCopyInviteLink;
private TextView textInviteLink;
private TextInputLayout layoutInputEmail;
private TextInputEditText inputEmail;
private MaterialButton buttonInviteByEmail;
private RecyclerView inviteHistoryList;
private InviteeListAdapter inviteHistoryAdapter;
private InlineChannelSpinnerAdapter channelSpinnerAdapter;
private AppCompatSpinner channelSpinner;
private View progressLoadingChannels;
private View progressLoadingInviteByEmail;
private View progressLoadingStatus;
private View inlineChannelCreator;
private TextInputEditText inlineChannelCreatorInputName;
private TextInputEditText inlineChannelCreatorInputDeposit;
private View inlineChannelCreatorInlineBalance;
private TextView inlineChannelCreatorInlineBalanceValue;
private View inlineChannelCreatorCancelLink;
private View inlineChannelCreatorProgress;
private MaterialButton inlineChannelCreatorCreateButton;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_invites, container, false);
layoutAccountDriver = root.findViewById(R.id.invites_account_driver_container);
layoutSdkInitializing = root.findViewById(R.id.container_sdk_initializing);
textLearnMoreLink = root.findViewById(R.id.invites_account_driver_learn_more);
buttonGetStarted = root.findViewById(R.id.invites_get_started_button);
textInviteLink = root.findViewById(R.id.invites_invite_link);
buttonCopyInviteLink = root.findViewById(R.id.invites_copy_invite_link);
layoutInputEmail = root.findViewById(R.id.invites_email_input_layout);
inputEmail = root.findViewById(R.id.invites_email_input);
buttonInviteByEmail = root.findViewById(R.id.invites_email_button);
progressLoadingChannels = root.findViewById(R.id.invites_loading_channels_progress);
progressLoadingInviteByEmail = root.findViewById(R.id.invites_loading_invite_by_email_progress);
progressLoadingStatus = root.findViewById(R.id.invites_loading_status_progress);
inviteHistoryList = root.findViewById(R.id.invite_history_list);
LinearLayoutManager llm = new LinearLayoutManager(getContext());
inviteHistoryList.setLayoutManager(llm);
channelSpinner = root.findViewById(R.id.invites_channel_spinner);
inlineChannelCreator = root.findViewById(R.id.container_inline_channel_form_create);
inlineChannelCreatorInputName = root.findViewById(R.id.inline_channel_form_input_name);
inlineChannelCreatorInputDeposit = root.findViewById(R.id.inline_channel_form_input_deposit);
inlineChannelCreatorInlineBalance = root.findViewById(R.id.inline_channel_form_inline_balance_container);
inlineChannelCreatorInlineBalanceValue = root.findViewById(R.id.inline_channel_form_inline_balance_value);
inlineChannelCreatorProgress = root.findViewById(R.id.inline_channel_form_create_progress);
inlineChannelCreatorCancelLink = root.findViewById(R.id.inline_channel_form_cancel_link);
inlineChannelCreatorCreateButton = root.findViewById(R.id.inline_channel_form_create_button);
initUi();
return root;
}
private void initUi() {
layoutAccountDriver.setVisibility(Lbryio.isSignedIn() ? View.GONE : View.VISIBLE);
layoutSdkInitializing.setVisibility(Lbry.SDK_READY ? View.GONE : View.VISIBLE);
Helper.applyHtmlForTextView(textLearnMoreLink);
inputEmail.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
layoutInputEmail.setHint(hasFocus ? getString(R.string.email) :
Helper.getValue(inputEmail.getText()).length() > 0 ?
getString(R.string.email) : getString(R.string.invite_email_placeholder));
}
});
inputEmail.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
Helper.setViewEnabled(buttonInviteByEmail, charSequence.length() > 0);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
buttonInviteByEmail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String email = Helper.getValue(inputEmail.getText());
if (email.indexOf("@") == -1) {
showError(getString(R.string.provide_valid_email));
return;
}
InviteByEmailTask task = new InviteByEmailTask(email, progressLoadingInviteByEmail, new GenericTaskHandler() {
@Override
public void beforeStart() {
Helper.setViewEnabled(buttonInviteByEmail, false);
}
@Override
public void onSuccess() {
Snackbar.make(getView(), getString(R.string.invite_sent_to, email), Snackbar.LENGTH_LONG).show();
Helper.setViewText(inputEmail, null);
Helper.setViewEnabled(buttonInviteByEmail, true);
fetchInviteStatus();
}
@Override
public void onError(Exception error) {
showError(error.getMessage());
Helper.setViewEnabled(buttonInviteByEmail, true);
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
buttonGetStarted.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Context context = getContext();
if (context instanceof MainActivity) {
((MainActivity) context).simpleSignIn();
}
}
});
if (Lbry.ownChannels != null) {
updateChannelList(Lbry.ownChannels);
}
channelSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {
Object item = adapterView.getItemAtPosition(position);
if (item instanceof Claim) {
Claim claim = (Claim) item;
if (claim.isPlaceholder()) {
if (!fetchingChannels) {
showInlineChannelCreator();
}
} else {
hideInlineChannelCreator();
// build invite link
updateInviteLink(claim);
}
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
textInviteLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
copyInviteLink();
}
});
buttonCopyInviteLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
copyInviteLink();
}
});
setupInlineChannelCreator(
inlineChannelCreator,
inlineChannelCreatorInputName,
inlineChannelCreatorInputDeposit,
inlineChannelCreatorInlineBalance,
inlineChannelCreatorInlineBalanceValue,
inlineChannelCreatorCancelLink,
inlineChannelCreatorCreateButton,
inlineChannelCreatorProgress
);
}
private void updateInviteLink(Claim claim) {
String link = String.format(INVITE_LINK_FORMAT, claim.getName(), claim.getClaimId());
textInviteLink.setText(link);
}
private void copyInviteLink() {
Context context = getContext();
if (context != null && textInviteLink != null) {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData data = ClipData.newPlainText("inviteLink", textInviteLink.getText());
clipboard.setPrimaryClip(data);
}
Snackbar.make(getView(), R.string.invite_link_copied, Snackbar.LENGTH_SHORT).show();
}
private void updateChannelList(List<Claim> channels) {
if (channelSpinnerAdapter == null) {
Context context = getContext();
channelSpinnerAdapter = new InlineChannelSpinnerAdapter(context, R.layout.spinner_item_channel, channels);
channelSpinnerAdapter.addPlaceholder(false);
channelSpinner.setAdapter(channelSpinnerAdapter);
channelSpinnerAdapter.notifyDataSetChanged();
} else {
channelSpinnerAdapter.clear();
channelSpinnerAdapter.addAll(channels);
channelSpinnerAdapter.addPlaceholder(false);
channelSpinnerAdapter.notifyDataSetChanged();
}
if (channelSpinnerAdapter.getCount() > 1) {
channelSpinner.setSelection(1);
}
}
public void onResume() {
super.onResume();
layoutAccountDriver.setVisibility(Lbryio.isSignedIn() ? View.GONE : View.VISIBLE);
fetchInviteStatus();
if (!Lbry.SDK_READY) {
Context context = getContext();
if (context instanceof MainActivity) {
MainActivity activity = (MainActivity) context;
activity.addSdkStatusListener(this);
activity.addWalletBalanceListener(this);
}
} else {
onSdkReady();
}
}
public void onSdkReady() {
Helper.setViewVisibility(layoutSdkInitializing, View.GONE);
fetchChannels();
}
public void onStart() {
super.onStart();
Context context = getContext();
if (context instanceof MainActivity) {
MainActivity activity = (MainActivity) context;
activity.setWunderbarValue(null);
activity.hideFloatingWalletBalance();
}
}
public void clearInputFocus() {
inputEmail.clearFocus();
inlineChannelCreatorInputName.clearFocus();
inlineChannelCreatorInputDeposit.clearFocus();
}
public void onStop() {
clearInputFocus();
Context context = getContext();
if (context instanceof MainActivity) {
MainActivity activity = (MainActivity) context;
activity.removeSdkStatusListener(this);
activity.removeWalletBalanceListener(this);
activity.showFloatingWalletBalance();
}
super.onStop();
}
private void showInlineChannelCreator() {
Helper.setViewVisibility(inlineChannelCreator, View.VISIBLE);
}
private void hideInlineChannelCreator() {
Helper.setViewVisibility(inlineChannelCreator, View.GONE);
}
private void fetchDefaultInviteLink() {
FetchReferralCodeTask task = new FetchReferralCodeTask(null, new FetchReferralCodeTask.FetchReferralCodeHandler() {
@Override
public void onSuccess(String referralCode) {
String previousLink = Helper.getValue(textInviteLink.getText());
if (Helper.isNullOrEmpty(previousLink)) {
Helper.setViewText(textInviteLink, String.format("https://lbry.tv/$/invite/%s", referralCode));
}
}
@Override
public void onError(Exception error) {
// pass
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void disableChannelSpinner() {
Helper.setViewEnabled(channelSpinner, false);
hideInlineChannelCreator();
}
private void enableChannelSpinner() {
Helper.setViewEnabled(channelSpinner, true);
Claim selectedClaim = (Claim) channelSpinner.getSelectedItem();
if (selectedClaim != null) {
if (selectedClaim.isPlaceholder()) {
showInlineChannelCreator();
} else {
hideInlineChannelCreator();
}
}
}
private void fetchChannels() {
fetchingChannels = true;
disableChannelSpinner();
ClaimListTask task = new ClaimListTask(Claim.TYPE_CHANNEL, progressLoadingChannels, new ClaimListResultHandler() {
@Override
public void onSuccess(List<Claim> claims) {
Lbry.ownChannels = new ArrayList<>(claims);
updateChannelList(Lbry.ownChannels);
if (Lbry.ownChannels == null || Lbry.ownChannels.size() == 0) {
fetchDefaultInviteLink();
}
enableChannelSpinner();
fetchingChannels = false;
}
@Override
public void onError(Exception error) {
fetchDefaultInviteLink();
enableChannelSpinner();
fetchingChannels = false;
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void fetchInviteStatus() {
FetchInviteStatusTask task = new FetchInviteStatusTask(progressLoadingStatus, new FetchInviteStatusTask.FetchInviteStatusHandler() {
@Override
public void onSuccess(List<Invitee> invitees) {
if (inviteHistoryAdapter == null) {
inviteHistoryAdapter = new InviteeListAdapter(invitees, getContext());
inviteHistoryAdapter.addHeader();
inviteHistoryList.setAdapter(inviteHistoryAdapter);
} else {
inviteHistoryAdapter.addInvitees(invitees);
}
Helper.setViewVisibility(inviteHistoryList,
inviteHistoryAdapter == null || inviteHistoryAdapter.getItemCount() == 0 ? View.GONE : View.VISIBLE
);
}
@Override
public void onError(Exception error) {
Helper.setViewVisibility(inviteHistoryList,
inviteHistoryAdapter == null || inviteHistoryAdapter.getItemCount() == 0 ? View.GONE : View.VISIBLE
);
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void setupInlineChannelCreator(
View container,
TextInputEditText inputChannelName,
TextInputEditText inputDeposit,
View inlineBalanceView,
TextView inlineBalanceValue,
View linkCancel,
MaterialButton buttonCreate,
View progressView) {
inputDeposit.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
Helper.setViewVisibility(inlineBalanceView, hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
linkCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Helper.setViewText(inputChannelName, null);
Helper.setViewText(inputDeposit, null);
Helper.setViewVisibility(container, View.GONE);
}
});
buttonCreate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// validate deposit and channel name
String channelNameString = Helper.getValue(inputChannelName.getText());
if (!channelNameString.startsWith("@")) {
channelNameString = String.format("@%s", channelNameString);
}
Claim claimToSave = new Claim();
claimToSave.setName(channelNameString);
String channelName = claimToSave.getName().startsWith("@") ? claimToSave.getName().substring(1) : claimToSave.getName();
String depositString = Helper.getValue(inputDeposit.getText());
if (Helper.isNullOrEmpty(channelName)) {
showError(getString(R.string.please_enter_channel_name));
return;
}
if (!LbryUri.isNameValid(channelName)) {
showError(getString(R.string.channel_name_invalid_characters));
return;
}
if (Helper.channelExists(channelName)) {
showError(getString(R.string.channel_name_already_created));
return;
}
double depositAmount = 0;
try {
depositAmount = Double.valueOf(depositString);
} catch (NumberFormatException ex) {
// pass
showError(getString(R.string.please_enter_valid_deposit));
return;
}
if (depositAmount == 0) {
String error = getResources().getQuantityString(R.plurals.min_deposit_required, depositAmount == 1 ? 1 : 2, String.valueOf(Helper.MIN_DEPOSIT));
showError(error);
return;
}
if (Lbry.walletBalance == null || Lbry.walletBalance.getAvailable().doubleValue() < depositAmount) {
showError(getString(R.string.deposit_more_than_balance));
return;
}
ChannelCreateUpdateTask task = new ChannelCreateUpdateTask(
claimToSave, new BigDecimal(depositString), false, progressView, new ClaimResultHandler() {
@Override
public void beforeStart() {
Helper.setViewEnabled(inputChannelName, false);
Helper.setViewEnabled(inputDeposit, false);
Helper.setViewEnabled(buttonCreate, false);
Helper.setViewEnabled(linkCancel, false);
}
@Override
public void onSuccess(Claim claimResult) {
if (!BuildConfig.DEBUG) {
LogPublishTask logPublishTask = new LogPublishTask(claimResult);
logPublishTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
// add the claim to the channel list and set it as the selected item
channelSpinnerAdapter.add(claimResult);
channelSpinner.setSelection(channelSpinnerAdapter.getCount() - 1);
Helper.setViewEnabled(inputChannelName, true);
Helper.setViewEnabled(inputDeposit, true);
Helper.setViewEnabled(buttonCreate, true);
Helper.setViewEnabled(linkCancel, true);
}
@Override
public void onError(Exception error) {
Helper.setViewEnabled(inputChannelName, true);
Helper.setViewEnabled(inputDeposit, true);
Helper.setViewEnabled(buttonCreate, true);
Helper.setViewEnabled(linkCancel, true);
showError(error.getMessage());
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
Helper.setViewText(inlineBalanceValue, Helper.shortCurrencyFormat(Lbry.walletBalance.getAvailable().doubleValue()));
}
@Override
public void onWalletBalanceUpdated(WalletBalance walletBalance) {
if (walletBalance != null && inlineChannelCreatorInlineBalanceValue != null) {
inlineChannelCreatorInlineBalanceValue.setText(Helper.shortCurrencyFormat(walletBalance.getAvailable().doubleValue()));
}
}
private void showError(String message) {
Context context = getContext();
if (context != null) {
Snackbar.make(getView(), message, Snackbar.LENGTH_LONG).setBackgroundTint(Color.RED).show();
}
}
}

View file

@ -27,8 +27,8 @@ import io.lbry.browser.R;
import io.lbry.browser.adapter.RewardListAdapter; import io.lbry.browser.adapter.RewardListAdapter;
import io.lbry.browser.listener.SdkStatusListener; import io.lbry.browser.listener.SdkStatusListener;
import io.lbry.browser.model.lbryinc.Reward; import io.lbry.browser.model.lbryinc.Reward;
import io.lbry.browser.tasks.ClaimRewardTask; import io.lbry.browser.tasks.lbryinc.ClaimRewardTask;
import io.lbry.browser.tasks.FetchRewardsTask; import io.lbry.browser.tasks.lbryinc.FetchRewardsTask;
import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.ui.BaseFragment;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
@ -130,6 +130,11 @@ public class RewardsFragment extends BaseFragment implements RewardListAdapter.R
Lbryio.updateRewardsLists(rewards); Lbryio.updateRewardsLists(rewards);
updateUnclaimedRewardsValue(); updateUnclaimedRewardsValue();
Context context = getContext();
if (context instanceof MainActivity) {
((MainActivity) context).showFloatingUnclaimedRewards();
}
if (adapter == null) { if (adapter == null) {
adapter = new RewardListAdapter(rewards, getContext()); adapter = new RewardListAdapter(rewards, getContext());
adapter.setClickListener(RewardsFragment.this); adapter.setClickListener(RewardsFragment.this);

View file

@ -250,7 +250,7 @@ public final class Lbryio {
JSONObject response = (JSONObject) parseResponse(Lbryio.call("lbc", "exchange_rate", null)); JSONObject response = (JSONObject) parseResponse(Lbryio.call("lbc", "exchange_rate", null));
LBCUSDRate = Helper.getJSONDouble("lbc_usd", 0, response); LBCUSDRate = Helper.getJSONDouble("lbc_usd", 0, response);
} catch (LbryioResponseException | LbryioRequestException | ClassCastException ex) { } catch (LbryioResponseException | LbryioRequestException | ClassCastException ex) {
// pass
} }
} }

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:fontFamily="@font/inter"
android:textStyle="bold"
android:text="@string/invite_by_email"
android:textSize="20sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:text="@string/invite_someone"
android:textSize="14sp"
android:textFontWeight="300" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/invites_email_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/invites_email_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:hint="@string/invite_email_placeholder"
android:textSize="14sp"
android:textFontWeight="300" />
</com.google.android.material.textfield.TextInputLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<ProgressBar
android:id="@+id/invites_loading_invite_by_email_progress"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_marginRight="24dp"
android:layout_toLeftOf="@id/invites_email_button"
android:visibility="gone" />
<com.google.android.material.button.MaterialButton
android:id="@+id/invites_email_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:enabled="false"
android:text="@string/invite" />
</RelativeLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:fontFamily="@font/inter"
android:textStyle="bold"
android:text="@string/invite_link"
android:textSize="20sp"/>
<ProgressBar
android:id="@+id/invites_loading_channels_progress"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:visibility="gone" />
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:text="@string/share_this_link"
android:textSize="14sp"
android:textFontWeight="300" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:fontFamily="@font/inter"
android:text="@string/your_invite_link"
android:textSize="14sp"
android:textFontWeight="300" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<LinearLayout
android:background="@drawable/bg_copyable_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/invites_copy_invite_link"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingLeft="12dp"
android:paddingRight="12dp">
<TextView
android:id="@+id/invites_invite_link"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:textSize="14sp"
android:letterSpacing="0.05"
android:singleLine="true"
android:ellipsize="middle" />
</LinearLayout>
<ImageButton
android:id="@+id/invites_copy_invite_link"
android:layout_marginLeft="8dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerVertical="true"
android:background="@null"
android:src="@drawable/ic_copy"
android:tint="@color/lbryGreen"
android:layout_alignParentRight="true" />
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:fontFamily="@font/inter"
android:text="@string/customize_invite_link"
android:textSize="14sp"
android:textFontWeight="300" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/invites_channel_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp" />
<include layout="@layout/container_inline_channel_form" />
</LinearLayout>
</androidx.cardview.widget.CardView>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:fontFamily="@font/inter"
android:textStyle="bold"
android:text="@string/invite_history"
android:textSize="20sp"/>
<ProgressBar
android:id="@+id/invites_loading_status_progress"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:visibility="gone" />
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:text="@string/earn_credits_for_inviting"
android:textSize="14sp"
android:textFontWeight="300" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/invite_history_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone"/>
</LinearLayout>
</androidx.cardview.widget.CardView>

View file

@ -40,7 +40,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/inter" android:fontFamily="@font/inter"
android:background="@drawable/bg_wallet_address" android:background="@drawable/bg_copyable_text"
android:textSize="14sp" android:textSize="14sp"
android:letterSpacing="0.05" android:letterSpacing="0.05"
android:paddingTop="8dp" android:paddingTop="8dp"

View file

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container_inline_channel_form_create"
android:layout_marginTop="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/inline_channel_form_input_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:hint="@string/channel_name"
android:textFontWeight="300"
android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout>
<RelativeLayout
android:layout_marginTop="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/inline_channel_form_layout_deposit"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:hint="@string/deposit">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/inline_channel_form_input_deposit"
android:fontFamily="@font/inter"
android:singleLine="true"
android:textSize="14sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/inline_channel_form_inline_currency"
android:layout_toRightOf="@id/inline_channel_form_layout_deposit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="30dp"
android:fontFamily="@font/inter"
android:text="@string/lbc"
android:textAllCaps="true"
android:textSize="10sp"
android:textFontWeight="300" />
<LinearLayout
android:id="@+id/inline_channel_form_inline_balance_container"
android:layout_toRightOf="@id/inline_channel_form_inline_currency"
android:layout_marginLeft="24dp"
android:layout_marginTop="28dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="invisible">
<io.lbry.browser.ui.controls.SolidIconView
android:layout_width="18dp"
android:layout_height="18dp"
android:text="@string/fa_coins" />
<TextView
android:id="@+id/inline_channel_form_inline_balance_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:textFontWeight="300"
android:layout_marginLeft="2dp" />
</LinearLayout>
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:fontFamily="@font/inter"
android:text="@string/deposit_remains_yours"
android:textFontWeight="300"
android:textSize="12sp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<TextView
android:id="@+id/inline_channel_form_cancel_link"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:fontFamily="@font/inter"
android:textSize="12sp"
android:text="@string/cancel" />
<ProgressBar
android:id="@+id/inline_channel_form_create_progress"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_marginRight="24dp"
android:layout_toLeftOf="@id/inline_channel_form_create_button"
android:visibility="gone" />
<com.google.android.material.button.MaterialButton
android:id="@+id/inline_channel_form_create_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="@string/create"/>
</RelativeLayout>
</LinearLayout>

View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/pageBackground">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<include layout="@layout/card_invites_by_link" />
<include layout="@layout/card_invites_by_email" />
<include layout="@layout/card_invites_history" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<include layout="@layout/container_sdk_initializing" />
<RelativeLayout
android:id="@+id/invites_account_driver_container"
android:background="@color/lbryGreen"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/invites_account_driver_actions"
android:orientation="vertical">
<TextView
android:id="@+id/invites_account_driver_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:fontFamily="@font/inter"
android:text="@string/lbry_invite_program"
android:textSize="24sp"
android:textColor="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:textSize="16sp"
android:layout_marginTop="16dp"
android:text="@string/earn_extra_credits_invites"
android:textColor="@color/white"
android:textFontWeight="300" />
<TextView
android:id="@+id/invites_account_driver_learn_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:textSize="16sp"
android:layout_marginTop="16dp"
android:text="@string/invites_learn_more"
android:textColor="@color/white"
android:textFontWeight="300" />
</LinearLayout>
<RelativeLayout
android:id="@+id/invites_account_driver_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/invites_get_started_button"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:fontFamily="@font/inter"
android:text="@string/get_started" />
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>

View file

@ -4,7 +4,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/pageBackground"> android:background="@color/pageBackground">
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="10">
<RelativeLayout
android:layout_weight="6"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="4dp">
<TextView
android:id="@+id/invitee_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:fontFamily="@font/inter"
android:singleLine="true"
android:textSize="14sp"
android:textFontWeight="300" />
</RelativeLayout>
<RelativeLayout
android:layout_weight="4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="4dp">
<TextView
android:id="@+id/invitee_reward"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:textSize="14sp"
android:textFontWeight="300" />
</RelativeLayout>
</LinearLayout>

View file

@ -9,8 +9,8 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="16dp" android:paddingTop="12dp"
android:paddingBottom="16dp" android:paddingBottom="12dp"
android:orientation="horizontal" android:orientation="horizontal"
android:weightSum="10"> android:weightSum="10">
<RelativeLayout <RelativeLayout
@ -109,7 +109,7 @@
android:fontFamily="@font/inter" android:fontFamily="@font/inter"
android:textSize="10sp" android:textSize="10sp"
android:textFontWeight="300" android:textFontWeight="300"
android:visibility="invisible" /> android:visibility="gone" />
<TextView <TextView
android:id="@+id/reward_item_lbc_value" android:id="@+id/reward_item_lbc_value"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -129,7 +129,7 @@
android:id="@+id/reward_item_usd_value" android:id="@+id/reward_item_usd_value"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="2dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:fontFamily="@font/inter" android:fontFamily="@font/inter"
android:textSize="12sp" android:textSize="12sp"

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="12dp">
<TextView
android:id="@+id/channel_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:textSize="14sp"
android:textFontWeight="300" />
</LinearLayout>

View file

@ -247,6 +247,7 @@
<!-- Channels --> <!-- Channels -->
<string name="no_channel_created">You have not created a channel.\nStart now by creating a new channel!</string> <string name="no_channel_created">You have not created a channel.\nStart now by creating a new channel!</string>
<string name="create_a_channel">Create a channel</string> <string name="create_a_channel">Create a channel</string>
<string name="create_a_channel_item">Create a channel...</string>
<string name="edit_channel">Edit channel</string> <string name="edit_channel">Edit channel</string>
<string name="delete_selection">Delete selection?</string> <string name="delete_selection">Delete selection?</string>
<string name="description">Description</string> <string name="description">Description</string>
@ -278,6 +279,7 @@
<string name="channel_save_successful">The channel was successfully saved.</string> <string name="channel_save_successful">The channel was successfully saved.</string>
<string name="item_pending_blockchain">The claim is pending publish on the blockchain. You will be able to access or edit the claim in a few moments.</string> <string name="item_pending_blockchain">The claim is pending publish on the blockchain. You will be able to access or edit the claim in a few moments.</string>
<string name="pending">Pending</string> <string name="pending">Pending</string>
<string name="create">Create</string>
<plurals name="min_deposit_required"> <plurals name="min_deposit_required">
<item quantity="one">A minimum deposit of %1$s credit is required.</item> <item quantity="one">A minimum deposit of %1$s credit is required.</item>
<item quantity="other">A minimum deposit of %1$s credits is required.</item> <item quantity="other">A minimum deposit of %1$s credits is required.</item>
@ -308,6 +310,27 @@
<item quantity="other">%1$s available credits</item> <item quantity="other">%1$s available credits</item>
</plurals> </plurals>
<!-- Invites -->
<string name="lbry_invite_program">LBRY Invite Program</string>
<string name="earn_extra_credits_invites">You can earn extra credits for each person you invite to use LBRY.</string>
<string name="invites_learn_more">&lt;a href="https://lbry.com/faq/invites"&gt;Learn more&lt;/a&gt;.</string>
<string name="invite_link">Invite Link</string>
<string name="share_this_link">Share this link with friends (or enemies) and get credits when they join lbry.tv.</string>
<string name="your_invite_link">Your invite link</string>
<string name="customize_invite_link">Customize invite link</string>
<string name="invite_by_email">Invite by Email</string>
<string name="invite_someone">Invite someone you know by email and earn credits when they join lbry.tv.</string>
<string name="invite_email_placeholder">imaginary@friend.com</string>
<string name="invite">Invite</string>
<string name="invite_history">Invite History</string>
<string name="earn_credits_for_inviting">Earn credits for invite a friend, an enemy, a frenemy, or an enefriend. Everyone needs content freedom.</string>
<string name="reward">Reward</string>
<string name="claimed">Claimed</string>
<string name="claimable">Claimable</string>
<string name="unclaimable">Unclaimable</string>
<string name="invite_link_copied">Invite link copied.</string>
<string name="invite_sent_to">Invite sent to %1$s</string>
<!-- Font Awesome --> <!-- Font Awesome -->
<string name="fa_gift">&#xf06b;</string> <string name="fa_gift">&#xf06b;</string>
<string name="fa_lock">&#xf023;</string> <string name="fa_lock">&#xf023;</string>