summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-04-05 10:46:09 -0400
committerFreya Murphy <freya@freyacat.org>2024-04-05 10:46:09 -0400
commit530bbf058781e00e588f1457b6ee589a64b74da1 (patch)
treec6cdd382a86d55538686293e51a1fc056cb59029
parentremove var (diff)
downloadxssbook2-530bbf058781e00e588f1457b6ee589a64b74da1.tar.gz
xssbook2-530bbf058781e00e588f1457b6ee589a64b74da1.tar.bz2
xssbook2-530bbf058781e00e588f1457b6ee589a64b74da1.zip
i did thing oh god large commit
-rw-r--r--src/db/migrations/0000.sql12
-rw-r--r--src/db/rest/media/_api_serve_system_media.sql (renamed from src/db/rest/util/_api_serve_media.sql)20
-rw-r--r--src/db/rest/media/_api_serve_user_media.sql37
-rw-r--r--src/db/rest/media/_api_serve_user_or_default_media.sql41
-rw-r--r--src/db/rest/media/api_profile_avatar.sql30
-rw-r--r--src/db/rest/media/api_profile_banner.sql13
-rw-r--r--src/db/rest/post/api_post.sql15
-rw-r--r--src/db/rest/rest.sql4
-rw-r--r--src/db/rest/user/api_user.sql67
-rw-r--r--src/db/rest/util/_api_raise_not_found.sql16
-rw-r--r--src/public/css/common.css35
-rw-r--r--src/public/css/home.css7
-rw-r--r--src/public/css/people.css50
-rw-r--r--src/public/css/post.css16
-rw-r--r--src/public/css/profile.css43
-rw-r--r--src/public/js/lib.js16
-rw-r--r--src/public/js/post.js21
-rw-r--r--src/web/_controller/_util/post.php8
-rw-r--r--src/web/_controller/apps/people.php7
-rw-r--r--src/web/_controller/apps/profile.php6
-rw-r--r--src/web/_model/apps/people.php62
-rw-r--r--src/web/_views/apps/home/main.php2
-rw-r--r--src/web/_views/apps/people/card.php26
-rw-r--r--src/web/_views/apps/people/footer.php3
-rw-r--r--src/web/_views/apps/people/header.php6
-rw-r--r--src/web/_views/apps/people/main.php121
-rw-r--r--src/web/_views/apps/profile/main.php173
-rw-r--r--src/web/_views/header.php16
-rw-r--r--src/web/_views/modal/new_post.php2
-rw-r--r--src/web/_views/template/comment.php2
-rw-r--r--src/web/_views/template/pfp.php17
-rw-r--r--src/web/_views/template/post.php5
-rw-r--r--src/web/_views/template/posts.php2
-rw-r--r--src/web/config/aesthetic.php1
-rw-r--r--src/web/helper/image.php33
-rw-r--r--src/web/index.php1
-rw-r--r--src/web/lang/en_US/apps/profile.php23
-rw-r--r--src/web/lang/en_US/common_lang.php1
38 files changed, 691 insertions, 269 deletions
diff --git a/src/db/migrations/0000.sql b/src/db/migrations/0000.sql
index 7e14ac4..aa20d2f 100644
--- a/src/db/migrations/0000.sql
+++ b/src/db/migrations/0000.sql
@@ -198,7 +198,7 @@ CREATE TABLE admin.media (
id INTEGER DEFAULT nextval('sys.media_id_seq'::regclass) NOT NULL,
name TEXT NOT NULL,
content BYTEA NOT NULL,
- type TEXT NOT NULL,
+ mime TEXT NOT NULL,
created TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
modified TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL
);
@@ -224,9 +224,12 @@ CREATE TYPE admin.user_media_type AS ENUM (
CREATE TABLE admin.user_media (
id INTEGER DEFAULT nextval('sys.user_media_id_seq'::regclass) NOT NULL,
- media_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
- type admin.user_media_type NOT NULL
+ content BYTEA NOT NULL,
+ mime TEXT NOT NULL,
+ type admin.user_media_type NOT NULL,
+ created TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
+ modified TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL
);
ALTER TABLE admin.user_media OWNER TO xssbook;
@@ -235,9 +238,6 @@ ALTER TABLE ONLY admin.user_media
ADD CONSTRAINT user_media_pkey PRIMARY KEY (id);
ALTER TABLE ONLY admin.user_media
- ADD CONSTRAINT user_media_media_id_fkey FOREIGN KEY (media_id) REFERENCES admin.media (id) ON DELETE CASCADE;
-
-ALTER TABLE ONLY admin.user_media
ADD CONSTRAINT user_media_user_id_fkey FOREIGN KEY (user_id) REFERENCES admin.user (id) ON DELETE CASCADE;
ALTER TABLE ONLY admin.user_media
diff --git a/src/db/rest/util/_api_serve_media.sql b/src/db/rest/media/_api_serve_system_media.sql
index c2e213a..5cd87c2 100644
--- a/src/db/rest/util/_api_serve_media.sql
+++ b/src/db/rest/media/_api_serve_system_media.sql
@@ -1,4 +1,4 @@
-CREATE FUNCTION _api.serve_media(
+CREATE FUNCTION _api.serve_system_media(
_media_id INTEGER
)
RETURNS sys."*/*"
@@ -8,34 +8,30 @@ DECLARE
_headers TEXT;
_data BYTEA;
BEGIN
-
SELECT FORMAT(
'[{"Content-Type": "%s"},'
'{"Content-Disposition": "inline; filename=\"%s\""},'
'{"Cache-Control": "max-age=259200"}]'
- , m.type, m.name)
+ , m.mime, m.name)
FROM admin.media m
- WHERE m.id = _media_id INTO _headers;
-
- PERFORM SET_CONFIG('response.headers', _headers, true);
+ WHERE m.id = _media_id
+ INTO _headers;
SELECT m.content
FROM admin.media m
WHERE m.id = _media_id
INTO _data;
- IF FOUND THEN
+ IF _data IS NOT NULL THEN
+ PERFORM SET_CONFIG('response.headers', _headers, true);
RETURN(_data);
ELSE
- PERFORM _api.raise(
- _msg => 'api_not_found',
- _err => 404
- );
+ PERFORM _api.raise_not_found();
END IF;
END
$BODY$;
-GRANT EXECUTE ON FUNCTION _api.serve_media(INTEGER)
+GRANT EXECUTE ON FUNCTION _api.serve_system_media(INTEGER)
TO rest_anon, rest_user;
GRANT SELECT ON TABLE admin.media
TO rest_anon, rest_user;
diff --git a/src/db/rest/media/_api_serve_user_media.sql b/src/db/rest/media/_api_serve_user_media.sql
new file mode 100644
index 0000000..3487493
--- /dev/null
+++ b/src/db/rest/media/_api_serve_user_media.sql
@@ -0,0 +1,37 @@
+CREATE FUNCTION _api.serve_user_media(
+ _media_id INTEGER
+)
+RETURNS sys."*/*"
+LANGUAGE plpgsql VOLATILE
+AS $BODY$
+DECLARE
+ _headers TEXT;
+ _data BYTEA;
+BEGIN
+ SELECT FORMAT(
+ '[{"Content-Type": "%s"},'
+ '{"Content-Disposition": "inline"},'
+ '{"Cache-Control": "max-age=259200"}]'
+ , m.mime)
+ FROM admin.user_media m
+ WHERE m.id = _media_id
+ INTO _headers;
+
+ SELECT m.content
+ FROM admin.user_media m
+ WHERE m.id = _media_id
+ INTO _data;
+
+ IF _data IS NOT NULL THEN
+ PERFORM SET_CONFIG('response.headers', _headers, true);
+ RETURN(_data);
+ ELSE
+ PERFORM _api.raise_not_found();
+ END IF;
+END
+$BODY$;
+
+GRANT EXECUTE ON FUNCTION _api.serve_user_media(INTEGER)
+ TO rest_anon, rest_user;
+GRANT SELECT ON TABLE admin.user_media
+ TO rest_anon, rest_user;
diff --git a/src/db/rest/media/_api_serve_user_or_default_media.sql b/src/db/rest/media/_api_serve_user_or_default_media.sql
new file mode 100644
index 0000000..c079ba9
--- /dev/null
+++ b/src/db/rest/media/_api_serve_user_or_default_media.sql
@@ -0,0 +1,41 @@
+CREATE FUNCTION _api.serve_user_or_default_media(
+ _user_id INTEGER,
+ _type admin.user_media_type,
+ _default TEXT
+)
+RETURNS sys."*/*"
+LANGUAGE plpgsql VOLATILE
+AS $BODY$
+DECLARE
+ _media_id INTEGER;
+BEGIN
+
+ SELECT id
+ FROM admin.user_media m
+ WHERE m.type = _type
+ AND m.user_id = _user_id
+ INTO _media_id;
+
+ IF FOUND THEN
+ RETURN _api.serve_user_media(_media_id);
+ END IF;
+
+ SELECT id
+ FROM admin.media m
+ WHERE m.name = _default
+ INTO _media_id;
+
+ IF FOUND THEN
+ RETURN _api.serve_system_media(_media_id);
+ END IF;
+
+ PERFORM _api_raise_not_found();
+END
+$BODY$;
+
+GRANT EXECUTE ON FUNCTION _api.serve_user_or_default_media(INTEGER, admin.user_media_type, TEXT)
+ TO rest_anon, rest_user;
+GRANT SELECT ON TABLE admin.user_media
+ TO rest_anon, rest_user;
+GRANT SELECT ON TABLE admin.media
+ TO rest_anon, rest_user;
diff --git a/src/db/rest/media/api_profile_avatar.sql b/src/db/rest/media/api_profile_avatar.sql
index 8607999..b3e456c 100644
--- a/src/db/rest/media/api_profile_avatar.sql
+++ b/src/db/rest/media/api_profile_avatar.sql
@@ -5,32 +5,16 @@ RETURNS sys."*/*"
LANGUAGE plpgsql VOLATILE
AS $BODY$
DECLARE
- _id INTEGER;
- _mod INTEGER;
- _name TEXT;
+ _default TEXT;
BEGIN
- SELECT media_id INTO _id
- FROM admin.user_media m
- WHERE m.user_id = profile_avatar.user_id
- AND type = 'avatar'::admin.user_media_type;
-
- -- get default if not exists
- IF NOT FOUND THEN
- _mod = MOD(user_id, 24);
- _name = 'default_avatar_' || _mod || '.png';
-
- SELECT id INTO _id
- FROM admin.media
- WHERE name = _name;
- END IF;
-
- RETURN _api.serve_media(_id);
+ _default := 'default_avatar_' || MOD(user_id, 25) || '.png';
+ RETURN _api.serve_user_or_default_media(
+ user_id,
+ 'avatar'::admin.user_media_type,
+ _default
+ );
END
$BODY$;
GRANT EXECUTE ON FUNCTION api.profile_avatar(INTEGER)
TO rest_anon, rest_user;
-GRANT SELECT ON TABLE admin.user_media
- TO rest_anon, rest_user;
-GRANT SELECT ON TABLE admin.media
- TO rest_anon, rest_user;
diff --git a/src/db/rest/media/api_profile_banner.sql b/src/db/rest/media/api_profile_banner.sql
index 272d021..d98f553 100644
--- a/src/db/rest/media/api_profile_banner.sql
+++ b/src/db/rest/media/api_profile_banner.sql
@@ -4,10 +4,21 @@ CREATE FUNCTION api.profile_banner(
RETURNS sys."*/*"
LANGUAGE plpgsql VOLATILE
AS $BODY$
+DECLARE
+ _default TEXT;
BEGIN
- PERFORM _api.raise_deny();
+ _default := 'default_banner_' || MOD(user_id, 25) || '.png';
+ RETURN _api.serve_user_or_default_media(
+ user_id,
+ 'banner'::admin.user_media_type,
+ _default
+ );
END
$BODY$;
GRANT EXECUTE ON FUNCTION api.profile_banner(INTEGER)
TO rest_anon, rest_user;
+GRANT SELECT ON TABLE admin.user_media
+ TO rest_anon, rest_user;
+GRANT SELECT ON TABLE admin.media
+ TO rest_anon, rest_user;
diff --git a/src/db/rest/post/api_post.sql b/src/db/rest/post/api_post.sql
index 0d60473..b5c42a8 100644
--- a/src/db/rest/post/api_post.sql
+++ b/src/db/rest/post/api_post.sql
@@ -6,7 +6,9 @@ CREATE VIEW api.post AS
p.created,
p.modified,
COALESCE(c.cc, 0)
- AS comment_count
+ AS comment_count,
+ COALESCE(l.lc, 0)
+ AS like_count
FROM
admin.post p
LEFT JOIN (
@@ -20,6 +22,17 @@ CREATE VIEW api.post AS
) c
ON
p.id = c.post_id
+ LEFT JOIN (
+ SELECT
+ COUNT(l.id) as lc,
+ l.post_id
+ FROM
+ admin.like l
+ GROUP BY
+ l.post_id
+ ) l
+ ON
+ p.id = l.post_id
LEFT JOIN
admin.user u
ON
diff --git a/src/db/rest/rest.sql b/src/db/rest/rest.sql
index e203f27..783866a 100644
--- a/src/db/rest/rest.sql
+++ b/src/db/rest/rest.sql
@@ -15,7 +15,6 @@ GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
-- util
\i /db/rest/util/_api_trim.sql;
-\i /db/rest/util/_api_serve_media.sql;
\i /db/rest/util/_api_raise.sql;
\i /db/rest/util/_api_raise_null.sql;
\i /db/rest/util/_api_raise_unique.sql;
@@ -47,6 +46,9 @@ GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
\i /db/rest/like/api_like_delete.sql;
-- media
+\i /db/rest/media/_api_serve_user_media.sql;
+\i /db/rest/media/_api_serve_system_media.sql;
+\i /db/rest/media/_api_serve_user_or_default_media.sql;
\i /db/rest/media/api_profile_avatar.sql;
\i /db/rest/media/api_profile_banner.sql;
diff --git a/src/db/rest/user/api_user.sql b/src/db/rest/user/api_user.sql
index 6735775..d71fd1b 100644
--- a/src/db/rest/user/api_user.sql
+++ b/src/db/rest/user/api_user.sql
@@ -13,9 +13,74 @@ CREATE VIEW api.user AS
u.profile_bio,
u.created,
u.modified,
- u.seen
+ u.seen,
+ COALESCE(f.fc, 0)
+ AS follower_count,
+ COALESCE(fl.fc, 0)
+ AS followed_count,
+ COALESCE(c.cc, 0)
+ AS comment_count,
+ COALESCE(p.pc, 0)
+ AS post_count,
+ COALESCE(l.lc, 0)
+ AS like_count
FROM
admin.user u
+ LEFT JOIN (
+ SELECT
+ COUNT(f.id) as fc,
+ f.followee_id
+ FROM
+ admin.follow f
+ GROUP BY
+ f.followee_id
+ ) f
+ ON
+ u.id = f.followee_id
+ LEFT JOIN (
+ SELECT
+ COUNT(fl.id) as fc,
+ fl.follower_id
+ FROM
+ admin.follow fl
+ GROUP BY
+ fl.follower_id
+ ) fl
+ ON
+ u.id = fl.follower_id
+ LEFT JOIN (
+ SELECT
+ COUNT(c.id) as cc,
+ c.user_id
+ FROM
+ admin.comment c
+ GROUP BY
+ c.user_id
+ ) c
+ ON
+ u.id = c.user_id
+ LEFT JOIN (
+ SELECT
+ COUNT(p.id) as pc,
+ p.user_id
+ FROM
+ admin.post p
+ GROUP BY
+ p.user_id
+ ) p
+ ON
+ u.id = p.user_id
+ LEFT JOIN (
+ SELECT
+ COUNT(l.id) as lc,
+ l.user_id
+ FROM
+ admin.like l
+ GROUP BY
+ l.user_id
+ ) l
+ ON
+ u.id = l.user_id
WHERE
u.deleted <> TRUE;
diff --git a/src/db/rest/util/_api_raise_not_found.sql b/src/db/rest/util/_api_raise_not_found.sql
new file mode 100644
index 0000000..f4997a6
--- /dev/null
+++ b/src/db/rest/util/_api_raise_not_found.sql
@@ -0,0 +1,16 @@
+CREATE FUNCTION _api.raise_not_found()
+RETURNS BOOLEAN
+LANGUAGE plpgsql VOLATILE
+AS $BODY$
+BEGIN
+ PERFORM _api.raise(
+ _msg => 'api_not_found',
+ _err => 404
+ );
+
+ RETURN TRUE;
+END
+$BODY$;
+
+GRANT EXECUTE ON FUNCTION _api.raise_not_found()
+ TO rest_anon, rest_user;
diff --git a/src/public/css/common.css b/src/public/css/common.css
index 0750b8a..281df6b 100644
--- a/src/public/css/common.css
+++ b/src/public/css/common.css
@@ -170,6 +170,21 @@ a, button {
color: var(--blue-alt);
}
+.btn {
+ position: relative;
+}
+
+.btn-border::before {
+ position: absolute;
+ content: "";
+ display: block;
+ bottom: -1px;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: var(--blue-alt);
+}
+
input.btn:focus {
border: none;
outline: none;
@@ -205,6 +220,7 @@ input.btn:focus {
display: flex;
flex-direction: row;
align-items: center;
+ z-index: 5;
}
.nav {
@@ -224,7 +240,7 @@ input.btn:focus {
flex: 1;
justify-content: center;
height: 100%;
- z-index: 2;
+ z-index: 6;
}
@media (min-width: 800px) {
@@ -274,6 +290,11 @@ input.btn:focus {
.nav-center .btn.active {
border-bottom: none;
}
+
+ .nav .btn-border::before {
+ background: inherit;
+ }
+
}
.nav-right .image-loading {
@@ -305,6 +326,10 @@ input.btn:focus {
animation: shimmer 1s linear infinite;
}
+.image-loaded {
+ background-color: var(--base);
+}
+
.card {
background-color: var(--surface0);
border-radius: .5rem;
@@ -393,6 +418,14 @@ input.btn:focus {
display: flex;
flex-direction: column;
animation: fadeIn .1s, slideInModal .1s linear;
+ z-index: 10;
+}
+
+@media (max-width: 40rem) {
+ .modal {
+ min-width: 100%;
+ width: 100%;
+ }
}
@keyframes slideInModal {
diff --git a/src/public/css/home.css b/src/public/css/home.css
index 3c2a3a1..3fdc381 100644
--- a/src/public/css/home.css
+++ b/src/public/css/home.css
@@ -2,11 +2,8 @@
display: flex;
flex-direction: column;
align-items: center;
-}
-
-.card {
- width: 40rem;
- margin-bottom: 1rem;
+ padding: 1rem;
+ padding-bottom: 0;
}
.new-post-modal textarea {
diff --git a/src/public/css/people.css b/src/public/css/people.css
index 279c839..6b07eff 100644
--- a/src/public/css/people.css
+++ b/src/public/css/people.css
@@ -11,42 +11,20 @@
}
#people-container {
- margin-left: auto;
- margin-right: auto;
+ display: grid;
+ width: 100%;
padding: 1rem 2rem;
- padding-bottom: 0;
- flex-direction: row;
- align-items: center;
- flex-wrap: wrap;
- max-width: 90rem;
+ margin-bottom: 1rem;
+ grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr) );
+ grid-auto-rows: max-content;
+ grid-gap: 2rem;
}
.profile {
- width: 25rem;
-}
-
-@media(max-width: 1400px) {
- #people-container {
- max-width: 70rem;
- }
- .profile {
- width: 25rem;
- }
-}
-
-@media(max-width: 1000px) {
- #people-container {
- max-width: 50rem;
- }
- .profile {
- width: 20rem;
- }
-}
-
-.profile {
- margin: 1rem;
+ width: 16rem;
text-decoration: none;
- height: fit-content;
+ margin-left: auto;
+ margin-right: auto;
}
.profile:hover {
@@ -54,16 +32,22 @@
}
.profile strong {
- font-size: 2rem;
+ font-size: 1.5rem;
}
.profile .pfp, .profile .pfp img {
padding: none;
margin: none;
- height: 6rem;
+ width: 100%;
+ height: 100%;
+ aspect-ratio: 1;
border-radius: .3rem;
}
+.profile .pfp {
+ margin-bottom: 1rem;
+}
+
td:nth-child(1) {
font-weight: bold;
color: var(--subtext);
diff --git a/src/public/css/post.css b/src/public/css/post.css
index 6fd7ca0..2b6a4b1 100644
--- a/src/public/css/post.css
+++ b/src/public/css/post.css
@@ -8,6 +8,22 @@
justify-content: center;
}
+.post, #new-post {
+ margin-bottom: 1rem;
+ width: 40rem;
+}
+
.post {
padding-bottom: 0;
}
+
+@media(max-width: 40rem) {
+ .post, #new-post {
+ width: 100%;
+ }
+}
+
+.post .likes {
+ display: block;
+ padding-top: .25rem;
+}
diff --git a/src/public/css/profile.css b/src/public/css/profile.css
index a16fdfd..71cbbfa 100644
--- a/src/public/css/profile.css
+++ b/src/public/css/profile.css
@@ -4,17 +4,34 @@
padding: 0;
}
-#profile-header {
+#profile-header-container {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
background-color: var(--surface0);
- margin-bottom: 2rem;
+ margin-bottom: 1rem;
+ border-bottom: 1px solid var(--surface1);
+}
+
+#profile-header {
+ min-width: 0;
+ max-width: 80rem;
+ flex-grow: 1;
}
#profile-header .banner {
width: 100%;
- min-height: 20rem;
+ min-height: 30rem;
aspect-ratio: 5;
}
+#profile-header .banner img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+}
+
#profile-header .info .pfp-wrapper .pfp,
#profile-header .info .pfp-wrapper .pfp img {
height: 12.5rem;
@@ -32,7 +49,7 @@
border-radius: 100%;
position: absolute;
top: -2.5rem;
- left: 2rem;
+ left: 1rem;
}
#profile-header .info .content {
@@ -65,13 +82,27 @@
text-align: center;
}
-#tab-posts,
+.tab {
+ max-width: 80rem;
+ width: 100%;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 0 1rem;
+ margin-bottom: 1rem;
+}
+
#post-container {
- width: 40rem;
+ max-width: 40rem;
+ width: 100%;
margin-left: auto;
margin-right: auto;
+ margin-bottom: -1rem;
}
#post-container .post {
margin-bottom: 1rem;
}
+
+td:nth-child(1) {
+ padding-right: 2rem;
+}
diff --git a/src/public/js/lib.js b/src/public/js/lib.js
index 19019ad..95f83b7 100644
--- a/src/public/js/lib.js
+++ b/src/public/js/lib.js
@@ -13,7 +13,8 @@ var $$ = (selector) => {
'click',
'submit',
'each',
- 'error'
+ 'error',
+ 'one'
];
let vtable = {};
@@ -46,9 +47,9 @@ var $$ = (selector) => {
let config = { childList: true, subtree: true };
let MutationObserver = window.MutationObserver;
let observer = new MutationObserver(onMutate);
+
observer.observe(document.body, config);
});
-
};
}
@@ -121,3 +122,14 @@ $.ajaxSetup({
})(),
error: errorToastAjax
})
+
+var onImgLoad = function(me) {
+ me.parentElement.classList.remove('image-loading');
+ me.parentElement.classList.add('image-loaded');
+}
+
+var onImgError = function(me) {
+ me.parentElement.classList.remove('image-loading');
+ me.parentElement.classList.add('image-loaded');
+ me.remove();
+}
diff --git a/src/public/js/post.js b/src/public/js/post.js
index 38bbb78..3c03bae 100644
--- a/src/public/js/post.js
+++ b/src/public/js/post.js
@@ -45,8 +45,15 @@ $$('#action-load-posts').on('click', function() {
let pageSize = Number(me.attr('pageSize'));
let postCount = Number(me.attr('postCount'));
let postMax = Number(me.attr('postMax'));
+ let filterUid = me.attr('userId');
let url = '/_util/post/posts?page=' + page + '&max=' + postMax;
+
+ if (!isNaN(filterUid)) {
+ console.log(filterUid);
+ url += '&user_id=' + filterUid;
+ }
+
$.get(url, function (data) {
if (data === '') {
me.remove();
@@ -101,13 +108,27 @@ $$('.action-like').on('click', function() {
let like_id = me.attr('likeId');
let post_id = me.attr('postId');
+ const updateLiked = (liked) => {
+ let post = me.closest('.post');
+ let likes = post.find('.likes');
+ let count = likes.find('.count');
+
+ let c = Number(count[0].textContent);
+ c += liked ? 1 : -1;
+ count[0].textContent = c;
+ }
+
const onPatch = () => {
+ let liked = me.hasClass('btn-blue');
me.toggleClass('btn-blue');
+ updateLiked(!liked);
}
const onPost = (data) => {
+ let liked = me.hasClass('btn-blue');
me.attr('likeId', data[0].id + '');
me.toggleClass('btn-blue');
+ updateLiked(!liked);
}
if (like_id) {
diff --git a/src/web/_controller/_util/post.php b/src/web/_controller/_util/post.php
index 4da2671..5346497 100644
--- a/src/web/_controller/_util/post.php
+++ b/src/web/_controller/_util/post.php
@@ -79,9 +79,9 @@ class Post_controller extends Controller {
->where('p.id')->le($max);
}
- if ($uid) {
+ if ($filter_uid) {
$query = $query
- ->where('p.user_id')->eq($uid);
+ ->where('p.user_id')->eq($filter_uid);
}
$posts = $query
@@ -106,9 +106,9 @@ class Post_controller extends Controller {
->select('COUNT(p.id) as pc')
->from('api.post p');
- if ($uid) {
+ if ($filter_uid) {
$query = $query
- ->where('p.user_id')->eq($uid);
+ ->where('p.user_id')->eq($filter_uid);
}
$pc = $query
diff --git a/src/web/_controller/apps/people.php b/src/web/_controller/apps/people.php
index 19910ac..86da3b3 100644
--- a/src/web/_controller/apps/people.php
+++ b/src/web/_controller/apps/people.php
@@ -17,10 +17,17 @@ class People_controller extends Controller {
parent::index();
$data = $this->people_model->get_data();
$this->view('header', $data);
+ $this->view('apps/people/header', $data);
$this->view('apps/people/main', $data);
+ $this->view('apps/people/footer', $data);
$this->view('footer', $data);
}
+ public function content(): void {
+ $data = $this->people_model->get_data();
+ $this->view('apps/people/main', $data);
+ }
+
/**
* @return array<string,mixed>
*/
diff --git a/src/web/_controller/apps/profile.php b/src/web/_controller/apps/profile.php
index aaed348..3bc9a91 100644
--- a/src/web/_controller/apps/profile.php
+++ b/src/web/_controller/apps/profile.php
@@ -7,12 +7,16 @@ class Profile_controller extends Controller {
// the format model
protected $format_model;
- // the post model
+ // the post controller
protected $post_controller;
+ // the people controller
+ protected $people_controller;
+
function __construct($load) {
parent::__construct($load);
$this->profile_model = $this->load->model('apps/profile');
+ $this->people_controller = $this->load->controller('apps/people');
$this->format_model = $this->load->model('format');
$this->post_controller = $this->load->controller('_util/post');
}
diff --git a/src/web/_model/apps/people.php b/src/web/_model/apps/people.php
index 1bb110f..ade59d3 100644
--- a/src/web/_model/apps/people.php
+++ b/src/web/_model/apps/people.php
@@ -7,40 +7,32 @@ class People_model extends Model {
parent::__construct($load);
$this->request_model = $this->load->model('request');
}
-
- private function get_filted_query($select) {
- $filter_username = $this->request_model->get_str('filter_username', FALSE);
- $filter_fisrt_name = $this->request_model->get_str('filter_first_name', FALSE);
- $filter_last_name = $this->request_model->get_str('filter_last_name', FALSE);
- $filter_email = $this->request_model->get_str('filter_email', FALSE);
- $max = $this->request_model->get_int('max', FALSE);
+ /**
+ * @param mixed $select
+ */
+ private function get_filted_query($select): DatabaseQuery {
+ $filter_type = $this->request_model->get_str('filter', FALSE);
+ $filter_uid = $this->request_model->get_int('uid', FALSE);
+ $max = $this->request_model->get_int('max', FALSE);
$query = $this->db
->select($select)
->from('api.user u');
- if ($filter_username) {
- $query = $query
- ->where('u.username')
- ->like('%' . $filter_username . '%');
- }
+ if ($filter_type && $filter_uid) {
+ switch ($filter_type) {
+ case 'follower': {
+ $query = $query
+ ->join('admin.follow f', 'f.follower_id = u.id AND f.followee_id', 'INNER')
+ ->eq($filter_uid);
+ } break;
- if ($filter_fisrt_name) {
- $query = $query
- ->where('u.first_name')
- ->like('%'. $filter_fisrt_name . '%');
- }
-
- if ($filter_last_name) {
- $query = $query
- ->where('u.last_name')
- ->like('%' . $filter_last_name . '%');
- }
-
- if ($filter_email) {
- $query = $query
- ->where('u.email')
- ->like('%' . $filter_email . '%');
+ case 'followee': {
+ $query = $query
+ ->join('admin.follow f', 'f.followee_id = u.id AND f.follower_id', 'INNER')
+ ->eq($filter_uid);
+ } break;
+ }
}
if ($max) {
@@ -52,12 +44,15 @@ class People_model extends Model {
return $query;
}
- public function get_users(): array {
+ /**
+ * @return array<string,mixed>
+ */
+ public function get_users(): array {
$page = $this->request_model->get_int('page', 0);
$page_size = 24;
$offset = $page_size * $page;
- $users = $this->get_filted_query('*')
+ $users = $this->get_filted_query('u.*')
->order_by('u.id', 'DESC')
->offset($offset)
->limit($page_size)
@@ -72,11 +67,16 @@ class People_model extends Model {
$max = max($max, $user['id']);
}
+ $filter_type = $this->request_model->get_str('filter', FALSE);
+ $filter_uid = $this->request_model->get_int('uid', FALSE);
+
return array(
'users' => $users,
'count' => $count,
'page_size' => $page_size,
- 'max_id' => $max
+ 'max_id' => $max,
+ 'filter_type' => $filter_type || '',
+ 'filter_uid' => $filter_uid || ''
);
}
diff --git a/src/web/_views/apps/home/main.php b/src/web/_views/apps/home/main.php
index 29bf7c3..735e3d8 100644
--- a/src/web/_views/apps/home/main.php
+++ b/src/web/_views/apps/home/main.php
@@ -4,7 +4,7 @@
<?php if ($self): ?>
<div id="new-post" class="card">
<div class="row grow">
- <?php $this->view('template/pfp', array('user' => $self))?>
+ <?=pfp($self)?>
<a
id="action-new-post"
class="btn btn-alt btn-wide ml"
diff --git a/src/web/_views/apps/people/card.php b/src/web/_views/apps/people/card.php
index a44b0d4..eda49b5 100644
--- a/src/web/_views/apps/people/card.php
+++ b/src/web/_views/apps/people/card.php
@@ -4,32 +4,12 @@
class="card profile"
href="/profile?id=<?=$user['id']?>"
>
- <div class="row">
- <?php $this->view('template/pfp', array('user' => $user, 'link' => FALSE)); ?>
+ <div class="col">
+ <?=pfp($user, FALSE)?>
<div class="col ml">
<strong class=""><?=$this->format_model->name($user)?></strong>
- <span class="dim"><?=lang('joined') . ' ' . $this->format_model->date($user['created'])?></span>
- <span class="dim"><?=lang('seen') . ' ' . $this->format_model->date($user['seen'])?></span>
+ <span class="dim"><?=$user['username']?></span>
</div>
</div>
- <hr>
- <table>
- <tr>
- <td><?=lang('tbl_username')?></td>
- <td><?=$user['username']?></td>
- <tr>
- <tr>
- <td><?=lang('tbl_email')?></td>
- <td><?=$user['email']?></td>
- <tr>
- <tr>
- <td><?=lang('tbl_gender')?></td>
- <td><?=$user['gender']?></td>
- <tr>
- <tr>
- <td><?=lang('tbl_uid')?></td>
- <td><?=$user['id']?></td>
- <tr>
- </table>
</a>
<?
diff --git a/src/web/_views/apps/people/footer.php b/src/web/_views/apps/people/footer.php
new file mode 100644
index 0000000..ff93026
--- /dev/null
+++ b/src/web/_views/apps/people/footer.php
@@ -0,0 +1,3 @@
+<?php /* Copyright (c) 2024 Freya Murphy */ ?>
+<?php /* vi: syntax=php */ ?>
+</div>
diff --git a/src/web/_views/apps/people/header.php b/src/web/_views/apps/people/header.php
new file mode 100644
index 0000000..7f3d95b
--- /dev/null
+++ b/src/web/_views/apps/people/header.php
@@ -0,0 +1,6 @@
+<?php /* Copyright (c) 2024 Freya Murphy */ ?>
+<?php /* vi: syntax=php */ ?>
+<div id="main-content" class="col">
+ <h1 class="title"><?=lang('title')?></h1>
+ <h3 class="desc"><?=lang('desc')?></h3>
+ <hr>
diff --git a/src/web/_views/apps/people/main.php b/src/web/_views/apps/people/main.php
index 171f25c..deec4c2 100644
--- a/src/web/_views/apps/people/main.php
+++ b/src/web/_views/apps/people/main.php
@@ -1,67 +1,72 @@
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
<?php /* vi: syntax=php */ ?>
-<div id="main-content" class="col">
- <h1 class="title"><?=lang('title')?></h1>
- <h3 class="desc"><?=lang('desc')?></h3>
- <hr>
- <div id="people-container" class="col">
- <?php
- $pdata = $this->people();
- ?>
- </div>
- <?php
- $loaded = count($pdata['users']);
- $page_size = $pdata['page_size'];
- $total = $pdata['count'];
- $max = $pdata['max_id'];
- ?>
- <?php if ($loaded >= $page_size && $page_size < $total): ?>
- <?=ilang('action_load_users',
- id: 'action-load-users',
- class: 'btn btn-line btn-wide mb',
- attrs: array(
- 'loaded' => $loaded,
- 'pageSize' => $page_size,
- 'userCount' => $total,
- 'userMax' => $max
- )
- )?>
- <script>
+<div id="people-container" class="col">
+<?php
+ $pdata = $this->people();
+?>
+</div>
+<?php
+ $loaded = count($pdata['users']);
+ $page_size = $pdata['page_size'];
+ $total = $pdata['count'];
+ $max = $pdata['max_id'];
+ $filter_uid = $pdata['filter_uid'];
+ $filer_type = $pdata['filter_type'];
+?>
+<?php if ($loaded >= $page_size && $page_size < $total): ?>
+ <?=ilang('action_load_users',
+ id: 'action-load-users',
+ class: 'btn btn-line btn-wide mb',
+ attrs: array(
+ 'loaded' => $loaded,
+ 'pageSize' => $page_size,
+ 'userCount' => $total,
+ 'userMax' => $max,
+ 'filterUid' => $filter_uid,
+ 'filterType' => $filer_type
+ )
+ )?>
+ <script>
- var urlParams = new URLSearchParams(window.location.search).toString();
+ $('#action-load-users').on('click', function() {
+ let me = $(this);
+ let page = me.attr('page');
+ if (!page) {
+ page = '1';
+ }
+ let newPage = Number(page) + 1;
+ me.attr('page', newPage + '');
- $('#action-load-users').on('click', function() {
- let me = $(this);
- let page = me.attr('page');
- if (!page) {
- page = '1';
- }
- let newPage = Number(page) + 1;
- me.attr('page', newPage + '');
+ let loaded = Number(me.attr('loaded'));
+ let pageSize = Number(me.attr('pageSize'));
+ let userCount = Number(me.attr('userCount'));
+ let userMax = Number(me.attr('userMax'));
- let loaded = Number(me.attr('loaded'));
- let pageSize = Number(me.attr('pageSize'));
- let userCount = Number(me.attr('userCount'));
- let userMax = Number(me.attr('userMax'));
+ let filterType = me.attr('filterType');
+ let filterUid = me.attr('filterUid');
- let url = '/people/people?page=' + page + '&max=' + userMax + '&' + urlParams;
- $.get(url, function (data) {
- if (data === '') {
- me.remove();
- return;
- }
+ let url = '/people/people?page=' + page + '&max=' + userMax;
- let container = $('#people-container');
- container.append(data);
+ if (filterType && filterUid) {
+ url += '&filter=' + filterType + '&uid=' + filterUid;
+ }
+
+ $.get(url, function (data) {
+ if (data === '') {
+ me.remove();
+ return;
+ }
- loaded += pageSize;
- if (loaded >= userCount) {
- me.remove();
- } else {
- me.attr('loaded', loaded + '');
- }
- });
+ let container = $('#people-container');
+ container.append(data);
+
+ loaded += pageSize;
+ if (loaded >= userCount) {
+ me.remove();
+ } else {
+ me.attr('loaded', loaded + '');
+ }
});
- </script>
- <?php endif ?>
-</div>
+ });
+ </script>
+<?php endif ?>
diff --git a/src/web/_views/apps/profile/main.php b/src/web/_views/apps/profile/main.php
index afa45bc..e3d65b5 100644
--- a/src/web/_views/apps/profile/main.php
+++ b/src/web/_views/apps/profile/main.php
@@ -1,37 +1,158 @@
+<?php /* Copyright (c) 2024 Freya Murphy */ ?>
+<?php /* vi: syntax=php */ ?>
<div id="main-content">
- <div id="profile-header" class="col">
- <div class="banner image-loading">
- <img src="/api/rpc/profile_banner?user_id=<?=$user['id']?>">
- </div>
- <div class="info row">
- <div class="pfp-wrapper">
- <?php $this->view('template/pfp', array('user' => $user)); ?>
- </div>
- <div class="col content">
- <strong class="name"><?=$this->format_model->name($user)?></strong>
- <span class="dim"><?=lang('joined') . $this->format_model->date($user['created'])?></span>
+ <div id="profile-header-container">
+ <div id="profile-header" class="col">
+ <?=image('/api/rpc/profile_banner?user_id=' . $user['id'], 'banner')?>
+ <div class="info row">
+ <div class="pfp-wrapper">
+ <?=pfp($user)?>
+ </div>
+ <div class="col content">
+ <strong class="name"><?=$this->format_model->name($user)?></strong>
+ <span class="dim"><?=$user['follower_count'] . ' ' . lang('followers')?></span>
+ <?php if(strlen($user['profile_bio']) > 0): ?>
+ <br>
+ <strong><?=lang('bio')?></strong>
+ <span class="dim"><?=$user['profile_bio']?></span>
+ <?php endif; ?>
+ </div>
</div>
+ <hr>
+ <div class="row options">
+ <?=ilang('action_posts',
+ sub: [$user['first_name']],
+ class: 'btn btn-blue btn-border',
+ id: 'action-posts'
+ )?>
+ <?=ilang('action_about',
+ sub: [$user['first_name']],
+ class: 'btn',
+ id: 'action-about'
+ )?>
+ <?=ilang('action_followers',
+ sub: [$user['first_name']],
+ class: 'btn',
+ id: 'action-followers'
+ )?>
+ <?=ilang('action_following',
+ sub: [$user['first_name']],
+ class: 'btn',
+ id: 'action-following'
+ )?>
</div>
- <hr>
- <div class="row options">
- <?=ilang('action_posts',
- sub: [$user['first_name']],
- class: 'btn'
- )?>
- <?=ilang('action_about',
- sub: [$user['first_name']],
- class: 'btn'
- )?>
- <?=ilang('action_friends',
- sub: [$user['first_name']],
- class: 'btn'
- )?>
</div>
</div>
- <div id="#tab-posts">
+ <div id="tab-posts" class="tab">
<?php
$_GET['user_id'] = $user['id'];
$this->post_controller->index();
?>
</div>
+ <div id="tab-about" class="tab">
+ <h1><?=lang('about_general')?></h1>
+ <table>
+ <tr>
+ <td><strong><?=lang('about_general_username')?></strong></td>
+ <td><?=$user['username']?></td>
+ </tr>
+ <tr>
+ <td><strong><?=lang('about_general_full_name')?></strong></td>
+ <td><?=$user['first_name'] . ' ' . $user['last_name']?></td>
+ </tr>
+ <tr>
+ <td><strong><?=lang('about_general_email')?></strong></td>
+ <td><?=$user['email']?></td>
+ </tr>
+ <tr>
+ <td><strong><?=lang('about_general_gender')?></strong></td>
+ <td><?=$user['gender']?></td>
+ </tr>
+ <tr>
+ <td><strong><?=lang('about_general_birth_date')?></strong></td>
+ <td><?=$user['birth_date']?></td>
+ </tr>
+ </table>
+ <h1><?=lang('about_stats')?></h1>
+ <table>
+ <tr>
+ <td><strong><?=lang('about_stats_posts')?></strong></td>
+ <td><?=$user['post_count']?></td>
+ </tr>
+ <tr>
+ <td><strong><?=lang('about_stats_like')?></strong></td>
+ <td><?=$user['like_count']?></td>
+ </tr>
+ <tr>
+ <td><strong><?=lang('about_stats_comments')?></strong></td>
+ <td><?=$user['comment_count']?></td>
+ </tr>
+ <tr>
+ <td><strong><?=lang('about_stats_following')?></strong></td>
+ <td><?=$user['followed_count']?></td>
+ </tr>
+ <tr>
+ <td><strong><?=lang('about_stats_joined')?></strong></td>
+ <td><?=$user['created']?></td>
+ </tr>
+ <tr>
+ <td><strong><?=lang('about_stats_seen')?></strong></td>
+ <td><?=$user['seen']?></td>
+ </tr>
+ </table>
+ </div>
+ <div id="tab-followers" class="tab">
+ <?php
+ $_GET['filter'] = 'follower';
+ $_GET['uid'] = $user['id'];
+ $this->people_controller->content();
+ ?>
+ </div>
+ <div id="tab-following" class="tab">
+ <?php
+ $_GET['filter'] = 'followee';
+ $_GET['uid'] = $user['id'];
+ $this->people_controller->content();
+ ?>
+ </div>
+ </div>
+ <script>
+ let tabs = {};
+
+ const disableTab = (tab) => {
+ tab.btn.removeClass('btn-blue');
+ tab.btn.removeClass('btn-border');
+ tab.tab.css('display', 'none');
+ };
+
+ const enableTab = (tab) => {
+ tab.btn.addClass('btn-blue');
+ tab.btn.addClass('btn-border');
+ tab.tab.css('display', '');
+ };
+
+ const loadTab = (name, disable = true) => {
+ let btn = $('#action-' + name);
+ btn.on('click', function() {
+ for (let tab of Object.values(tabs)) {
+ disableTab(tab);
+ }
+ enableTab(tabs[name]);
+ });
+
+ tabs[name] = {
+ 'btn': btn,
+ 'tab': $('#tab-' + name)
+ };
+
+ if (disable) {
+ disableTab(tabs[name]);
+ }
+ };
+
+ loadTab('posts', false);
+ loadTab('about');
+ loadTab('followers');
+ loadTab('following');
+ </script>
</div>
diff --git a/src/web/_views/header.php b/src/web/_views/header.php
index 8a0333e..f1aef01 100644
--- a/src/web/_views/header.php
+++ b/src/web/_views/header.php
@@ -11,7 +11,7 @@
<div class="nav-center" :class="{hidden: !visible}">
<a
id="action-home"
- class="btn"
+ class="btn<?=$this->main->info['app'] == 'home' ? ' btn-blue btn-border' : ''?>"
href="/home"
title="<?=lang('action_home_tip')?>"
>
@@ -20,35 +20,33 @@
</a>
<a
id="action-people"
- class="btn"
+ class="btn<?=$this->main->info['app'] == 'people' ? ' btn-blue btn-border' : ''?>"
href="/people"
title="<?=lang('action_people_tip')?>"
>
<i class="mi mi-lg">people</i>
<span><?=lang('action_people_text')?></span>
</a>
- <a
+ <!--a
id="action-chat"
- class="btn"
+ class="btn<?=$this->main->info['app'] == 'chat' ? ' btn-blue btn-border' : ''?>"
href="/chat"
title="<?=lang('action_chat_tip')?>"
>
<i class="mi mi-lg">chat</i>
<span><?=lang('action_chat_text')?></span>
- </a>
+ </a-->
</div>
<div class="nav-right">
<button
id="action-hamburger"
title="<?=lang('action_hamburger_tip')?>"
+ class="btn mr"
>
<i class="mi mi-lg">menu</i>
</button>
<?php if($self): ?>
- <?php $this->view('template/pfp', array(
- 'user' => $self,
- 'class' => 'pfp-sm ml',
- )); ?>
+ <?=pfp($self)?>
<?php else: ?>
<?=ilang('action_login', class: 'btn', href: '/auth/login')?>
<?php endif; ?>
diff --git a/src/web/_views/modal/new_post.php b/src/web/_views/modal/new_post.php
index 50b9b84..15163c9 100644
--- a/src/web/_views/modal/new_post.php
+++ b/src/web/_views/modal/new_post.php
@@ -6,7 +6,7 @@
<form id="new-post-form">
<div class="modal-content new-post-modal">
<div class="row">
- <?php $this->view('template/pfp', array('user' => $user))?>
+ <?=pfp($user)?>
<div class="col ml">
<strong><?=$user['first_name'] . ' ' . $user['last_name']?></strong>
<span class="dim"><?=lang('now')?></span>
diff --git a/src/web/_views/template/comment.php b/src/web/_views/template/comment.php
index 3ff473b..cf2c0b4 100644
--- a/src/web/_views/template/comment.php
+++ b/src/web/_views/template/comment.php
@@ -4,7 +4,7 @@
$format_model = $this->load->model('format');
?>
<div class="comment row mt">
- <?php $this->view('template/pfp', array('user' => $user))?>
+ <?=pfp($user)?>
<div class="ml col sub-card">
<div class="row">
<strong><?=$format_model->name($user)?></strong>
diff --git a/src/web/_views/template/pfp.php b/src/web/_views/template/pfp.php
deleted file mode 100644
index ebb4b5f..0000000
--- a/src/web/_views/template/pfp.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php /* Copyright (c) 2024 Freya Murphy */ ?>
-<?php /* vi: syntax=php */ ?>
-<?php
- $class = isset($class) ? $class : '';
- $link = isset($link) ? $link : TRUE;
-?>
-<?php if($link): ?>
-<a class="image-loading pfp <?=$class?>" href="/profile?id=<?=$user['id']?>">
-<?php else: ?>
-<div class="image-loading pfp <?=$class?>">
-<?php endif; ?>
- <img src="/api/rpc/profile_avatar?user_id=<?=$user['id']?>"/>
-<?php if ($link): ?>
-</a>
-<?php else: ?>
-</div>
-<?php endif; ?>
diff --git a/src/web/_views/template/post.php b/src/web/_views/template/post.php
index 0633985..fb8cef5 100644
--- a/src/web/_views/template/post.php
+++ b/src/web/_views/template/post.php
@@ -2,7 +2,7 @@
<?php /* vi: syntax=php */ ?>
<div class="post card">
<div class="row">
- <?php $this->view('template/pfp', array('user' => $user))?>
+ <?=pfp($user)?>
<div class="col ml">
<strong><?=$user['first_name'] . ' ' . $user['last_name']?></strong>
<span class="dim"><?=$post['created']?></span>
@@ -21,6 +21,7 @@
$post_attrs['likeId'] = $post['like_id'];
}
?>
+ <span class="likes dim"><span class="count"><?=$post['like_count']?></span><?=' ' . lang('likes')?></span>
<?php if ($self): ?>
<hr>
<div class="row">
@@ -61,7 +62,7 @@
</div>
<?php if ($self): ?>
<div class="row pb">
- <?php $this->view('template/pfp', array('user' => $self))?>
+ <?=pfp($self)?>
<form class="ml action-new-comment-form row">
<input
type="hidden"
diff --git a/src/web/_views/template/posts.php b/src/web/_views/template/posts.php
index 5fec698..137c0dd 100644
--- a/src/web/_views/template/posts.php
+++ b/src/web/_views/template/posts.php
@@ -17,7 +17,7 @@
'pageSize' => $page_size,
'postCount' => $total,
'postMax' => $max,
- 'userId' => $filterUid
+ 'userId' => $filterUid ? json_encode($filterUid) : ''
)
);
}
diff --git a/src/web/config/aesthetic.php b/src/web/config/aesthetic.php
index 99a1959..d37f4a9 100644
--- a/src/web/config/aesthetic.php
+++ b/src/web/config/aesthetic.php
@@ -45,6 +45,7 @@ class Aesthetic {
],
'css' => [
'css/profile.css',
+ 'css/people.css',
'css/post.css'
],
),
diff --git a/src/web/helper/image.php b/src/web/helper/image.php
new file mode 100644
index 0000000..ac2f808
--- /dev/null
+++ b/src/web/helper/image.php
@@ -0,0 +1,33 @@
+<?php /* Copyright (c) 2024 Freya Murphy */
+
+function image($src, $class = NULL, $link = NULL): string {
+ if ($class) {
+ $class = 'image-loading ' . $class;
+ } else {
+ $class = 'image-loading';
+ }
+
+ $content = '';
+
+ if ($link) {
+ $content .= '<a class="' . $class . '" href="' . $link . '">';
+ } else {
+ $content .= '<span class="' . $class . '">';
+ }
+ $content .= '<img src="' . $src . '" onerror="onImgError(this)" onload="onImgLoad(this)"/>';
+ if ($link) {
+ $content .= '</a>';
+ } else {
+ $content .= '</span>';
+ }
+
+ return $content;
+}
+
+function pfp(
+ $user,
+ $embedLink = TRUE,
+): string {
+ $link = $embedLink ? '/profile?id=' . $user['id'] : NULL;
+ return image('/api/rpc/profile_avatar?user_id=' . $user['id'], 'pfp', link: $link);
+}
diff --git a/src/web/index.php b/src/web/index.php
index 688383f..dc54905 100644
--- a/src/web/index.php
+++ b/src/web/index.php
@@ -6,6 +6,7 @@ session_start();
$webroot = dirname(__FILE__);
// load all the helper files
+require($webroot . '/helper/image.php');
require($webroot . '/helper/error.php');
require($webroot . '/helper/lang.php');
diff --git a/src/web/lang/en_US/apps/profile.php b/src/web/lang/en_US/apps/profile.php
index 2cc9b4e..43a3247 100644
--- a/src/web/lang/en_US/apps/profile.php
+++ b/src/web/lang/en_US/apps/profile.php
@@ -4,15 +4,34 @@ $lang['title'] = '%s\'s profile';
$lang['joined'] = 'Joined: ';
$lang['seen'] = 'Seen: ';
+$lang['followers'] = 'Followers';
+$lang['bio'] = 'Bio';
$lang['action_posts_text'] = 'Posts';
$lang['action_posts_tip'] = 'View %s\'s posts';
$lang['action_about_text'] = 'About';
$lang['action_about_tip'] = 'View %s\'s information';
-$lang['action_friends_text'] = 'Friends';
-$lang['action_friends_tip'] = 'View %s\'s friends';
+$lang['action_followers_text'] = 'Followers';
+$lang['action_followers_tip'] = 'View %s\'s followres';
+$lang['action_following_text'] = 'Following';
+$lang['action_following_tip'] = 'View who %s is following';
$lang['action_load_posts_text'] = 'Load more posts';
$lang['action_load_posts_tip'] = 'Load more posts';
+$lang['about_general'] = 'General';
+$lang['about_general_username'] = 'Username';
+$lang['about_general_full_name'] = 'Full Name';
+$lang['about_general_email'] = 'Email';
+$lang['about_general_gender'] = 'Gender';
+$lang['about_general_birth_date'] = 'Birthday';
+
+$lang['about_stats'] = 'Statistics';
+$lang['about_stats_posts'] = 'Posts Created';
+$lang['about_stats_like'] = 'Posts Liked';
+$lang['about_stats_comments'] = 'Comments Created';
+$lang['about_stats_following'] = 'Accounts Followed';
+$lang['about_stats_joined'] = 'Date Joined';
+$lang['about_stats_seen'] = 'Last Seen';
+
?>
diff --git a/src/web/lang/en_US/common_lang.php b/src/web/lang/en_US/common_lang.php
index 7e214b5..eb60888 100644
--- a/src/web/lang/en_US/common_lang.php
+++ b/src/web/lang/en_US/common_lang.php
@@ -46,5 +46,6 @@ $lang['action_new_post_tip'] = 'Author a new post.';
// Words
$lang['now'] = 'Now';
+$lang['likes'] = 'Likes';
?>