diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/TrendsActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/TrendsActivity.java index 4a7429f2..7875c182 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/TrendsActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/TrendsActivity.java @@ -33,6 +33,7 @@ import app.fedilab.android.R; import app.fedilab.android.databinding.ActivityTrendsBinding; import app.fedilab.android.mastodon.client.entities.app.Timeline; import app.fedilab.android.mastodon.helper.Helper; +import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonLink; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTag; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline; @@ -56,6 +57,7 @@ public class TrendsActivity extends BaseBarActivity { binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots))); + binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.links))); binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { @@ -75,6 +77,8 @@ public class TrendsActivity extends BaseBarActivity { fragmentMastodonTimeline.scrollToTop(); } else if (fragment instanceof FragmentMastodonTag fragmentMastodonTag) { fragmentMastodonTag.scrollToTop(); + }else if (fragment instanceof FragmentMastodonLink fragmentMastodonLink) { + fragmentMastodonLink.scrollToTop(); } } } @@ -129,11 +133,17 @@ public class TrendsActivity extends BaseBarActivity { bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_TAG); fragmentMastodonTag.setArguments(bundle); return fragmentMastodonTag; + } else if(position == 1) { + FragmentMastodonTimeline fragmentMastodonTimeline = new FragmentMastodonTimeline(); + bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_MESSAGE); + fragmentMastodonTimeline.setArguments(bundle); + return fragmentMastodonTimeline; + } else { + FragmentMastodonLink fragmentMastodonLink = new FragmentMastodonLink(); + bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_LINK); + fragmentMastodonLink.setArguments(bundle); + return fragmentMastodonLink; } - FragmentMastodonTimeline fragmentMastodonTimeline = new FragmentMastodonTimeline(); - bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_MESSAGE); - fragmentMastodonTimeline.setArguments(bundle); - return fragmentMastodonTimeline; } @Override @@ -142,7 +152,7 @@ public class TrendsActivity extends BaseBarActivity { @Override public int getCount() { - return 2; + return 3; } } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/endpoints/MastodonTimelinesService.java b/app/src/main/java/app/fedilab/android/mastodon/client/endpoints/MastodonTimelinesService.java index 1722eb79..634a2863 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/client/endpoints/MastodonTimelinesService.java +++ b/app/src/main/java/app/fedilab/android/mastodon/client/endpoints/MastodonTimelinesService.java @@ -18,6 +18,7 @@ import java.util.List; import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Conversation; +import app.fedilab.android.mastodon.client.entities.api.Link; import app.fedilab.android.mastodon.client.entities.api.Marker; import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.mastodon.client.entities.api.Status; @@ -80,6 +81,11 @@ public interface MastodonTimelinesService { @Query("offset") Integer offset, @Query("limit") Integer limit); + @GET("trends/links") + Call> getLinkTrends(@Header("Authorization") String token, + @Query("offset") Integer offset, + @Query("limit") Integer limit); + //Public Tags timelines @GET("timelines/tag/{hashtag}") Call> getHashTag( diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Link.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Link.java new file mode 100644 index 00000000..b05f5889 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Link.java @@ -0,0 +1,52 @@ +package app.fedilab.android.mastodon.client.entities.api; +/* Copyright 2025 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + +import com.google.gson.annotations.SerializedName; +import java.io.Serializable; +import java.util.List; + +public class Link implements Serializable { + @SerializedName("url") + public String url; + @SerializedName("title") + public String title; + @SerializedName("description") + public String description; + @SerializedName("type") + public String type; + @SerializedName("author_name") + public String author_name; + @SerializedName("author_url") + public String author_url; + @SerializedName("provider_name") + public String provider_name; + @SerializedName("provider_url") + public String provider_url; + @SerializedName("html") + public String html; + @SerializedName("width") + public int width; + @SerializedName("height") + public int height; + @SerializedName("image") + public String image; + @SerializedName("embed_url") + public String embed_url; + @SerializedName("blurhash") + public String blurhash; + @SerializedName("history") + public List history; +} diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/Timeline.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/Timeline.java index a475d237..5877bdb7 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/Timeline.java +++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/Timeline.java @@ -378,6 +378,8 @@ public class Timeline { REMOTE("REMOTE"), @SerializedName("TREND_TAG") TREND_TAG("TREND_TAG"), + @SerializedName("TREND_LINK") + TREND_LINK("TREND_LINK"), @SerializedName("TREND_MESSAGE") TREND_MESSAGE("TREND_MESSAGE"), @SerializedName("ACCOUNT_SUGGESTION") diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/LinkAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/LinkAdapter.java new file mode 100644 index 00000000..a86b7e31 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/LinkAdapter.java @@ -0,0 +1,181 @@ +package app.fedilab.android.mastodon.ui.drawer; +/* Copyright 2025 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.core.app.ActivityOptionsCompat; +import androidx.preference.PreferenceManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.CenterInside; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; +import com.github.mikephil.charting.components.Description; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.android.R; +import app.fedilab.android.databinding.DrawerLinkBinding; +import app.fedilab.android.mastodon.activities.MediaActivity; +import app.fedilab.android.mastodon.client.entities.api.Attachment; +import app.fedilab.android.mastodon.client.entities.api.History; +import app.fedilab.android.mastodon.client.entities.api.Link; +import app.fedilab.android.mastodon.client.entities.app.CachedBundle; +import app.fedilab.android.mastodon.helper.Helper; + +public class LinkAdapter extends RecyclerView.Adapter { + private final List linkList; + private Context context; + + public LinkAdapter(List linkList) { + this.linkList = linkList; + } + + public int getCount() { + return linkList.size(); + } + + public Link getItem(int position) { + return linkList.get(position); + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + context = parent.getContext(); + DrawerLinkBinding itemBinding = DrawerLinkBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + return new LinkViewHolder(itemBinding); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + Link link = linkList.get(position); + LinkViewHolder linkViewHolder = (LinkViewHolder) viewHolder; + linkViewHolder.binding.linkTitle.setText(link.title); + linkViewHolder.binding.linkDescription.setText(link.description); + linkViewHolder.binding.linkAuthor.setText(link.provider_name); + + if(link.author_url != null && link.author_url.startsWith("http")) { + linkViewHolder.binding.linkAuthor.setText(link.provider_name + " (" +link.author_name + ")"); + linkViewHolder.binding.linkAuthor.setOnClickListener(v->{ + Helper.openBrowser(context, link.author_url); + }); + } + + if(link.image != null) { + Glide.with(context).load(link.image) .apply(new RequestOptions().transform(new CenterInside(), new RoundedCorners(10))).into(linkViewHolder.binding.linkImage); + linkViewHolder.binding.linkImage.setVisibility(View.VISIBLE); + linkViewHolder.binding.linkImage.setOnClickListener(v->{ + Intent intent = new Intent(context, MediaActivity.class); + Bundle args = new Bundle(); + Attachment attachment = new Attachment(); + attachment.preview_url = link.image; + attachment.url = link.image; + attachment.remote_url = link.image; + attachment.type = "image"; + ArrayList attachments = new ArrayList<>(); + attachments.add(attachment); + args.putSerializable(Helper.ARG_MEDIA_ARRAY, attachments); + args.putInt(Helper.ARG_MEDIA_POSITION, 1); + new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { + Bundle bundle = new Bundle(); + bundle.putLong(Helper.ARG_INTENT_ID, bundleId); + intent.putExtras(bundle); + ActivityOptionsCompat options = ActivityOptionsCompat + .makeSceneTransitionAnimation(((Activity)context), linkViewHolder.binding.linkImage, attachment.url); + // start the new activity + context.startActivity(intent, options.toBundle()); + }); + }); + } else { + linkViewHolder.binding.linkImage.setVisibility(View.GONE); + } + + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); + if (sharedpreferences.getBoolean(context.getString(R.string.SET_CARDVIEW), false)) { + linkViewHolder.binding.cardviewContainer.setCardElevation(Helper.convertDpToPixel(5, context)); + linkViewHolder.binding.dividerCard.setVisibility(View.GONE); + } + + List trendsEntry = new ArrayList<>(); + + List historyList = link.history; + + + + if (historyList != null) { + for (History history : historyList) { + trendsEntry.add(0, new Entry(Float.parseFloat(history.day), Float.parseFloat(history.uses))); + } + } + + LineDataSet dataTrending = new LineDataSet(trendsEntry, context.getString(R.string.trending)); + dataTrending.setDrawValues(false); + dataTrending.setDrawFilled(true); + dataTrending.setDrawCircles(false); + dataTrending.setDrawCircleHole(false); + linkViewHolder.binding.chart.getAxis(YAxis.AxisDependency.LEFT).setEnabled(false); + linkViewHolder.binding.chart.getAxis(YAxis.AxisDependency.RIGHT).setEnabled(false); + linkViewHolder.binding.chart.getXAxis().setEnabled(false); + linkViewHolder.binding.chart.getLegend().setEnabled(false); + linkViewHolder.binding.chart.setTouchEnabled(false); + dataTrending.setMode(LineDataSet.Mode.CUBIC_BEZIER); + Description description = linkViewHolder.binding.chart.getDescription(); + description.setEnabled(false); + List dataSets = new ArrayList<>(); + + + dataSets.add(dataTrending); + + LineData data = new LineData(dataSets); + linkViewHolder.binding.chart.setData(data); + linkViewHolder.binding.chart.invalidate(); + + linkViewHolder.binding.getRoot().setOnClickListener(v -> Helper.openBrowser(context, link.url)); + } + + @Override + public int getItemCount() { + return linkList.size(); + } + + + public static class LinkViewHolder extends RecyclerView.ViewHolder { + DrawerLinkBinding binding; + + LinkViewHolder(DrawerLinkBinding itemView) { + super(itemView.getRoot()); + binding = itemView; + } + } +} diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonLink.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonLink.java new file mode 100644 index 00000000..54a2db01 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonLink.java @@ -0,0 +1,178 @@ +package app.fedilab.android.mastodon.ui.fragment.timeline; +/* Copyright 2025 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + + +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.preference.PreferenceManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.android.BaseMainActivity; +import app.fedilab.android.R; +import app.fedilab.android.databinding.FragmentPaginationBinding; +import app.fedilab.android.mastodon.activities.SearchResultTabActivity; +import app.fedilab.android.mastodon.client.entities.api.Link; +import app.fedilab.android.mastodon.helper.MastodonHelper; +import app.fedilab.android.mastodon.ui.drawer.LinkAdapter; +import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM; + + +public class FragmentMastodonLink extends Fragment { + + + private FragmentPaginationBinding binding; + private LinkAdapter linkAdapter; + private Integer offset; + private boolean flagLoading; + private List linkList; + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + + binding = FragmentPaginationBinding.inflate(inflater, container, false); + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()); + boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false); + binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar); + return binding.getRoot(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + binding.loader.setVisibility(View.VISIBLE); + binding.recyclerView.setVisibility(View.GONE); + offset = 0; + flagLoading = false; + binding.swipeContainer.setRefreshing(false); + binding.swipeContainer.setEnabled(false); + router(); + } + + /** + * Router for timelines + */ + private void router() { + TimelinesVM timelinesVM = new ViewModelProvider(FragmentMastodonLink.this).get(TimelinesVM.class); + timelinesVM.getLinksTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, offset, MastodonHelper.SEARCH_PER_CALL) + .observe(getViewLifecycleOwner(), links -> { + if (links != null && offset == 0) { + initializeLinkCommonView(links); + } else if (links != null) { + dealWithPaginationTag(links); + } + }); + } + + public void scrollToTop() { + binding.recyclerView.setAdapter(linkAdapter); + } + + private void dealWithPaginationTag(final List links) { + if (binding == null || !isAdded() || getActivity() == null) { + return; + } + if (links == null || links.isEmpty()) { + flagLoading = true; + binding.loadingNextElements.setVisibility(View.GONE); + return; + } + offset += MastodonHelper.SEARCH_PER_CALL; + binding.swipeContainer.setRefreshing(false); + binding.loadingNextElements.setVisibility(View.GONE); + flagLoading = false; + int start = linkList.size(); + linkList.addAll(links); + linkAdapter.notifyItemRangeInserted(start, links.size()); + } + + /** + * Initialize the view for links + * + * @param links List of {@link Link} + */ + private void initializeLinkCommonView(final List links) { + if (binding == null || !isAdded() || getActivity() == null) { + return; + } + linkList = new ArrayList<>(); + binding.loader.setVisibility(View.GONE); + binding.noAction.setVisibility(View.GONE); + binding.swipeContainer.setRefreshing(false); + binding.swipeContainer.setOnRefreshListener(() -> { + binding.swipeContainer.setRefreshing(true); + router(); + }); + if (links == null || links.isEmpty()) { + if (requireActivity() instanceof SearchResultTabActivity) { + ((SearchResultTabActivity) requireActivity()).tagEmpty = true; + if (((SearchResultTabActivity) requireActivity()).accountEmpty != null) { + if (((SearchResultTabActivity) requireActivity()).accountEmpty) { + ((SearchResultTabActivity) requireActivity()).moveToMessage(); + } else { + ((SearchResultTabActivity) requireActivity()).moveToAccount(); + } + } + + } + binding.recyclerView.setVisibility(View.GONE); + binding.noAction.setVisibility(View.VISIBLE); + binding.noActionText.setText(R.string.no_tags); + return; + } + offset += MastodonHelper.SEARCH_PER_CALL; + binding.recyclerView.setVisibility(View.VISIBLE); + binding.noAction.setVisibility(View.GONE); + linkList.addAll(links); + linkAdapter = new LinkAdapter(linkList); + LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity()); + binding.recyclerView.setLayoutManager(mLayoutManager); + binding.recyclerView.setAdapter(linkAdapter); + binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + + int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); + if (dy > 0) { + int visibleItemCount = mLayoutManager.getChildCount(); + int totalItemCount = mLayoutManager.getItemCount(); + + if (firstVisibleItem + visibleItemCount == totalItemCount) { + if (!flagLoading) { + flagLoading = true; + binding.loadingNextElements.setVisibility(View.VISIBLE); + router(); + } + } else { + binding.loadingNextElements.setVisibility(View.GONE); + } + } + } + }); + } + +} \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/TimelinesVM.java b/app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/TimelinesVM.java index 3f1e4da6..ac4a446e 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/TimelinesVM.java +++ b/app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/TimelinesVM.java @@ -49,6 +49,7 @@ import app.fedilab.android.mastodon.client.endpoints.MastodonTimelinesService; import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Conversation; import app.fedilab.android.mastodon.client.entities.api.Conversations; +import app.fedilab.android.mastodon.client.entities.api.Link; import app.fedilab.android.mastodon.client.entities.api.Marker; import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.mastodon.client.entities.api.Pagination; @@ -95,6 +96,7 @@ public class TimelinesVM extends AndroidViewModel { private MutableLiveData markerMutableLiveData; private MutableLiveData> statusListMutableLiveData; private MutableLiveData> tagListMutableLiveData; + private MutableLiveData> linkListMutableLiveData; public TimelinesVM(@NonNull Application application) { super(application); @@ -244,6 +246,30 @@ public class TimelinesVM extends AndroidViewModel { return tagListMutableLiveData; } + public LiveData> getLinksTrends(String token, @NonNull String instance, Integer offset, Integer limit) { + MastodonTimelinesService mastodonTimelinesService = init(instance); + linkListMutableLiveData = new MutableLiveData<>(); + new Thread(() -> { + Call> publicTlCall = mastodonTimelinesService.getLinkTrends(token, offset, limit); + List linkList = null; + if (publicTlCall != null) { + try { + Response> publicTlResponse = publicTlCall.execute(); + if (publicTlResponse.isSuccessful()) { + linkList = publicTlResponse.body(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + Handler mainHandler = new Handler(Looper.getMainLooper()); + List finalLinkList = linkList; + Runnable myRunnable = () -> linkListMutableLiveData.setValue(finalLinkList); + mainHandler.post(myRunnable); + }).start(); + return linkListMutableLiveData; + } + /** * Public timeline for Nitter * diff --git a/app/src/main/res/layouts/mastodon/layout/drawer_link.xml b/app/src/main/res/layouts/mastodon/layout/drawer_link.xml new file mode 100644 index 00000000..21b0bc72 --- /dev/null +++ b/app/src/main/res/layouts/mastodon/layout/drawer_link.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aa8cdfc9..0b1f06ce 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,6 +19,7 @@ Email Accounts Messages + Links Tags Save Instance @@ -1383,6 +1384,7 @@ Set the delay between each new fetch Fetch notifications every: Notifications fetch time + Image attached to the link u+1f604