Tidying up the feed feature
This commit is contained in:
parent
9c3f578eb6
commit
93b49f2533
10 changed files with 159 additions and 95 deletions
|
@ -1,5 +1,5 @@
|
||||||
namespace Neighbourhood.omg.lol {
|
namespace Neighbourhood.omg.lol {
|
||||||
public static class FeatureFlags {
|
public static class FeatureFlags {
|
||||||
public static bool Following { get; } = false;
|
public static bool Following { get; } = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,16 +198,18 @@ namespace Neighbourhood.omg.lol {
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsFollowing(string address) => Following?.Contains(address) ?? false;
|
public bool IsFollowing(string address) => Following?.Contains(address) ?? false;
|
||||||
public void Follow(string address) {
|
public async Task Follow(string address) {
|
||||||
if (Following == null) Following = new List<string>();
|
if (Following == null) Following = new List<string>();
|
||||||
Following.Add(address);
|
Following.Add(address);
|
||||||
Preferences.Default.Set("following", JsonSerializer.Serialize(Following));
|
Preferences.Default.Set("following", JsonSerializer.Serialize(Following));
|
||||||
|
await GetFeed(forceRefresh: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unfollow(string address) {
|
public async Task Unfollow(string address) {
|
||||||
if (Following == null) Following = new List<string>();
|
if (Following == null) Following = new List<string>();
|
||||||
Following.Remove(address);
|
Following.Remove(address);
|
||||||
Preferences.Default.Set("following", JsonSerializer.Serialize(Following));
|
Preferences.Default.Set("following", JsonSerializer.Serialize(Following));
|
||||||
|
await GetFeed(forceRefresh: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MarkupString?> GetBio(string address, bool forceRefresh = false) {
|
public async Task<MarkupString?> GetBio(string address, bool forceRefresh = false) {
|
||||||
|
|
|
@ -4,9 +4,13 @@
|
||||||
<i class="emoji medium" data-emoji="👋">👋</i>
|
<i class="emoji medium" data-emoji="👋">👋</i>
|
||||||
<span>Hey, @(State.Name ?? "there").</span>
|
<span>Hey, @(State.Name ?? "there").</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="m s row" href="/directory">
|
<a class="s row" href="/now">
|
||||||
|
<i class="square fa-duotone fa-seedling"></i>
|
||||||
|
<span>Now.garden</span>
|
||||||
|
</a>
|
||||||
|
<a class="s row" href="/directory">
|
||||||
<i class="square fa-duotone fa-address-book"></i>
|
<i class="square fa-duotone fa-address-book"></i>
|
||||||
<span>Address Directory</span>
|
<span>Directory</span>
|
||||||
</a>
|
</a>
|
||||||
@foreach (AddressResponseData address in State.AddressList ?? new List<AddressResponseData>()) {
|
@foreach (AddressResponseData address in State.AddressList ?? new List<AddressResponseData>()) {
|
||||||
<a class="row @(address == State.SelectedAddress ? "active" : "")" @onclick="() => State.SelectedAddress = address">
|
<a class="row @(address == State.SelectedAddress ? "active" : "")" @onclick="() => State.SelectedAddress = address">
|
||||||
|
@ -32,13 +36,7 @@
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if (FeatureFlags.Following && State.IsAuthorized) {
|
|
||||||
<a class="m s row" href="/feed">
|
|
||||||
<i class="square fa-solid fa-list-timeline"></i>
|
|
||||||
<span>Feed</span>
|
|
||||||
</a>
|
|
||||||
<FollowingList class="m s"></FollowingList>
|
|
||||||
}
|
|
||||||
@if (State.IsAuthorized) {
|
@if (State.IsAuthorized) {
|
||||||
<a class="row" @onclick='() => AuthStateProvider.Logout()'>
|
<a class="row" @onclick='() => AuthStateProvider.Logout()'>
|
||||||
<i class="fa-solid fa-door-open"></i>
|
<i class="fa-solid fa-door-open"></i>
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
@inject State State
|
|
||||||
|
|
||||||
@if (State.Following != null) {
|
|
||||||
<details class="@(@class)">
|
|
||||||
<summary class="none">
|
|
||||||
<a class="row">
|
|
||||||
<i class="square fa-solid fa-caret-down"></i>
|
|
||||||
<span>Following</span>
|
|
||||||
</a>
|
|
||||||
</summary>
|
|
||||||
@foreach (string address in State.Following) {
|
|
||||||
<a class="transparent button horizontal-padding indent row" href="/person/@address">
|
|
||||||
<img class="tiny circle avatar" src="https://profiles.cache.lol/@address/picture" />
|
|
||||||
<span class="address"><i class="fa-solid fa-fw fa-at tiny"></i>@address</span>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</details>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code {
|
|
||||||
[Parameter]
|
|
||||||
public string? @class { get; set; }
|
|
||||||
}
|
|
|
@ -11,24 +11,26 @@
|
||||||
<i class="square fa-light fa-comment-dots"></i>
|
<i class="square fa-light fa-comment-dots"></i>
|
||||||
<div class="label">Eph.emer.al</div>
|
<div class="label">Eph.emer.al</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink class="nav-link" href="/now">
|
@if (FeatureFlags.Following && State.IsAuthorized) {
|
||||||
<i class="square fa-duotone fa-seedling"></i>
|
<NavLink class="nav-link" href="/feed">
|
||||||
<div class="label">Now.garden</div>
|
<i class="square fa-solid fa-list-timeline"></i>
|
||||||
</NavLink>
|
<div class="label">Timeline</div>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink class="l m nav-link" href="/now">
|
||||||
<NavLink class="l nav-link" href="/directory">
|
<i class="square fa-duotone fa-seedling"></i>
|
||||||
<i class="square fa-duotone fa-address-book"></i>
|
<div class="label">Now.garden</div>
|
||||||
<div class="label">Address Directory</div>
|
</NavLink>
|
||||||
</NavLink>
|
|
||||||
@if (FeatureFlags.Following) {
|
|
||||||
<AuthorizeView>
|
|
||||||
<Authorized>
|
|
||||||
<NavLink class="l nav-link" href="/feed">
|
|
||||||
<i class="square fa-solid fa-list-timeline"></i>
|
|
||||||
<div class="label">Feed</div>
|
|
||||||
</NavLink>
|
|
||||||
<FollowingList class="l"></FollowingList>
|
|
||||||
</Authorized>
|
|
||||||
</AuthorizeView>
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
<NavLink class="nav-link" href="/now">
|
||||||
|
<i class="square fa-duotone fa-seedling"></i>
|
||||||
|
<div class="label">Now.garden</div>
|
||||||
|
</NavLink>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<NavLink class="l m nav-link" href="/directory">
|
||||||
|
<i class="square fa-duotone fa-address-book"></i>
|
||||||
|
<div class="label">Directory</div>
|
||||||
|
</NavLink>
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
@if (messages != null) {
|
@if (messages != null) {
|
||||||
foreach (MarkupString message in messages) {
|
foreach (MarkupString message in messages) {
|
||||||
<article class="ephemeral center">
|
<article class="ephemeral">
|
||||||
@message
|
@message
|
||||||
</article>
|
</article>
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,29 +2,85 @@
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@inject IJSRuntime JS
|
@inject IJSRuntime JS
|
||||||
@inject State State
|
@inject State State
|
||||||
|
@inject NavigationManager Nav
|
||||||
|
|
||||||
<RefreshButton></RefreshButton>
|
<RefreshButton></RefreshButton>
|
||||||
|
|
||||||
<PageHeading title="Feed" icon="fa-solid fa-list-timeline">
|
<PageHeading title="Timeline" icon="fa-solid fa-list-timeline">
|
||||||
<Description>A feed of all the statuses and pics of the people you follow.</Description>
|
<Description>A feed of all the statuses and pics of the people you follow.</Description>
|
||||||
</PageHeading>
|
</PageHeading>
|
||||||
|
|
||||||
@if (feed != null) foreach (StatusOrPic item in feed) {
|
@if(!(State.Following?.Any() ?? false)) {
|
||||||
if (item.IsStatus) {
|
<PageHeading title="" icon="fa-light fa-face-sad-sweat">
|
||||||
<StatusCard Status="@item.Status"></StatusCard>
|
<Description>
|
||||||
}
|
It looks like you're not following anyone yet.
|
||||||
else if (item.IsPic) {
|
</Description>
|
||||||
<PicCard Pic="@item.Pic"></PicCard>
|
</PageHeading>
|
||||||
}
|
<p class="center-align">Check out the <a href="/directory">Directory</a> (or other parts of the app) to find awesome people to follow.</p>
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
<div class="responsive">
|
||||||
|
<div class="tabs scroll">
|
||||||
|
<a data-ui="#feed" class="active">
|
||||||
|
<i class="fa-solid fa-list-timeline"></i>
|
||||||
|
<span>Timeline</span>
|
||||||
|
</a>
|
||||||
|
<a data-ui="#following">
|
||||||
|
<i class="fa-duotone fa-address-book"></i>
|
||||||
|
<span>Following</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<LoadingCard id="feedLoading" icon="fa-solid fa-list-timeline"></LoadingCard>
|
<div class="responsive page-container">
|
||||||
|
<div id="feed" class="page no-padding active">
|
||||||
|
@if (feed != null){
|
||||||
|
foreach (StatusOrPic item in feed) {
|
||||||
|
if (item.IsStatus) {
|
||||||
|
<StatusCard Status="@item.Status"></StatusCard>
|
||||||
|
}
|
||||||
|
else if (item.IsPic) {
|
||||||
|
<PicCard Pic="@item.Pic"></PicCard>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<LoadingCard id="feedLoading" icon="fa-solid fa-list-timeline"></LoadingCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="following" class="page no-padding">
|
||||||
|
<ul>
|
||||||
|
@foreach (string address in State.Following ?? new List<string>()) {
|
||||||
|
string displayAddress = address;
|
||||||
|
string linkAddress = address;
|
||||||
|
@* if (group.Key == "😀") {
|
||||||
|
try {
|
||||||
|
linkAddress = idn.GetAscii(address);
|
||||||
|
displayAddress = $"{address} {linkAddress}";
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
} *@
|
||||||
|
<li class="vertical-margin row padding surface-container">
|
||||||
|
<img class="round" src="https://profiles.cache.lol/@linkAddress/picture">
|
||||||
|
<div class="max">
|
||||||
|
<a href="/person/@linkAddress" class="address"><i class="fa-solid fa-fw fa-at tiny"></i>@displayAddress</a>
|
||||||
|
</div>
|
||||||
|
<button id="follow-button" @onclick="() => UnfollowClick(address)">
|
||||||
|
<i class="fa-solid fa-minus"></i> Unfollow
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private IOrderedEnumerable<StatusOrPic>? feed;
|
private IOrderedEnumerable<StatusOrPic>? feed;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
|
string fragment = new Uri(Nav.Uri).Fragment;
|
||||||
|
await JS.InvokeVoidAsync("ui", fragment);
|
||||||
if (feed == null || feed.Count() == 0) feed = await State.GetFeed();
|
if (feed == null || feed.Count() == 0) feed = await State.GetFeed();
|
||||||
State.PropertyChanged += StateChanged;
|
State.PropertyChanged += StateChanged;
|
||||||
State.CanRefresh = true;
|
State.CanRefresh = true;
|
||||||
|
@ -41,6 +97,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UnfollowClick(string address) {
|
||||||
|
await State.Unfollow(address);
|
||||||
|
feed = await State.GetFeed(forceRefresh: true);
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
State.PropertyChanged -= StateChanged;
|
State.PropertyChanged -= StateChanged;
|
||||||
State.CanRefresh = false;
|
State.CanRefresh = false;
|
||||||
|
|
|
@ -14,12 +14,12 @@
|
||||||
@if (FeatureFlags.Following) {
|
@if (FeatureFlags.Following) {
|
||||||
<div class="row center-align">
|
<div class="row center-align">
|
||||||
@if (State.IsFollowing(Address)) {
|
@if (State.IsFollowing(Address)) {
|
||||||
<button id="follow-button" @onclick="() => {State.Unfollow(Address);InvokeAsync(StateHasChanged);}">
|
<button id="follow-button" @onclick="async() => {await State.Unfollow(Address);await InvokeAsync(StateHasChanged);}">
|
||||||
<i class="fa-solid fa-minus"></i> Unfollow
|
<i class="fa-solid fa-minus"></i> Unfollow
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
<button id="follow-button" @onclick="() => {State.Follow(Address);InvokeAsync(StateHasChanged);}">
|
<button id="follow-button" @onclick="async() => {await State.Follow(Address);await InvokeAsync(StateHasChanged);}">
|
||||||
<i class="fa-solid fa-plus"></i> Follow
|
<i class="fa-solid fa-plus"></i> Follow
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,38 @@
|
||||||
@inject IJSRuntime JS
|
@inject IJSRuntime JS
|
||||||
|
|
||||||
<article class="no-padding">
|
<article class="pic no-padding">
|
||||||
<img src="@Pic!.Url" loading="lazy">
|
<img src="@Pic!.Url" loading="lazy">
|
||||||
<div class="padding">
|
<div class="padding row">
|
||||||
<nav>
|
<div class="emoji" data-emoji="🖼️">🖼️</div>
|
||||||
<a class="author" href="/person/@Pic.Address#pics">
|
<div class="max">
|
||||||
<i class="fa-solid fa-fw fa-at"></i>@Pic.Address
|
<nav>
|
||||||
</a>
|
<a class="author" href="/person/@Pic.Address#pics">
|
||||||
<span class="max"></span>
|
<i class="fa-solid fa-fw fa-at"></i>@Pic.Address
|
||||||
<a class="chip transparent-border right">
|
</a>
|
||||||
<i class="fa fa-clock"></i> @Pic.RelativeTime
|
<span class="max"></span>
|
||||||
</a>
|
</nav>
|
||||||
</nav>
|
@if(!string.IsNullOrWhiteSpace(Pic.Description)){
|
||||||
@if(!string.IsNullOrWhiteSpace(Pic.Description)){
|
<p>@((MarkupString)Pic.DescriptionHtml)</p>
|
||||||
<p>@((MarkupString)Pic.DescriptionHtml)</p>
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
<div class="padding padding yellow-2-bg yellow-9-fg">
|
|
||||||
<i class="fa-solid fa-triangle-exclamation"></i>
|
|
||||||
<span>This picture needs a description in order to be shared.</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<nav>
|
|
||||||
<div class="max"></div>
|
|
||||||
@if(Editable) {
|
|
||||||
<button @onclick="EditPic"><i class="fa-solid fa-pencil"></i> Edit</button>
|
|
||||||
}
|
}
|
||||||
<button class="transparent circle" @onclick="ShareClick">
|
else {
|
||||||
<i class="fa-solid fa-share-nodes"></i>
|
<div class="padding padding yellow-2-bg yellow-9-fg">
|
||||||
</button>
|
<i class="fa-solid fa-triangle-exclamation"></i>
|
||||||
</nav>
|
<span>This picture needs a description in order to be shared.</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<nav>
|
||||||
|
<a class="chip transparent-border">
|
||||||
|
<i class="fa fa-clock"></i> @Pic.RelativeTime
|
||||||
|
</a>
|
||||||
|
<div class="max"></div>
|
||||||
|
@if(Editable) {
|
||||||
|
<button @onclick="EditPic"><i class="fa-solid fa-pencil"></i> Edit</button>
|
||||||
|
}
|
||||||
|
<button class="transparent circle" @onclick="ShareClick">
|
||||||
|
<i class="fa-solid fa-share-nodes"></i>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
--background: var(--gray-8);
|
--background: var(--gray-8);
|
||||||
--shadow: var(--black);
|
--shadow: var(--black);
|
||||||
--button-shadow: var(--gray-7);
|
--button-shadow: var(--gray-7);
|
||||||
|
--max-article-size: 75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.dark {
|
body.dark {
|
||||||
|
@ -83,7 +84,7 @@ img {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status .emoji {
|
:is(.status,.pic) .emoji {
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
inline-size: 5.5rem;
|
inline-size: 5.5rem;
|
||||||
block-size: 5.5rem;
|
block-size: 5.5rem;
|
||||||
|
@ -92,7 +93,7 @@ img {
|
||||||
text-indent: -10px;
|
text-indent: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status .emoji, #status-emoji, #new-status-emoji {
|
:is(.status,.pic) .emoji, #status-emoji, #new-status-emoji {
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
inline-size: 3.5rem;
|
inline-size: 3.5rem;
|
||||||
block-size: 3.5rem;
|
block-size: 3.5rem;
|
||||||
|
@ -101,6 +102,10 @@ img {
|
||||||
text-indent: -6px;
|
text-indent: -6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:not(#feed)>.pic .emoji{
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
:is(h1, h2, h3, h4, h5, h6) :is(i.fa-at, i:only-child){
|
:is(h1, h2, h3, h4, h5, h6) :is(i.fa-at, i:only-child){
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
inline-size: auto;
|
inline-size: auto;
|
||||||
|
@ -296,6 +301,10 @@ article.ephemeral {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#now-garden{
|
||||||
|
gap:1rem;
|
||||||
|
}
|
||||||
|
|
||||||
#now-garden > :not(.now) {
|
#now-garden > :not(.now) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
@ -476,3 +485,14 @@ menu > details .row, menu > li > details .row {
|
||||||
menu > details > a:is(:hover,:focus,.active), menu > details > summary:is(:hover,:focus,.active) {
|
menu > details > a:is(:hover,:focus,.active), menu > details > summary:is(:hover,:focus,.active) {
|
||||||
background-color: var(--active);
|
background-color: var(--active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--max-article-size);
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
article.now {
|
||||||
|
margin: 0;
|
||||||
|
width:auto;
|
||||||
|
}
|
Loading…
Reference in a new issue