Skip to content

Commit

Permalink
Integrate "Edit Profile" into the Profile page.
Browse files Browse the repository at this point in the history
Reduces the number of pages by 1. Woo.

Also:
* Upgraded Svelte hoping it would fix a bug. It didn't.
* Delete dead code in Button.
  • Loading branch information
Cody Casterline committed Jul 3, 2023
1 parent b241455 commit b420c40
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 132 deletions.
26 changes: 1 addition & 25 deletions web-client/components/Button.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="buttonPosition {$$props.class}">
<div class="button" class:disabled class:confirmationMode on:mouseup={clicked} on:mouseleave={onMouseLeave}>
<div class="button" class:disabled on:mouseup={clicked}>
<slot/>
</div>
</div>
Expand All @@ -19,10 +19,6 @@ export let disabled = false
// Otherwise, we open up a new window (tab) to the new external URL.
export let href = ""
// This button requires confirmation, and is currently asking for confirmation
// TODO: Get rid of confirmation. I think I've stopped using it everywhere. also get rid of buttonPositon container.
let confirmationMode = false
let dispatcher = createEventDispatcher()
function clicked(event: MouseEvent) {
Expand All @@ -42,13 +38,9 @@ function clicked(event: MouseEvent) {
return
}
confirmationMode = false
dispatcher("click")
}
function onMouseLeave() {
confirmationMode = false
}
</script>

Expand Down Expand Up @@ -81,28 +73,12 @@ function onMouseLeave() {
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);
}
.button.confirmationMode {
animation-name: buttonConfirm;
animation-duration: 150ms;
animation-iteration-count: 5;
animation-fill-mode: forwards;
animation-direction: alternate;
animation-timing-function: ease-in-out;
}
/* Required so that we can use position: absolute on the confirmation box. */
.buttonPosition {
position: relative;
}
@keyframes swoopUp {
from {
bottom: 0%;
opacity: 0;
}
}
@keyframes buttonConfirm {
to{
background-color: red;
Expand Down
22 changes: 8 additions & 14 deletions web-client/components/EditProfile.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import type { AppState } from "../ts/app";
import type { Writable } from "svelte/store";
import { UserID as ClientUserID, protobuf as pb, getInner } from "../ts/client";
import { parseUserID, validateServerURL } from "../ts/common";
import { getContext, tick } from "svelte";
import { getContext, onMount, tick } from "svelte";
import InputBox from "./InputBox.svelte";
import { DateTime } from "luxon";
import { decodeBase58 } from "../ts/fbBase58";
Expand All @@ -49,8 +49,6 @@ let appState: Writable<AppState> = getContext("appStateStore")
// Exported so that EditorWithPreview can preview, serialize, & send it for us.
export let item: pb.Item
// Possibly imported, so we can start editing an existing profile:
export let initialItem: pb.Item|undefined
export let validationErrors: string[] = []
$: validationErrors = function(): string[] {
Expand Down Expand Up @@ -164,26 +162,24 @@ $: {
}
}
// This is the inverse of $: itemProto above. Given an Item, load data from it.
// This is the inverse of `$: item = ...` below. Given an Item, load data from it.
function loadFromProto(item: pb.Item) {
let profile = getInner(item, "profile")!
displayName = profile.displayName
profileContent = profile.about
let profile = getInner(item, "profile")
displayName = profile?.displayName ?? ""
profileContent = profile?.about ?? ""
let _follows = new Array<FollowEntry>()
profile.follows.forEach((follow) => {
profile?.follows.forEach((follow) => {
let f = new FollowEntry(ClientUserID.fromBytes(follow.user!.bytes).toString(), follow.displayName)
_follows.push(f)
})
follows = _follows
servers = profile.servers.map((s) => { return {url: s.url} })
servers = profile?.servers.map((s) => { return {url: s.url} }) ?? []
}
if (initialItem) {
loadFromProto(initialItem)
}
loadFromProto(item)
$: item = function(): pb.Item {
Expand Down Expand Up @@ -221,6 +217,4 @@ $: item = function(): pb.Item {
return item
}()
</script>
19 changes: 11 additions & 8 deletions web-client/components/EditorWithPreview.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<PageHeading />
{#if !reuse}
<PageHeading />

<TabBar {tabs} bind:activeTab/>
<TabBar {tabs} bind:activeTab/>
{/if}

{#if activeTab == "Edit"}
{#if mode === "profile"}
<EditProfile
{initialItem}
bind:validationErrors
bind:item
bind:this={editProfile}
Expand Down Expand Up @@ -71,12 +72,14 @@ let appState: Writable<AppState> = getContext("appStateStore")
// There will probably be a separate, inline editor for "comment" types since they'll be simpler.
export let mode: "post"|"profile" = "post"
let tabs = ["Edit", "Preview"]
let activeTab = "Edit"
// A hack to let me re-use this w/o tearing out all the logic:
export let reuse = false
// Can provide an initial item for editing.
export let initialItem: pb.Item|undefined = undefined
type TabType = "Edit" | "Preview"
let tabs: TabType[] = ["Edit", "Preview"]
export let activeTab: TabType = "Edit"
// Can provide an initial item for editing.
let fileAttachments: FileInfo[] = []
let userID: ClientUserID
Expand All @@ -89,7 +92,7 @@ let warnings: string[] = []
// The Item protobuf generated by EditProfile/EditPost.
let item: pb.Item = new pb.Item()
export let item: pb.Item = new pb.Item()
let editPost: EditPost|undefined = undefined
let editProfile: EditProfile|undefined = undefined
Expand Down
6 changes: 5 additions & 1 deletion web-client/components/ItemHeader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
<div class="header">
<ProfileImage {userID} />
<div class="text">
<UserIdView {userID} />
{#if profile}
<UserIdView {userID} displayName={profile.displayName} resolve={false} shouldLink={!previewMode}/>
{:else}
<UserIdView {userID} shouldLink={!previewMode} />
{/if}
{#if showReplyTo && comment}
<a href={refToLink(comment.replyTo)}>
replied to
Expand Down
68 changes: 46 additions & 22 deletions web-client/components/ItemView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import UserIdView from "./UserIDView.svelte"
import CommentView from "./CommentView.svelte"
import ItemHeader from "./ItemHeader.svelte"
import { createEventDispatcher, getContext, tick } from "svelte";
import UserIdChip from "./UserIDChip.svelte";
export let userID: string
export let signature: string
Expand Down Expand Up @@ -223,6 +224,7 @@ function leftPage() {
</div>
</div>
{:else}<!-- item && !loadError-->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="item"
id={signature}
Expand Down Expand Up @@ -264,37 +266,50 @@ function leftPage() {
{:else if profile}
<ItemHeader item={loadedItem} userID={UserID.fromString(userID)} {signature} {previewMode} bind:viewMode />
<div class="body">
{#if profile.displayName}
<h1>{profile.displayName}</h1>
{/if}
<div class="userIDInfo">
id: <UserIdView userID={UserID.fromString(userID)} resolve={false} shouldLink={false} />
</div>
{#if viewMode == "normal"}
{@html markdownToHtml(profile.about, {relativeBase: `/u/${userID}/i/${signature}`})}
{:else if viewMode == "markdown"}
{#if viewMode == "markdown"}
<p>Markdown source:</p>
<code><pre>{profile.about}</pre></code>
{:else}
{:else if viewMode == "data"}
<p>JSON representation of Protobuf Item:</p>
<code><pre>{JSON.stringify(loadedItem, null, 4)}</pre></code>
{:else}
{@html markdownToHtml(profile.about, {relativeBase: `/u/${userID}/i/${signature}`})}

{#if validFollows.length > 0}
<h2>Follows</h2>
<user-follows>
{#each validFollows as follow}
<UserIdChip
userID={follow.userID}
displayName={follow.displayName}
shouldLink={!previewMode}
/>
{:else}
<div>(None)</div>
{/each}
</user-follows>
{/if}

{#if profile.servers.length > 0}
<h2>Servers</h2>
<ul>
{#each profile.servers as server (server)}
<!-- NOT hyperlinking this for now, in case someone tries to inject a javascript: link. -->
<li><code>{server.url}</code></li>
{:else}
<li>(None)</li>
{/each}
</ul>
{/if}
{/if}

<h2>Follows</h2>
<ul>
{#each validFollows as follow}
<li><UserIdView userID={follow.userID} displayName={follow.displayName} resolve={false}/></li>
{:else}
<li>(None)</li>
{/each}
</ul>

<h2>Servers</h2>
<ul>
{#each profile.servers as server (server)}
<!-- NOT hyperlinking this for now, in case someone tries to inject a javascript: link. -->
<li><code>{server.url}</code></li>
{:else}
<li>(None)</li>
{/each}
</ul>

</div>
{:else if loadedItem.itemType.case == "comment"}
<CommentView {showReplyTo} item={loadedItem}
Expand All @@ -321,6 +336,10 @@ function leftPage() {
color: #888;
}
h1:has(+ .userIDInfo) {
margin-bottom: 0;
}
.shrinkImages .body :global(img) {
height: 5px;
}
Expand All @@ -330,4 +349,9 @@ function leftPage() {
box-shadow: 0px 5px 20px rgb(0 0 0 / 80%);
}
user-follows {
display: flex;
flex-wrap: wrap;
}
</style>
8 changes: 1 addition & 7 deletions web-client/components/PageHeading.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -196,20 +196,14 @@ function setNav(app: AppState) {
isActive: new UrlPattern("/u/:uid/profile").match(pagePath),
})
// TODO: Inline "New Post" and "Edit Profile" into the Post/Profile page.
// TODO: Inline "New Post" into the Post/Profile page.
if (isMe) {
navs.push({
text: `New Post`,
href: `#/u/${uid}/post`,
isActive: new UrlPattern("/u/:uid/post").match(pagePath),
})
navs.push({
text: `Edit Profile`,
href: `#/u/${uid}/profile/edit`,
isActive: new UrlPattern("/u/:uid/profile/edit").match(pagePath),
})
navs.push({
text: `Sync`,
href: `#/u/${uid}/sync`,
Expand Down
15 changes: 13 additions & 2 deletions web-client/components/SignAndSend.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,14 @@ async function trackerSubmit(tracker: TaskTracker): Promise<void> {
}
// Save this before onSendSuccess(), because it could change values:
let navigateDestination = `#/u/${userID}/i/${sig}/`
const isProfileUpdate = item.itemType.case == "profile"
let navigateDestination =
isProfileUpdate ? `#/u/${userID}/profile`
: `#/u/${userID}/i/${sig}/`
if (isProfileUpdate) {
$appState.userProfileChanged()
}
await tracker.runSubtask("executing onSendSuccess()", async (tracker) => {
// Mostly here to catch and report an exception in the handler:
Expand All @@ -290,7 +297,11 @@ async function trackerSubmit(tracker: TaskTracker): Promise<void> {
}
if (navigateWhenDone) {
window.location.hash = navigateDestination
if (window.location.hash == navigateDestination) {
window.location.reload()
} else {
window.location.hash = navigateDestination
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions web-client/components/TabBar.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
<!-- a tab bar that sits atop an item. -->
<div class="tabBar" transition:slide|local>
<div class="tabBar">
{#each tabs as tabName, idx (tabName)}
<span class="tab" class:inactive={idx != activeIdx} on:click={() => setActiveIndex(idx)}>{tabName}</span>
{/each}
</div>

<script lang="ts">
import { slide } from "svelte/transition";
export let tabs: string[] = []
Expand Down
36 changes: 36 additions & 0 deletions web-client/components/UserIDChip.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!--
A "chip" element w/ userID, and their icon.
-->

<user-chip>
<ProfileImage {userID} size="line" />
<UserIDView {userID} {displayName} {href} {shouldLink} />
</user-chip>

<script lang="ts">
import type { UserID } from "feoblog-client";
import UserIDView from "./UserIDView.svelte"
import ProfileImage from "./ProfileImage.svelte";
export let userID: UserID
export let displayName: string = ""
export let href: string|undefined = undefined
export let shouldLink = true
</script>

<style>
user-chip {
display: inline-block;
border-radius: 5px;
/* TODO: Same as others */
background-color: #eee;
padding: 0.25em;
margin: 0.25em;
white-space: nowrap;
max-width: 20em;
text-overflow: ellipsis;
}
</style>
Loading

0 comments on commit b420c40

Please sign in to comment.