1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-14 10:47:30 +00:00

convert StoryModel to StoryMedia

close #1151, close #1208
This commit is contained in:
Austin Huang 2021-06-29 16:56:12 -04:00
parent 53b0301385
commit 4d9494cbcf
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
25 changed files with 292 additions and 603 deletions

View File

@ -10,20 +10,21 @@ import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.databinding.ItemStoryBinding; import awais.instagrabber.databinding.ItemStoryBinding;
import awais.instagrabber.models.StoryModel; import awais.instagrabber.repositories.responses.stories.StoryMedia;
import awais.instagrabber.utils.ResponseBodyUtils;
public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter.StoryViewHolder> { public final class StoriesAdapter extends ListAdapter<StoryMedia, StoriesAdapter.StoryViewHolder> {
private final OnItemClickListener onItemClickListener; private final OnItemClickListener onItemClickListener;
private static final DiffUtil.ItemCallback<StoryModel> diffCallback = new DiffUtil.ItemCallback<StoryModel>() { private static final DiffUtil.ItemCallback<StoryMedia> diffCallback = new DiffUtil.ItemCallback<StoryMedia>() {
@Override @Override
public boolean areItemsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) { public boolean areItemsTheSame(@NonNull final StoryMedia oldItem, @NonNull final StoryMedia newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()); return oldItem.getId().equals(newItem.getId());
} }
@Override @Override
public boolean areContentsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) { public boolean areContentsTheSame(@NonNull final StoryMedia oldItem, @NonNull final StoryMedia newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()); return oldItem.getId().equals(newItem.getId());
} }
}; };
@ -42,8 +43,8 @@ public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter
@Override @Override
public void onBindViewHolder(@NonNull final StoryViewHolder holder, final int position) { public void onBindViewHolder(@NonNull final StoryViewHolder holder, final int position) {
final StoryModel storyModel = getItem(position); final StoryMedia storyMedia = getItem(position);
holder.bind(storyModel, position, onItemClickListener); holder.bind(storyMedia, position, onItemClickListener);
} }
public final static class StoryViewHolder extends RecyclerView.ViewHolder { public final static class StoryViewHolder extends RecyclerView.ViewHolder {
@ -54,7 +55,7 @@ public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter
this.binding = binding; this.binding = binding;
} }
public void bind(final StoryModel model, public void bind(final StoryMedia model,
final int position, final int position,
final OnItemClickListener clickListener) { final OnItemClickListener clickListener) {
if (model == null) return; if (model == null) return;
@ -67,14 +68,11 @@ public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter
}); });
binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE); binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE);
binding.icon.setImageURI(model.getStoryUrl()); binding.icon.setImageURI(ResponseBodyUtils.getThumbUrl(model));
// Glide.with(itemView).load(model.getStoryUrl())
// .apply(new RequestOptions().override(width, height))
// .into(holder.icon);
} }
} }
public interface OnItemClickListener { public interface OnItemClickListener {
void onItemClick(StoryModel storyModel, int position); void onItemClick(StoryMedia storyModel, int position);
} }
} }

View File

@ -548,7 +548,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
// private void fetchStories() { // private void fetchStories() {
// if (!isLoggedIn) return; // if (!isLoggedIn) return;
// storiesFetching = true; // storiesFetching = true;
// storiesRepository.getUserStory( // storiesRepository.getStories(
// StoryViewerOptions.forHashtag(hashtagModel.getName()), // StoryViewerOptions.forHashtag(hashtagModel.getName()),
// CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { // CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
// if (throwable != null) { // if (throwable != null) {

View File

@ -552,7 +552,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
// private void fetchStories() { // private void fetchStories() {
// if (isLoggedIn) { // if (isLoggedIn) {
// storiesFetching = true; // storiesFetching = true;
// storiesRepository.getUserStory( // storiesRepository.getStories(
// StoryViewerOptions.forLocation(locationId, locationModel.getName()), // StoryViewerOptions.forLocation(locationId, locationModel.getName()),
// CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { // CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
// if (throwable != null) { // if (throwable != null) {

View File

@ -58,8 +58,10 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import java.io.IOException; import java.io.IOException;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.stream.Collectors;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -72,23 +74,17 @@ import awais.instagrabber.databinding.FragmentStoryViewerBinding;
import awais.instagrabber.fragments.main.ProfileFragmentDirections; import awais.instagrabber.fragments.main.ProfileFragmentDirections;
import awais.instagrabber.fragments.settings.PreferenceKeys; import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.interfaces.SwipeEvent; import awais.instagrabber.interfaces.SwipeEvent;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.models.stickers.SliderModel;
import awais.instagrabber.models.stickers.SwipeUpModel;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.requests.StoryViewerOptions.Type; import awais.instagrabber.repositories.requests.StoryViewerOptions.Type;
import awais.instagrabber.repositories.requests.directmessages.ThreadIdsOrUserIds; import awais.instagrabber.repositories.requests.directmessages.ThreadIdsOrUserIds;
import awais.instagrabber.repositories.responses.stories.Broadcast; import awais.instagrabber.repositories.responses.stories.*;
import awais.instagrabber.repositories.responses.stories.Story;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.CoroutineUtilsKt;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.ArchivesViewModel; import awais.instagrabber.viewmodels.ArchivesViewModel;
@ -121,16 +117,16 @@ public class StoryViewerFragment extends Fragment {
private GestureDetectorCompat gestureDetector; private GestureDetectorCompat gestureDetector;
private StoriesRepository storiesRepository; private StoriesRepository storiesRepository;
private MediaRepository mediaRepository; private MediaRepository mediaRepository;
private StoryModel currentStory; private StoryMedia currentStory;
private Broadcast live; private Broadcast live;
private int slidePos; private int slidePos;
private int lastSlidePos; private int lastSlidePos;
private String url; private String url;
private PollModel poll; private PollSticker poll;
private QuestionModel question; private QuestionSticker question;
private String[] mentions; private List<String> mentions = new ArrayList<String>();
private QuizModel quiz; private QuizSticker quiz;
private SliderModel slider; private SliderSticker slider;
private MenuItem menuDownload, menuDm, menuProfile; private MenuItem menuDownload, menuDm, menuProfile;
private SimpleExoPlayer player; private SimpleExoPlayer player;
// private boolean isHashtag; // private boolean isHashtag;
@ -220,10 +216,10 @@ public class StoryViewerFragment extends Fragment {
csrfToken, csrfToken,
userId, userId,
deviceId, deviceId,
ThreadIdsOrUserIds.Companion.ofOneUser(String.valueOf(currentStory.getUserId())), ThreadIdsOrUserIds.Companion.ofOneUser(String.valueOf(currentStory.getUser().getPk())),
input.getText().toString(), input.getText().toString(),
currentStory.getStoryMediaId(), currentStory.getId(),
String.valueOf(currentStory.getUserId()), String.valueOf(currentStory.getUser().getPk()),
CoroutineUtilsKt.getContinuation( CoroutineUtilsKt.getContinuation(
(directThreadBroadcastResponse, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { (directThreadBroadcastResponse, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable1 != null) { if (throwable1 != null) {
@ -253,7 +249,7 @@ public class StoryViewerFragment extends Fragment {
return true; return true;
} }
if (itemId == R.id.action_profile) { if (itemId == R.id.action_profile) {
openProfile("@" + currentStory.getUsername()); openProfile("@" + currentStory.getUser().getPk());
} }
return false; return false;
} }
@ -357,7 +353,7 @@ public class StoryViewerFragment extends Fragment {
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
swipeEvent = isRightSwipe -> { swipeEvent = isRightSwipe -> {
final List<StoryModel> storyModels = storiesViewModel.getList().getValue(); final List<StoryMedia> storyModels = storiesViewModel.getList().getValue();
final int storiesLen = storyModels == null ? 0 : storyModels.size(); final int storiesLen = storyModels == null ? 0 : storyModels.size();
if (sticking) { if (sticking) {
Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_SHORT).show();
@ -373,12 +369,14 @@ public class StoryViewerFragment extends Fragment {
Toast.makeText(context, R.string.no_more_stories, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.no_more_stories, Toast.LENGTH_SHORT).show();
return; return;
} }
removeStickers();
final Object feedStoryModel = isRightSwipe final Object feedStoryModel = isRightSwipe
? finalModels.get(index - 1) ? finalModels.get(index - 1)
: finalModels.size() == index + 1 ? null : finalModels.get(index + 1); : finalModels.size() == index + 1 ? null : finalModels.get(index + 1);
paginateStories(feedStoryModel, finalModels.get(index), context, isRightSwipe, currentFeedStoryIndex == finalModels.size() - 2); paginateStories(feedStoryModel, finalModels.get(index), context, isRightSwipe, currentFeedStoryIndex == finalModels.size() - 2);
return; return;
} }
removeStickers();
if (isRightSwipe) { if (isRightSwipe) {
if (--slidePos <= 0) { if (--slidePos <= 0) {
slidePos = 0; slidePos = 0;
@ -471,35 +469,31 @@ public class StoryViewerFragment extends Fragment {
}); });
final View.OnClickListener storyActionListener = v -> { final View.OnClickListener storyActionListener = v -> {
final Object tag = v.getTag(); final Object tag = v.getTag();
if (tag instanceof PollModel) { if (tag instanceof PollSticker) {
poll = (PollModel) tag; poll = (PollSticker) tag;
if (poll.getMyChoice() > -1) { final List<Tally> tallies = poll.getTallies();
final String[] choices = tallies.stream()
.map(t -> (poll.getViewerVote() == tallies.indexOf(t) ? "" : "")
+ t.getText() + " (" + t.getCount() + ")" )
.toArray(String[]::new);
final ArrayAdapter adapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices);
if (poll.getViewerVote() > -1) {
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setTitle(R.string.voted_story_poll) .setTitle(R.string.voted_story_poll)
.setAdapter(new ArrayAdapter<>( .setAdapter(adapter, null)
context,
android.R.layout.simple_list_item_1,
new String[]{
(poll.getMyChoice() == 0 ? "" : "") + poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
(poll.getMyChoice() == 1 ? "" : "") + poll.getRightChoice() + " (" + poll.getRightCount() + ")"
}),
null)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show(); .show();
} else { } else {
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setTitle(poll.getQuestion()) .setTitle(poll.getQuestion())
.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, new String[]{ .setAdapter(adapter, (d, w) -> {
poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
poll.getRightChoice() + " (" + poll.getRightCount() + ")"
}), (d, w) -> {
sticking = true; sticking = true;
storiesRepository.respondToPoll( storiesRepository.respondToPoll(
csrfToken, csrfToken,
userId, userId,
deviceId, deviceId,
currentStory.getStoryMediaId().split("_")[0], currentStory.getId().split("_")[0],
poll.getId(), poll.getPollId(),
w, w,
CoroutineUtilsKt.getContinuation( CoroutineUtilsKt.getContinuation(
(storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
@ -513,7 +507,7 @@ public class StoryViewerFragment extends Fragment {
} }
sticking = false; sticking = false;
try { try {
poll.setMyChoice(w); poll.setViewerVote(w);
Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
} catch (Exception ignored) {} } catch (Exception ignored) {}
}), }),
@ -524,8 +518,8 @@ public class StoryViewerFragment extends Fragment {
.setPositiveButton(R.string.cancel, null) .setPositiveButton(R.string.cancel, null)
.show(); .show();
} }
} else if (tag instanceof QuestionModel) { } else if (tag instanceof QuestionSticker) {
question = (QuestionModel) tag; question = (QuestionSticker) tag;
final EditText input = new EditText(context); final EditText input = new EditText(context);
input.setHint(R.string.answer_hint); input.setHint(R.string.answer_hint);
final AlertDialog ad = new AlertDialog.Builder(context) final AlertDialog ad = new AlertDialog.Builder(context)
@ -537,8 +531,8 @@ public class StoryViewerFragment extends Fragment {
csrfToken, csrfToken,
userId, userId,
deviceId, deviceId,
currentStory.getStoryMediaId().split("_")[0], currentStory.getId().split("_")[0],
question.getId(), question.getQuestionId(),
input.getText().toString(), input.getText().toString(),
CoroutineUtilsKt.getContinuation( CoroutineUtilsKt.getContinuation(
(storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
@ -575,28 +569,31 @@ public class StoryViewerFragment extends Fragment {
public void afterTextChanged(final Editable s) {} public void afterTextChanged(final Editable s) {}
}); });
} else if (tag instanceof String[]) { } else if (tag instanceof String[]) {
mentions = (String[]) tag; final String[] rawMentions = (String[]) tag;
mentions = new ArrayList<String>(Arrays.asList(rawMentions));
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setTitle(R.string.story_mentions) .setTitle(R.string.story_mentions)
.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, mentions), (d, w) -> openProfile(mentions[w])) .setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, rawMentions), (d, w) -> openProfile(mentions.get(w)))
.setPositiveButton(R.string.cancel, null) .setPositiveButton(R.string.cancel, null)
.show(); .show();
} else if (tag instanceof QuizModel) { } else if (tag instanceof QuizSticker) {
String[] choices = new String[quiz.getChoices().length]; final List<Tally> tallies = quiz.getTallies();
for (int q = 0; q < choices.length; ++q) { final String[] choices = tallies.stream().map(
choices[q] = (quiz.getMyChoice() == q ? "" : "") + quiz.getChoices()[q] + " (" + quiz.getCounts()[q] + ")"; t -> (quiz.getViewerAnswer() == tallies.indexOf(t) ? "" : "") +
} (quiz.getCorrectAnswer() == tallies.indexOf(t) ? "*** " : "") +
t.getText() + " (" + t.getCount() + ")"
).toArray(String[]::new);
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setTitle(quiz.getMyChoice() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion()) .setTitle(quiz.getViewerAnswer() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion())
.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices), (d, w) -> { .setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices), (d, w) -> {
if (quiz.getMyChoice() == -1) { if (quiz.getViewerAnswer() == -1) {
sticking = true; sticking = true;
storiesRepository.respondToQuiz( storiesRepository.respondToQuiz(
csrfToken, csrfToken,
userId, userId,
deviceId, deviceId,
currentStory.getStoryMediaId().split("_")[0], currentStory.getId().split("_")[0],
quiz.getId(), quiz.getQuizId(),
w, w,
CoroutineUtilsKt.getContinuation( CoroutineUtilsKt.getContinuation(
(storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
@ -610,7 +607,7 @@ public class StoryViewerFragment extends Fragment {
} }
sticking = false; sticking = false;
try { try {
quiz.setMyChoice(w); quiz.setViewerAnswer(w);
Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
} catch (Exception ignored) {} } catch (Exception ignored) {}
}), }),
@ -621,8 +618,8 @@ public class StoryViewerFragment extends Fragment {
}) })
.setPositiveButton(R.string.cancel, null) .setPositiveButton(R.string.cancel, null)
.show(); .show();
} else if (tag instanceof SliderModel) { } else if (tag instanceof SliderSticker) {
slider = (SliderModel) tag; slider = (SliderSticker) tag;
NumberFormat percentage = NumberFormat.getPercentInstance(); NumberFormat percentage = NumberFormat.getPercentInstance();
percentage.setMaximumFractionDigits(2); percentage.setMaximumFractionDigits(2);
LinearLayout sliderView = new LinearLayout(context); LinearLayout sliderView = new LinearLayout(context);
@ -633,11 +630,11 @@ public class StoryViewerFragment extends Fragment {
TextView tv = new TextView(context); TextView tv = new TextView(context);
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
final SeekBar input = new SeekBar(context); final SeekBar input = new SeekBar(context);
double avg = slider.getAverage() * 100; double avg = slider.getSliderVoteAverage() * 100;
input.setProgress((int) avg); input.setProgress((int) avg);
sliderView.addView(input); sliderView.addView(input);
sliderView.addView(tv); sliderView.addView(tv);
if (slider.getMyChoice().isNaN() && slider.canVote()) { if (slider.getViewerVote().isNaN() && slider.getViewerCanVote()) {
input.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { input.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override @Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@ -656,9 +653,9 @@ public class StoryViewerFragment extends Fragment {
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion()) .setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion())
.setMessage(getResources().getQuantityString(R.plurals.slider_info, .setMessage(getResources().getQuantityString(R.plurals.slider_info,
slider.getVoteCount(), slider.getSliderVoteCount(),
slider.getVoteCount(), slider.getSliderVoteCount(),
percentage.format(slider.getAverage()))) percentage.format(slider.getSliderVoteAverage())))
.setView(sliderView) .setView(sliderView)
.setPositiveButton(R.string.confirm, (d, w) -> { .setPositiveButton(R.string.confirm, (d, w) -> {
sticking = true; sticking = true;
@ -666,8 +663,8 @@ public class StoryViewerFragment extends Fragment {
csrfToken, csrfToken,
userId, userId,
deviceId, deviceId,
currentStory.getStoryMediaId().split("_")[0], currentStory.getId().split("_")[0],
slider.getId(), slider.getSliderId(),
sliderValue, sliderValue,
CoroutineUtilsKt.getContinuation( CoroutineUtilsKt.getContinuation(
(storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
@ -681,7 +678,7 @@ public class StoryViewerFragment extends Fragment {
} }
sticking = false; sticking = false;
try { try {
slider.setMyChoice(sliderValue); slider.setViewerVote(sliderValue);
Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
} catch (Exception ignored) {} } catch (Exception ignored) {}
}), Dispatchers.getIO() }), Dispatchers.getIO()
@ -692,13 +689,13 @@ public class StoryViewerFragment extends Fragment {
.show(); .show();
} else { } else {
input.setEnabled(false); input.setEnabled(false);
tv.setText(getString(R.string.slider_answer, percentage.format(slider.getMyChoice()))); tv.setText(getString(R.string.slider_answer, percentage.format(slider.getViewerVote())));
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion()) .setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion())
.setMessage(getResources().getQuantityString(R.plurals.slider_info, .setMessage(getResources().getQuantityString(R.plurals.slider_info,
slider.getVoteCount(), slider.getSliderVoteCount(),
slider.getVoteCount(), slider.getSliderVoteCount(),
percentage.format(slider.getAverage()))) percentage.format(slider.getSliderVoteAverage())))
.setView(sliderView) .setView(sliderView)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show(); .show();
@ -746,11 +743,12 @@ public class StoryViewerFragment extends Fragment {
case FEED_STORY_POSITION: { case FEED_STORY_POSITION: {
final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel; final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel;
final List<Story> models = feedStoriesViewModel.getList().getValue(); final List<Story> models = feedStoriesViewModel.getList().getValue();
if (models == null || currentFeedStoryIndex >= models.size() || currentFeedStoryIndex < 0) return; if (models == null || currentFeedStoryIndex >= models.size() || currentFeedStoryIndex < 0)
return;
final Story model = models.get(currentFeedStoryIndex); final Story model = models.get(currentFeedStoryIndex);
currentStoryMediaId = model.getId(); currentStoryMediaId = String.valueOf(model.getUser().getPk());
currentStoryUsername = model.getUser().getUsername(); currentStoryUsername = model.getUser().getUsername();
fetchOptions = StoryViewerOptions.forUser(Long.parseLong(currentStoryMediaId), currentStoryUsername); fetchOptions = StoryViewerOptions.forUser(model.getUser().getPk(), currentStoryUsername);
live = model.getBroadcast(); live = model.getBroadcast();
break; break;
} }
@ -767,11 +765,12 @@ public class StoryViewerFragment extends Fragment {
fetchOptions = StoryViewerOptions.forStoryArchive(model.getId()); fetchOptions = StoryViewerOptions.forStoryArchive(model.getId());
break; break;
} }
} case USER: {
if (type == Type.USER) {
currentStoryMediaId = String.valueOf(options.getId()); currentStoryMediaId = String.valueOf(options.getId());
currentStoryUsername = options.getName(); currentStoryUsername = options.getName();
fetchOptions = StoryViewerOptions.forUser(options.getId(), currentStoryUsername); fetchOptions = StoryViewerOptions.forUser(options.getId(), currentStoryUsername);
break;
}
} }
setTitle(type); setTitle(type);
storiesViewModel.getList().setValue(Collections.emptyList()); storiesViewModel.getList().setValue(Collections.emptyList());
@ -804,9 +803,9 @@ public class StoryViewerFragment extends Fragment {
refreshLive(); refreshLive();
return; return;
} }
final ServiceCallback<List<StoryModel>> storyCallback = new ServiceCallback<List<StoryModel>>() { final ServiceCallback<List<StoryMedia>> storyCallback = new ServiceCallback<List<StoryMedia>>() {
@Override @Override
public void onSuccess(final List<StoryModel> storyModels) { public void onSuccess(final List<StoryMedia> storyModels) {
fetching = false; fetching = false;
if (storyModels == null || storyModels.isEmpty()) { if (storyModels == null || storyModels.isEmpty()) {
storiesViewModel.getList().setValue(Collections.emptyList()); storiesViewModel.getList().setValue(Collections.emptyList());
@ -826,11 +825,10 @@ public class StoryViewerFragment extends Fragment {
@Override @Override
public void onFailure(final Throwable t) { public void onFailure(final Throwable t) {
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
Log.e(TAG, "Error", t); Log.e(TAG, "Error", t);
} }
}; };
storiesRepository.getUserStory( storiesRepository.getStories(
fetchOptions, fetchOptions,
CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) { if (throwable != null) {
@ -838,7 +836,7 @@ public class StoryViewerFragment extends Fragment {
return; return;
} }
//noinspection unchecked //noinspection unchecked
storyCallback.onSuccess((List<StoryModel>) storyModels); storyCallback.onSuccess((List<StoryMedia>) storyModels);
}), Dispatchers.getIO()) }), Dispatchers.getIO())
); );
} }
@ -887,9 +885,9 @@ public class StoryViewerFragment extends Fragment {
private synchronized void refreshStory() { private synchronized void refreshStory() {
if (binding.storiesList.getVisibility() == View.VISIBLE) { if (binding.storiesList.getVisibility() == View.VISIBLE) {
final List<StoryModel> storyModels = storiesViewModel.getList().getValue(); final List<StoryMedia> storyModels = storiesViewModel.getList().getValue();
if (storyModels != null && storyModels.size() > 0) { if (storyModels != null && storyModels.size() > 0) {
StoryModel item = storyModels.get(lastSlidePos); StoryMedia item = storyModels.get(lastSlidePos);
if (item != null) { if (item != null) {
item.setCurrentSlide(false); item.setCurrentSlide(false);
storiesAdapter.notifyItemChanged(lastSlidePos, item); storiesAdapter.notifyItemChanged(lastSlidePos, item);
@ -903,59 +901,96 @@ public class StoryViewerFragment extends Fragment {
} }
lastSlidePos = slidePos; lastSlidePos = slidePos;
final MediaItemType itemType = currentStory.getItemType(); final MediaItemType itemType = currentStory.getMediaType();
url = itemType == MediaItemType.MEDIA_TYPE_IMAGE ? currentStory.getStoryUrl() : currentStory.getVideoUrl(); url = itemType == MediaItemType.MEDIA_TYPE_IMAGE
? ResponseBodyUtils.getImageUrl(currentStory)
: ResponseBodyUtils.getVideoUrl(currentStory);
final String shortCode = currentStory.getTappableShortCode(); if (currentStory.getStoryFeedMedia() != null) {
binding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE); final String shortCode = currentStory.getStoryFeedMedia().get(0).getMediaId();
binding.viewStoryPost.setVisibility(View.VISIBLE);
binding.viewStoryPost.setTag(shortCode); binding.viewStoryPost.setTag(shortCode);
}
final String spotify = currentStory.getSpotify(); final StoryAppAttribution spotify = currentStory.getStoryAppAttribution();
binding.spotify.setVisibility(spotify != null ? View.VISIBLE : View.GONE); if (spotify != null) {
binding.spotify.setTag(spotify); binding.spotify.setVisibility(View.VISIBLE);
binding.spotify.setText(spotify.getName());
binding.spotify.setTag(spotify.getContentUrl().split("?")[0]);
}
poll = currentStory.getPoll(); if (currentStory.getStoryPolls() != null) {
binding.poll.setVisibility(poll != null ? View.VISIBLE : View.GONE); poll = currentStory.getStoryPolls().get(0).getPollSticker();
binding.poll.setVisibility(View.VISIBLE);
binding.poll.setTag(poll); binding.poll.setTag(poll);
}
question = currentStory.getQuestion(); if (currentStory.getStoryQuestions() != null) {
binding.answer.setVisibility((question != null) ? View.VISIBLE : View.GONE); question = currentStory.getStoryQuestions().get(0).getQuestionSticker();
binding.answer.setVisibility(View.VISIBLE);
binding.answer.setTag(question); binding.answer.setTag(question);
}
mentions = currentStory.getMentions(); mentions.clear();
binding.mention.setVisibility((mentions != null && mentions.length > 0) ? View.VISIBLE : View.GONE); if (currentStory.getReelMentions() != null) {
binding.mention.setTag(mentions); mentions.addAll(currentStory.getReelMentions().stream().map(
s -> s.getUser().getUsername()
).distinct().collect(Collectors.toList()));
}
if (currentStory.getStoryHashtags() != null) {
mentions.addAll(currentStory.getStoryHashtags().stream().map(
s -> s.getHashtag().getName()
).distinct().collect(Collectors.toList()));
}
if (currentStory.getStoryLocations() != null) {
mentions.addAll(currentStory.getStoryLocations().stream().map(
s -> s.getLocation().getShortName() + " (" + s.getLocation().getPk() + ")"
).distinct().collect(Collectors.toList()));
}
if (mentions.size() > 0) {
binding.mention.setVisibility(View.VISIBLE);
binding.mention.setTag(mentions.stream().toArray(String[]::new));
}
quiz = currentStory.getQuiz(); if (currentStory.getStoryQuizs() != null) {
binding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE); quiz = currentStory.getStoryQuizs().get(0).getQuizSticker();
binding.quiz.setVisibility(View.VISIBLE);
binding.quiz.setTag(quiz); binding.quiz.setTag(quiz);
}
slider = currentStory.getSlider(); if (currentStory.getStorySliders() != null) {
binding.slider.setVisibility(slider != null ? View.VISIBLE : View.GONE); slider = currentStory.getStorySliders().get(0).getSliderSticker();
binding.slider.setVisibility(View.VISIBLE);
binding.slider.setTag(slider); binding.slider.setTag(slider);
}
final SwipeUpModel swipeUp = currentStory.getSwipeUp(); if (currentStory.getStoryCta() != null) {
if (swipeUp != null) { final StoryCta swipeUp = currentStory.getStoryCta().get(0).getLinks();
binding.swipeUp.setVisibility(View.VISIBLE); binding.swipeUp.setVisibility(View.VISIBLE);
binding.swipeUp.setText(swipeUp.getText()); binding.swipeUp.setText(currentStory.getLinkText());
binding.swipeUp.setTag(swipeUp.getUrl()); final String swipeUpUrl = swipeUp.getWebUri();
} else binding.swipeUp.setVisibility(View.GONE); final String actualLink = swipeUpUrl.startsWith("https://l.instagram.com/")
? Uri.parse(swipeUpUrl).getQueryParameter("u")
: null;
binding.swipeUp.setTag(actualLink == null && actualLink.startsWith("http")
? swipeUpUrl : actualLink);
}
releasePlayer(); releasePlayer();
final Type type = options.getType(); final Type type = options.getType();
if (type == Type.HASHTAG || type == Type.LOCATION) { if (type == Type.HASHTAG || type == Type.LOCATION) {
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) { if (actionBar != null) {
actionBarTitle = currentStory.getUsername(); actionBarTitle = currentStory.getUser().getUsername();
actionBar.setTitle(currentStory.getUsername()); actionBar.setTitle(currentStory.getUser().getUsername());
} }
} }
if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(); if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo();
else setupImage(); else setupImage();
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); final ActionBar actionBar = fragmentActivity.getSupportActionBar();
actionBarSubtitle = TextUtils.epochSecondToString(currentStory.getTimestamp()); actionBarSubtitle = TextUtils.epochSecondToString(currentStory.getTakenAt());
if (actionBar != null) { if (actionBar != null) {
try { try {
actionBar.setSubtitle(actionBarSubtitle); actionBar.setSubtitle(actionBarSubtitle);
@ -969,13 +1004,23 @@ public class StoryViewerFragment extends Fragment {
csrfToken, csrfToken,
userId, userId,
deviceId, deviceId,
currentStory.getStoryMediaId(), currentStory.getId(),
currentStory.getTimestamp(), currentStory.getTakenAt(),
System.currentTimeMillis() / 1000, System.currentTimeMillis() / 1000,
CoroutineUtilsKt.getContinuation((s, throwable) -> {}, Dispatchers.getIO()) CoroutineUtilsKt.getContinuation((s, throwable) -> {}, Dispatchers.getIO())
); );
} }
private void removeStickers() {
binding.swipeUp.setVisibility(View.GONE);
binding.quiz.setVisibility(View.GONE);
binding.spotify.setVisibility(View.GONE);
binding.mention.setVisibility(View.GONE);
binding.viewStoryPost.setVisibility(View.GONE);
binding.answer.setVisibility(View.GONE);
binding.slider.setVisibility(View.GONE);
}
private void downloadStory() { private void downloadStory() {
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
@ -1016,7 +1061,7 @@ public class StoryViewerFragment extends Fragment {
dmVisible = true; dmVisible = true;
menuDm.setVisible(true); menuDm.setVisible(true);
} }
if (!TextUtils.isEmpty(currentStory.getUsername())) { if (!TextUtils.isEmpty(currentStory.getUser().getUsername())) {
profileVisible = true; profileVisible = true;
menuProfile.setVisible(true); menuProfile.setVisible(true);
} }
@ -1057,7 +1102,7 @@ public class StoryViewerFragment extends Fragment {
dmVisible = true; dmVisible = true;
menuDm.setVisible(true); menuDm.setVisible(true);
} }
if (!TextUtils.isEmpty(currentStory.getUsername()) && menuProfile != null) { if (!TextUtils.isEmpty(currentStory.getUser().getUsername()) && menuProfile != null) {
profileVisible = true; profileVisible = true;
menuProfile.setVisible(true); menuProfile.setVisible(true);
} }
@ -1077,7 +1122,7 @@ public class StoryViewerFragment extends Fragment {
dmVisible = true; dmVisible = true;
menuDm.setVisible(true); menuDm.setVisible(true);
} }
if (!TextUtils.isEmpty(currentStory.getUsername()) && menuProfile != null) { if (!TextUtils.isEmpty(currentStory.getUser().getUsername()) && menuProfile != null) {
profileVisible = true; profileVisible = true;
menuProfile.setVisible(true); menuProfile.setVisible(true);
} }

View File

@ -1,29 +0,0 @@
package awais.instagrabber.models
import awais.instagrabber.models.enums.MediaItemType
import awais.instagrabber.models.stickers.*
import java.io.Serializable
data class StoryModel(
val storyMediaId: String? = null,
val storyUrl: String? = null,
var thumbnail: String? = null,
val itemType: MediaItemType? = null,
val timestamp: Long = 0,
val username: String? = null,
val userId: Long = 0,
val canReply: Boolean = false,
) : Serializable {
var videoUrl: String? = null
var tappableShortCode: String? = null
val tappableId: String? = null
var spotify: String? = null
var poll: PollModel? = null
var question: QuestionModel? = null
var slider: SliderModel? = null
var quiz: QuizModel? = null
var swipeUp: SwipeUpModel? = null
var mentions: Array<String>? = null
var position = 0
var isCurrentSlide = false
}

View File

@ -1,52 +0,0 @@
package awais.instagrabber.models.stickers;
import java.io.Serializable;
public final class PollModel implements Serializable {
private int leftcount, rightcount, mychoice;
private final String id, question, leftchoice, rightchoice;
public PollModel(final String id, final String question, final String leftchoice, final int leftcount,
final String rightchoice, final int rightcount, final int mychoice) {
this.id = id; // only the poll id
this.question = question;
this.leftchoice = leftchoice;
this.leftcount = leftcount;
this.rightchoice = rightchoice;
this.rightcount = rightcount;
this.mychoice = mychoice;
}
public String getId() {
return id;
}
public String getQuestion() {
return question;
}
public String getLeftChoice() {
return leftchoice;
}
public int getLeftCount() {
return leftcount;
}
public String getRightChoice() {
return rightchoice;
}
public int getRightCount() {
return rightcount;
}
public int getMyChoice() { return mychoice; }
public int setMyChoice(final int choice) {
this.mychoice = choice;
if (choice == 0) this.leftcount += 1;
else if (choice == 1) this.rightcount += 1;
return choice;
}
}

View File

@ -1,20 +0,0 @@
package awais.instagrabber.models.stickers;
import java.io.Serializable;
public final class QuestionModel implements Serializable {
private final String id, question;
public QuestionModel(final String id, final String question) {
this.id = id; // only the poll id
this.question = question;
}
public String getId() {
return id;
}
public String getQuestion() {
return question;
}
}

View File

@ -1,37 +0,0 @@
package awais.instagrabber.models.stickers;
import java.io.Serializable;
public final class QuizModel implements Serializable {
private final String id, question;
private final String[] choices;
private Long[] counts;
private int mychoice;
public QuizModel(final String id, final String question, final String[] choices, final Long[] counts, final int mychoice) {
this.id = id; // only the poll id
this.question = question;
this.choices = choices;
this.counts = counts;
this.mychoice = mychoice;
}
public String getId() {
return id;
}
public String getQuestion() {
return question;
}
public String[] getChoices() { return choices;}
public Long[] getCounts() { return counts;}
public int getMyChoice() { return mychoice; }
public void setMyChoice(final int choice) {
this.mychoice = choice;
counts[choice] += 1L;
}
}

View File

@ -1,53 +0,0 @@
package awais.instagrabber.models.stickers;
import java.io.Serializable;
public final class SliderModel implements Serializable {
private final int voteCount;
private final Double average;
private Double myChoice;
private final boolean canVote;
private final String id, question, emoji;
public SliderModel(final String id, final String question, final String emoji, final boolean canVote,
final Double average, final int voteCount, final Double myChoice) {
this.id = id;
this.question = question;
this.emoji = emoji;
this.canVote = canVote;
this.average = average;
this.voteCount = voteCount;
this.myChoice = myChoice;
}
public String getId() {
return id;
}
public String getQuestion() {
return question;
}
public String getEmoji() {
return emoji;
}
public boolean canVote() {
return canVote;
}
public int getVoteCount() {
return voteCount;
}
public Double getAverage() {
return average;
}
public Double getMyChoice() { return myChoice; }
public Double setMyChoice(final Double choice) {
this.myChoice = choice;
return choice;
}
}

View File

@ -1,20 +0,0 @@
package awais.instagrabber.models.stickers;
import java.io.Serializable;
public final class SwipeUpModel implements Serializable {
private final String url, text;
public SwipeUpModel(final String url, final String text) {
this.url = url;
this.text = text;
}
public String getUrl() {
return url;
}
public String getText() {
return text;
}
}

View File

@ -1,14 +1,17 @@
package awais.instagrabber.repositories package awais.instagrabber.repositories
import awais.instagrabber.repositories.responses.stories.ArchiveResponse import awais.instagrabber.repositories.responses.stories.ArchiveResponse
import awais.instagrabber.repositories.responses.stories.ReelsMediaResponse
import awais.instagrabber.repositories.responses.stories.ReelsResponse
import awais.instagrabber.repositories.responses.stories.ReelsTrayResponse import awais.instagrabber.repositories.responses.stories.ReelsTrayResponse
import awais.instagrabber.repositories.responses.stories.StoryMediaResponse
import awais.instagrabber.repositories.responses.stories.StoryStickerResponse import awais.instagrabber.repositories.responses.stories.StoryStickerResponse
import retrofit2.http.* import retrofit2.http.*
interface StoriesService { interface StoriesService {
// this one is the same as MediaRepository.fetch BUT you need to make sure it's a story // this one is the same as MediaRepository.fetch BUT you need to make sure it's a story
@GET("/api/v1/media/{mediaId}/info/") @GET("/api/v1/media/{mediaId}/info/")
suspend fun fetch(@Path("mediaId") mediaId: Long): String suspend fun fetch(@Path("mediaId") mediaId: Long): StoryMediaResponse
@GET("/api/v1/feed/reels_tray/") @GET("/api/v1/feed/reels_tray/")
suspend fun getFeedStories(): ReelsTrayResponse? suspend fun getFeedStories(): ReelsTrayResponse?
@ -19,14 +22,20 @@ interface StoriesService {
@GET("/api/v1/archive/reel/day_shells/") @GET("/api/v1/archive/reel/day_shells/")
suspend fun fetchArchive(@QueryMap queryParams: Map<String, String>): ArchiveResponse? suspend fun fetchArchive(@QueryMap queryParams: Map<String, String>): ArchiveResponse?
@GET @GET("/api/v1/feed/reels_media/")
suspend fun getUserStory(@Url url: String): String suspend fun getReelsMedia(@Query("user_ids") id: String): ReelsMediaResponse
@GET("/api/v1/{type}/{id}/story/")
suspend fun getStories(@Path("type") type: String, @Path("id") id: String): ReelsResponse
@GET("/api/v1/feed/user/{id}/story/")
suspend fun getUserStories(@Path("id") id: String): ReelsResponse
@FormUrlEncoded @FormUrlEncoded
@POST("/api/v1/media/{storyId}/{stickerId}/{action}/") @POST("/api/v1/media/{storyId}/{stickerId}/{action}/")
suspend fun respondToSticker( suspend fun respondToSticker(
@Path("storyId") storyId: String, @Path("storyId") storyId: String,
@Path("stickerId") stickerId: String, @Path("stickerId") stickerId: Long,
@Path("action") action: String, // story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer @Path("action") action: String, // story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer
@FieldMap form: Map<String, String>, @FieldMap form: Map<String, String>,
): StoryStickerResponse ): StoryStickerResponse

View File

@ -42,7 +42,7 @@ public class StoryViewerOptions implements Serializable {
} }
public static StoryViewerOptions forUser(final long id, final String name) { public static StoryViewerOptions forUser(final long id, final String name) {
return new StoryViewerOptions(id, name,Type.USER); return new StoryViewerOptions(id, name, Type.USER);
} }
public static StoryViewerOptions forHighlight(final String highlight) { public static StoryViewerOptions forHighlight(final String highlight) {

View File

@ -9,5 +9,5 @@ data class PollSticker(
val pollId: Long?, val pollId: Long?,
val question: String?, val question: String?,
val tallies: List<Tally>?, val tallies: List<Tally>?,
val viewerVote: Int? var viewerVote: Int = -1
) : Serializable ) : Serializable

View File

@ -9,6 +9,6 @@ data class QuizSticker(
val quizId: Long?, val quizId: Long?,
val question: String?, val question: String?,
val tallies: List<Tally>?, val tallies: List<Tally>?,
val viewerAnswer: Int?, var viewerAnswer: Int? = -1,
val correctAnswer: Int? val correctAnswer: Int?
) : Serializable ) : Serializable

View File

@ -0,0 +1,8 @@
package awais.instagrabber.repositories.responses.stories
import java.io.Serializable
data class ReelsMediaResponse(
val status: String?,
val reels: Map<String, Story?>?
) : Serializable

View File

@ -4,6 +4,7 @@ import java.io.Serializable
data class ReelsResponse( data class ReelsResponse(
val status: String?, val status: String?,
val reel: Story?, val reel: Story?, // users
val story: Story?, // hashtag and locations (unused)
val broadcast: Broadcast? val broadcast: Broadcast?
) : Serializable ) : Serializable

View File

@ -10,7 +10,7 @@ data class SliderSticker(
val question: String?, val question: String?,
val emoji: String?, val emoji: String?,
val viewerCanVote: Boolean?, val viewerCanVote: Boolean?,
val viewerVote: Double?, var viewerVote: Double?,
val sliderVoteAverage: Double?, val sliderVoteAverage: Double?,
val sliderVoteCount: Int?, val sliderVoteCount: Int?,
) : Serializable ) : Serializable

View File

@ -41,6 +41,8 @@ data class StoryMedia(
val storyAppAttribution: StoryAppAttribution? = null val storyAppAttribution: StoryAppAttribution? = null
) : Serializable { ) : Serializable {
private var dateString: String? = null private var dateString: String? = null
var position = 0
var isCurrentSlide = false
// TODO use extension once all usages are converted to kotlin // TODO use extension once all usages are converted to kotlin
// val date: String by lazy { // val date: String by lazy {

View File

@ -0,0 +1,14 @@
package awais.instagrabber.repositories.responses.stories
import awais.instagrabber.models.enums.MediaItemType
import awais.instagrabber.repositories.responses.ImageVersions2
import awais.instagrabber.repositories.responses.MediaCandidate
import awais.instagrabber.repositories.responses.User
import awais.instagrabber.utils.TextUtils
import java.io.Serializable
data class StoryMediaResponse(
val items: List<StoryMedia?>?, // length 1
val status: String?
// ignoring pagination properties
) : Serializable

View File

@ -13,9 +13,9 @@ import androidx.documentfile.provider.DocumentFile
import androidx.work.* import androidx.work.*
import awais.instagrabber.R import awais.instagrabber.R
import awais.instagrabber.fragments.settings.PreferenceKeys import awais.instagrabber.fragments.settings.PreferenceKeys
import awais.instagrabber.models.StoryModel
import awais.instagrabber.models.enums.MediaItemType import awais.instagrabber.models.enums.MediaItemType
import awais.instagrabber.repositories.responses.Media import awais.instagrabber.repositories.responses.Media
import awais.instagrabber.repositories.responses.stories.StoryMedia
import awais.instagrabber.utils.TextUtils.isEmpty import awais.instagrabber.utils.TextUtils.isEmpty
import awais.instagrabber.workers.DownloadWorker import awais.instagrabber.workers.DownloadWorker
import com.google.gson.Gson import com.google.gson.Gson
@ -392,18 +392,19 @@ object DownloadUtils {
@JvmStatic @JvmStatic
fun download( fun download(
context: Context, context: Context,
storyModel: StoryModel storyModel: StoryMedia
) { ) {
val downloadDir = getDownloadDir(context, storyModel.username) ?: return val downloadDir = getDownloadDir(context, storyModel.user?.username) ?: return
val url = val url =
if (storyModel.itemType == MediaItemType.MEDIA_TYPE_VIDEO) storyModel.videoUrl else storyModel.storyUrl if (storyModel.mediaType == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(storyModel)
else ResponseBodyUtils.getImageUrl(storyModel)
val extension = getFileExtensionFromUrl(url) val extension = getFileExtensionFromUrl(url)
val baseFileName = (storyModel.storyMediaId + "_" val baseFileName = (storyModel.id + "_"
+ storyModel.timestamp + extension) + storyModel.takenAt + extension)
val usernamePrepend = val usernamePrepend =
if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME)
&& storyModel.username != null && storyModel.user?.username != null
) storyModel.username + "_" else "" ) storyModel.user.username + "_" else ""
val fileName = usernamePrepend + baseFileName val fileName = usernamePrepend + baseFileName
var saveFile = downloadDir.findFile(fileName) var saveFile = downloadDir.findFile(fileName)
if (saveFile == null) { if (saveFile == null) {

View File

@ -14,13 +14,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.models.stickers.SliderModel;
import awais.instagrabber.models.stickers.SwipeUpModel;
import awais.instagrabber.repositories.responses.Caption; import awais.instagrabber.repositories.responses.Caption;
import awais.instagrabber.repositories.responses.FriendshipStatus; import awais.instagrabber.repositories.responses.FriendshipStatus;
import awais.instagrabber.repositories.responses.ImageVersions2; import awais.instagrabber.repositories.responses.ImageVersions2;
@ -276,136 +270,6 @@ public final class ResponseBodyUtils {
); );
} }
public static StoryModel parseStoryItem(final JSONObject data,
final boolean isLocOrHashtag,
final String username) throws JSONException {
final boolean isVideo = data.has("video_duration");
final StoryModel model = new StoryModel(data.getString("id"),
data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(0)
.getString("url"), null,
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
data.optLong("taken_at", 0),
isLocOrHashtag ? data.getJSONObject("user").getString("username") : username,
data.getJSONObject("user").getLong("pk"),
data.optBoolean("can_reply"));
if (data.getJSONObject("image_versions2").getJSONArray("candidates").length() > 1) {
model.setThumbnail(data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(1)
.getString("url"));
}
final JSONArray videoResources = data.optJSONArray("video_versions");
if (isVideo && videoResources != null)
model.setVideoUrl(ResponseBodyUtils.getHighQualityPost(videoResources, true, true, false));
if (data.has("story_feed_media")) {
model.setTappableShortCode(data.getJSONArray("story_feed_media").getJSONObject(0).optString("media_id"));
}
// TODO: this may not be limited to spotify
if (!data.isNull("story_app_attribution"))
model.setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]);
if (data.has("story_polls")) {
final JSONArray storyPolls = data.optJSONArray("story_polls");
JSONObject tappableObject = null;
if (storyPolls != null) {
tappableObject = storyPolls.getJSONObject(0).optJSONObject("poll_sticker");
}
if (tappableObject != null) model.setPoll(new PollModel(
String.valueOf(tappableObject.getLong("poll_id")),
tappableObject.getString("question"),
tappableObject.getJSONArray("tallies").getJSONObject(0).getString("text"),
tappableObject.getJSONArray("tallies").getJSONObject(0).getInt("count"),
tappableObject.getJSONArray("tallies").getJSONObject(1).getString("text"),
tappableObject.getJSONArray("tallies").getJSONObject(1).getInt("count"),
tappableObject.optInt("viewer_vote", -1)
));
}
if (data.has("story_questions")) {
final JSONObject tappableObject = data.getJSONArray("story_questions").getJSONObject(0)
.optJSONObject("question_sticker");
if (tappableObject != null && !tappableObject.getString("question_type").equals("music"))
model.setQuestion(new QuestionModel(
String.valueOf(tappableObject.getLong("question_id")),
tappableObject.getString("question")
));
}
if (data.has("story_quizs")) {
JSONObject tappableObject = data.getJSONArray("story_quizs").getJSONObject(0).optJSONObject("quiz_sticker");
if (tappableObject != null) {
String[] choices = new String[tappableObject.getJSONArray("tallies").length()];
Long[] counts = new Long[choices.length];
for (int q = 0; q < choices.length; ++q) {
JSONObject tempchoice = tappableObject.getJSONArray("tallies").getJSONObject(q);
choices[q] = (q == tappableObject.getInt("correct_answer") ? "*** " : "")
+ tempchoice.getString("text");
counts[q] = tempchoice.getLong("count");
}
model.setQuiz(new QuizModel(
String.valueOf(tappableObject.getLong("quiz_id")),
tappableObject.getString("question"),
choices,
counts,
tappableObject.optInt("viewer_answer", -1)
));
}
}
if (data.has("story_cta") && data.has("link_text")) {
JSONObject tappableObject = data.getJSONArray("story_cta").getJSONObject(0).getJSONArray("links").getJSONObject(0);
String swipeUpUrl = tappableObject.optString("webUri");
final String backupSwipeUpUrl = swipeUpUrl;
if (swipeUpUrl != null && swipeUpUrl.startsWith("https://l.instagram.com/")) {
swipeUpUrl = Uri.parse(swipeUpUrl).getQueryParameter("u");
}
if (swipeUpUrl != null && swipeUpUrl.startsWith("http"))
model.setSwipeUp(new SwipeUpModel(swipeUpUrl, data.getString("link_text")));
else if (backupSwipeUpUrl != null && backupSwipeUpUrl.startsWith("http"))
model.setSwipeUp(new SwipeUpModel(backupSwipeUpUrl, data.getString("link_text")));
}
if (data.has("story_sliders")) {
final JSONObject tappableObject = data.getJSONArray("story_sliders").getJSONObject(0)
.optJSONObject("slider_sticker");
if (tappableObject != null)
model.setSlider(new SliderModel(
String.valueOf(tappableObject.getLong("slider_id")),
tappableObject.getString("question"),
tappableObject.getString("emoji"),
tappableObject.getBoolean("viewer_can_vote"),
tappableObject.optDouble("slider_vote_average"),
tappableObject.getInt("slider_vote_count"),
tappableObject.optDouble("viewer_vote")
));
}
JSONArray hashtags = data.optJSONArray("story_hashtags");
JSONArray locations = data.optJSONArray("story_locations");
JSONArray atmarks = data.optJSONArray("reel_mentions");
String[] mentions = new String[(hashtags == null ? 0 : hashtags.length())
+ (atmarks == null ? 0 : atmarks.length())
+ (locations == null ? 0 : locations.length())];
if (hashtags != null) {
for (int h = 0; h < hashtags.length(); ++h) {
mentions[h] = "#" + hashtags.getJSONObject(h).getJSONObject("hashtag").getString("name");
}
}
if (atmarks != null) {
for (int h = 0; h < atmarks.length(); ++h) {
mentions[h + (hashtags == null ? 0 : hashtags.length())] =
"@" + atmarks.getJSONObject(h).getJSONObject("user").getString("username");
}
}
if (locations != null) {
for (int h = 0; h < locations.length(); ++h) {
mentions[h + (hashtags == null ? 0 : hashtags.length()) + (atmarks == null ? 0 : atmarks.length())] =
locations.getJSONObject(h).getJSONObject("location").getString("short_name")
+ " (" + locations.getJSONObject(h).getJSONObject("location").getLong("pk") + ")";
}
}
if (mentions.length != 0) model.setMentions(mentions);
return model;
}
public static String getThumbUrl(final Object media) { public static String getThumbUrl(final Object media) {
return getImageCandidate(media, CandidateType.THUMBNAIL); return getImageCandidate(media, CandidateType.THUMBNAIL);
} }
@ -415,6 +279,7 @@ public final class ResponseBodyUtils {
} }
private static String getImageCandidate(final Object rawMedia, final CandidateType type) { private static String getImageCandidate(final Object rawMedia, final CandidateType type) {
if (rawMedia == null) return null;
final ImageVersions2 imageVersions2; final ImageVersions2 imageVersions2;
final int originalWidth, originalHeight; final int originalWidth, originalHeight;
if (rawMedia instanceof StoryMedia) { if (rawMedia instanceof StoryMedia) {
@ -453,22 +318,34 @@ public final class ResponseBodyUtils {
return getVideoCandidate(media, CandidateType.VIDEO_THUMBNAIL); return getVideoCandidate(media, CandidateType.VIDEO_THUMBNAIL);
} }
public static String getVideoUrl(final Media media) { public static String getVideoUrl(final Object media) {
return getVideoCandidate(media, CandidateType.DOWNLOAD); return getVideoCandidate(media, CandidateType.DOWNLOAD);
} }
// TODO: merge with getImageCandidate when Kotlin // TODO: merge with getImageCandidate when Kotlin
private static String getVideoCandidate(final Media media, final CandidateType type) { private static String getVideoCandidate(final Object rawMedia, final CandidateType type) {
if (media == null) return null; if (rawMedia == null) return null;
final List<MediaCandidate> candidates = media.getVideoVersions(); final List<MediaCandidate> candidates;
final int originalWidth, originalHeight;
if (rawMedia instanceof StoryMedia) {
candidates = ((StoryMedia) rawMedia).getVideoVersions();
originalWidth = ((StoryMedia) rawMedia).getOriginalWidth();
originalHeight = ((StoryMedia) rawMedia).getOriginalHeight();
}
else if (rawMedia instanceof Media) {
candidates = ((Media) rawMedia).getVideoVersions();
originalWidth = ((Media) rawMedia).getOriginalWidth();
originalHeight = ((Media) rawMedia).getOriginalHeight();
}
else return null;
if (candidates == null || candidates.isEmpty()) return null; if (candidates == null || candidates.isEmpty()) return null;
final boolean isSquare = Integer.compare(media.getOriginalWidth(), media.getOriginalHeight()) == 0; final boolean isSquare = Integer.compare(originalWidth, originalHeight) == 0;
final List<MediaCandidate> sortedCandidates = candidates.stream() final List<MediaCandidate> sortedCandidates = candidates.stream()
.sorted((c1, c2) -> Integer.compare(c2.getWidth(), c1.getWidth())) .sorted((c1, c2) -> Integer.compare(c2.getWidth(), c1.getWidth()))
.collect(Collectors.toList()); .collect(Collectors.toList());
final List<MediaCandidate> filteredCandidates = sortedCandidates.stream() final List<MediaCandidate> filteredCandidates = sortedCandidates.stream()
.filter(c -> .filter(c ->
c.getWidth() <= media.getOriginalWidth() c.getWidth() <= originalWidth
&& c.getWidth() <= type.getValue() && c.getWidth() <= type.getValue()
&& (isSquare || Integer && (isSquare || Integer
.compare(c.getWidth(), c.getHeight()) != 0) .compare(c.getWidth(), c.getHeight()) != 0)
@ -480,19 +357,6 @@ public final class ResponseBodyUtils {
return candidate.getUrl(); return candidate.getUrl();
} }
public static StoryModel parseBroadcastItem(final JSONObject data) throws JSONException {
final StoryModel model = new StoryModel(data.getString("id"),
data.getString("cover_frame_url"),
data.getString("cover_frame_url"),
MediaItemType.MEDIA_TYPE_LIVE,
data.optLong("published_time", 0),
data.getJSONObject("broadcast_owner").getString("username"),
data.getJSONObject("broadcast_owner").getLong("pk"),
false);
model.setVideoUrl(data.getString("dash_playback_url"));
return model;
}
private enum CandidateType { private enum CandidateType {
VIDEO_THUMBNAIL(700), VIDEO_THUMBNAIL(700),
THUMBNAIL(1000), THUMBNAIL(1000),

View File

@ -8,7 +8,6 @@ import awais.instagrabber.db.entities.Favorite
import awais.instagrabber.db.repositories.FavoriteRepository import awais.instagrabber.db.repositories.FavoriteRepository
import awais.instagrabber.managers.DirectMessagesManager import awais.instagrabber.managers.DirectMessagesManager
import awais.instagrabber.models.Resource import awais.instagrabber.models.Resource
import awais.instagrabber.models.StoryModel
import awais.instagrabber.models.enums.BroadcastItemType import awais.instagrabber.models.enums.BroadcastItemType
import awais.instagrabber.models.enums.FavoriteType import awais.instagrabber.models.enums.FavoriteType
import awais.instagrabber.repositories.requests.StoryViewerOptions import awais.instagrabber.repositories.requests.StoryViewerOptions
@ -17,6 +16,7 @@ import awais.instagrabber.repositories.responses.User
import awais.instagrabber.repositories.responses.UserProfileContextLink import awais.instagrabber.repositories.responses.UserProfileContextLink
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
import awais.instagrabber.repositories.responses.stories.Story import awais.instagrabber.repositories.responses.stories.Story
import awais.instagrabber.repositories.responses.stories.StoryMedia
import awais.instagrabber.utils.ControlledRunner import awais.instagrabber.utils.ControlledRunner
import awais.instagrabber.utils.Event import awais.instagrabber.utils.Event
import awais.instagrabber.utils.SingleRunner import awais.instagrabber.utils.SingleRunner
@ -153,9 +153,9 @@ class ProfileFragmentViewModel(
} }
} }
private val storyFetchControlledRunner = ControlledRunner<List<StoryModel>?>() private val storyFetchControlledRunner = ControlledRunner<List<StoryMedia>?>()
val userStories: LiveData<Resource<List<StoryModel>?>> = currentUserProfileActionLiveData.switchMap { currentUserAndProfilePair -> val userStories: LiveData<Resource<List<StoryMedia>?>> = currentUserProfileActionLiveData.switchMap { currentUserAndProfilePair ->
liveData<Resource<List<StoryModel>?>>(context = viewModelScope.coroutineContext + ioDispatcher) { liveData<Resource<List<StoryMedia>?>>(context = viewModelScope.coroutineContext + ioDispatcher) {
val (currentUserResource, profileResource, action) = currentUserAndProfilePair val (currentUserResource, profileResource, action) = currentUserAndProfilePair
if (action != INIT && action != REFRESH) { if (action != INIT && action != REFRESH) {
return@liveData return@liveData
@ -231,7 +231,7 @@ class ProfileFragmentViewModel(
return graphQLRepository.fetchUser(stateUsername) return graphQLRepository.fetchUser(stateUsername)
} }
private suspend fun fetchUserStory(fetchedUser: User): List<StoryModel> = storiesRepository.getUserStory( private suspend fun fetchUserStory(fetchedUser: User): List<StoryMedia> = storiesRepository.getStories(
StoryViewerOptions.forUser(fetchedUser.pk, fetchedUser.fullName) StoryViewerOptions.forUser(fetchedUser.pk, fetchedUser.fullName)
) )

View File

@ -5,12 +5,12 @@ import androidx.lifecycle.ViewModel;
import java.util.List; import java.util.List;
import awais.instagrabber.models.StoryModel; import awais.instagrabber.repositories.responses.stories.StoryMedia;
public class StoriesViewModel extends ViewModel { public class StoriesViewModel extends ViewModel {
private MutableLiveData<List<StoryModel>> list; private MutableLiveData<List<StoryMedia>> list;
public MutableLiveData<List<StoryModel>> getList() { public MutableLiveData<List<StoryMedia>> getList() {
if (list == null) { if (list == null) {
list = new MutableLiveData<>(); list = new MutableLiveData<>();
} }

View File

@ -1,26 +1,22 @@
package awais.instagrabber.webservices package awais.instagrabber.webservices
import awais.instagrabber.fragments.settings.PreferenceKeys import awais.instagrabber.fragments.settings.PreferenceKeys
import awais.instagrabber.models.StoryModel
import awais.instagrabber.repositories.StoriesService import awais.instagrabber.repositories.StoriesService
import awais.instagrabber.repositories.requests.StoryViewerOptions import awais.instagrabber.repositories.requests.StoryViewerOptions
import awais.instagrabber.repositories.responses.stories.ArchiveResponse import awais.instagrabber.repositories.responses.stories.ArchiveResponse
import awais.instagrabber.repositories.responses.stories.Story import awais.instagrabber.repositories.responses.stories.Story
import awais.instagrabber.repositories.responses.stories.StoryMedia
import awais.instagrabber.repositories.responses.stories.StoryStickerResponse import awais.instagrabber.repositories.responses.stories.StoryStickerResponse
import awais.instagrabber.utils.ResponseBodyUtils
import awais.instagrabber.utils.TextUtils.isEmpty import awais.instagrabber.utils.TextUtils.isEmpty
import awais.instagrabber.utils.Utils import awais.instagrabber.utils.Utils
import awais.instagrabber.webservices.RetrofitFactory.retrofit import awais.instagrabber.webservices.RetrofitFactory.retrofit
import org.json.JSONArray import java.util.UUID
import org.json.JSONObject
import java.util.*
open class StoriesRepository(private val service: StoriesService) { open class StoriesRepository(private val service: StoriesService) {
suspend fun fetch(mediaId: Long): StoryModel { suspend fun fetch(mediaId: Long): StoryMedia? {
val response = service.fetch(mediaId) val response = service.fetch(mediaId)
val itemJson = JSONObject(response).getJSONArray("items").getJSONObject(0) return response.items?.get(0)
return ResponseBodyUtils.parseStoryItem(itemJson, false, null)
} }
suspend fun getFeedStories(): List<Story> { suspend fun getFeedStories(): List<Story> {
@ -70,31 +66,30 @@ open class StoriesRepository(private val service: StoriesService) {
return service.fetchArchive(form) return service.fetchArchive(form)
} }
open suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> { open suspend fun getStories(options: StoryViewerOptions): List<StoryMedia> {
val url = buildUrl(options) ?: return emptyList() return when (options.type) {
val response = service.getUserStory(url) StoryViewerOptions.Type.HIGHLIGHT,
val isLocOrHashtag = options.type == StoryViewerOptions.Type.LOCATION || options.type == StoryViewerOptions.Type.HASHTAG StoryViewerOptions.Type.STORY_ARCHIVE
val isHighlight = options.type == StoryViewerOptions.Type.HIGHLIGHT || options.type == StoryViewerOptions.Type.STORY_ARCHIVE -> {
var data: JSONObject? = JSONObject(response) val response = service.getReelsMedia(options.name)
data = if (!isHighlight) { val story: Story? = response.reels?.get(options.name)
data?.optJSONObject(if (isLocOrHashtag) "story" else "reel") story?.items ?: emptyList()
} else {
data?.getJSONObject("reels")?.optJSONObject(options.name)
} }
var username: String? = null StoryViewerOptions.Type.USER -> {
if (data != null && !isLocOrHashtag) { val response = service.getUserStories(options.id.toString())
username = data.getJSONObject("user").getString("username") response.reel?.items ?: emptyList()
} }
val media: JSONArray? = data?.optJSONArray("items") // should not reach beyond this point
return if (media?.length() ?: 0 > 0 && media?.optJSONObject(0) != null) { StoryViewerOptions.Type.LOCATION -> {
val mediaLen = media.length() val response = service.getStories("locations", options.id.toString())
val models: MutableList<StoryModel> = ArrayList() response.story?.items ?: emptyList()
for (i in 0 until mediaLen) { }
data = media.getJSONObject(i) StoryViewerOptions.Type.HASHTAG -> {
models.add(ResponseBodyUtils.parseStoryItem(data, isLocOrHashtag, username)) val response = service.getStories("tags", options.name)
response.story?.items ?: emptyList()
}
else -> emptyList()
} }
models
} else emptyList()
} }
private suspend fun respondToSticker( private suspend fun respondToSticker(
@ -102,7 +97,7 @@ open class StoriesRepository(private val service: StoriesService) {
userId: Long, userId: Long,
deviceUuid: String, deviceUuid: String,
storyId: String, storyId: String,
stickerId: String, stickerId: Long,
action: String, action: String,
arg1: String, arg1: String,
arg2: String, arg2: String,
@ -125,7 +120,7 @@ open class StoriesRepository(private val service: StoriesService) {
userId: Long, userId: Long,
deviceUuid: String, deviceUuid: String,
storyId: String, storyId: String,
stickerId: String, stickerId: Long,
answer: String, answer: String,
): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_question_response", "response", answer) ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_question_response", "response", answer)
@ -134,7 +129,7 @@ open class StoriesRepository(private val service: StoriesService) {
userId: Long, userId: Long,
deviceUuid: String, deviceUuid: String,
storyId: String, storyId: String,
stickerId: String, stickerId: Long,
answer: Int, answer: Int,
): StoryStickerResponse { ): StoryStickerResponse {
return respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_quiz_answer", "answer", answer.toString()) return respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_quiz_answer", "answer", answer.toString())
@ -145,7 +140,7 @@ open class StoriesRepository(private val service: StoriesService) {
userId: Long, userId: Long,
deviceUuid: String, deviceUuid: String,
storyId: String, storyId: String,
stickerId: String, stickerId: Long,
answer: Int, answer: Int,
): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_poll_vote", "vote", answer.toString()) ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_poll_vote", "vote", answer.toString())
@ -154,7 +149,7 @@ open class StoriesRepository(private val service: StoriesService) {
userId: Long, userId: Long,
deviceUuid: String, deviceUuid: String,
storyId: String, storyId: String,
stickerId: String, stickerId: Long,
answer: Double, answer: Double,
): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_slider_vote", "vote", answer.toString()) ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_slider_vote", "vote", answer.toString())
@ -182,43 +177,6 @@ open class StoriesRepository(private val service: StoriesService) {
return service.seen(queryMap, signedForm) return service.seen(queryMap, signedForm)
} }
private fun buildUrl(options: StoryViewerOptions): String? {
val builder = StringBuilder()
builder.append("https://i.instagram.com/api/v1/")
val type = options.type
var id: String? = null
when (type) {
StoryViewerOptions.Type.HASHTAG -> {
builder.append("tags/")
id = options.name
}
StoryViewerOptions.Type.LOCATION -> {
builder.append("locations/")
id = options.id.toString()
}
StoryViewerOptions.Type.USER -> {
builder.append("feed/user/")
id = options.id.toString()
}
StoryViewerOptions.Type.HIGHLIGHT, StoryViewerOptions.Type.STORY_ARCHIVE -> {
builder.append("feed/reels_media/?user_ids=")
id = options.name
}
StoryViewerOptions.Type.STORY -> {
}
else -> {
}
}
if (id == null) {
return null
}
builder.append(id)
if (type != StoryViewerOptions.Type.HIGHLIGHT && type != StoryViewerOptions.Type.STORY_ARCHIVE) {
builder.append("/story/")
}
return builder.toString()
}
private fun sort(list: List<Story>): List<Story> { private fun sort(list: List<Story>): List<Story> {
val listCopy = ArrayList(list) val listCopy = ArrayList(list)
listCopy.sortWith { o1, o2 -> listCopy.sortWith { o1, o2 ->

View File

@ -10,12 +10,12 @@ import awais.instagrabber.db.entities.Favorite
import awais.instagrabber.db.repositories.FavoriteRepository import awais.instagrabber.db.repositories.FavoriteRepository
import awais.instagrabber.getOrAwaitValue import awais.instagrabber.getOrAwaitValue
import awais.instagrabber.models.Resource import awais.instagrabber.models.Resource
import awais.instagrabber.models.StoryModel
import awais.instagrabber.models.enums.FavoriteType import awais.instagrabber.models.enums.FavoriteType
import awais.instagrabber.repositories.requests.StoryViewerOptions import awais.instagrabber.repositories.requests.StoryViewerOptions
import awais.instagrabber.repositories.responses.FriendshipStatus import awais.instagrabber.repositories.responses.FriendshipStatus
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.User
import awais.instagrabber.repositories.responses.stories.Story import awais.instagrabber.repositories.responses.stories.Story
import awais.instagrabber.repositories.responses.stories.StoryMedia
import awais.instagrabber.webservices.* import awais.instagrabber.webservices.*
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.json.JSONException import org.json.JSONException
@ -320,13 +320,13 @@ internal class ProfileFragmentViewModelTest {
"username" to testPublicUser.username "username" to testPublicUser.username
) )
) )
val testUserStories = listOf(StoryModel()) val testUserStories = listOf(StoryMedia())
val testUserHighlights = listOf(Story()) val testUserHighlights = listOf(Story())
val userRepository = object : UserRepository(UserServiceAdapter()) { val userRepository = object : UserRepository(UserServiceAdapter()) {
override suspend fun getUsernameInfo(username: String): User = testPublicUser override suspend fun getUsernameInfo(username: String): User = testPublicUser
} }
val storiesRepository = object : StoriesRepository(StoriesServiceAdapter()) { val storiesRepository = object : StoriesRepository(StoriesServiceAdapter()) {
override suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> = testUserStories override suspend fun getStories(options: StoryViewerOptions): List<StoryMedia> = testUserStories
override suspend fun fetchHighlights(profileId: Long): List<Story> = testUserHighlights override suspend fun fetchHighlights(profileId: Long): List<Story> = testUserHighlights
} }
val viewModel = ProfileFragmentViewModel( val viewModel = ProfileFragmentViewModel(