Initial commit.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @fileoverview Collabs Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description Collaboration projects page
|
||||
* @keywords collabs, page, projects, collaboration, feed
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class CollabsPage extends BaseComponent {
|
||||
init() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.addClass('page', 'collabs-page');
|
||||
|
||||
this.setHtml(`
|
||||
<header class="page-header">
|
||||
<h1>Collabs</h1>
|
||||
</header>
|
||||
<rant-feed sort="recent" feed-type="collabs"></rant-feed>
|
||||
`);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
const feed = this.$('rant-feed');
|
||||
if (feed) {
|
||||
feed.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('collabs-page', CollabsPage);
|
||||
|
||||
export { CollabsPage };
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @fileoverview Home Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description Main feed page displaying latest rants
|
||||
* @keywords home, page, feed, main, landing
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class HomePage extends BaseComponent {
|
||||
static get observedAttributes() {
|
||||
return ['sort'];
|
||||
}
|
||||
|
||||
init() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
const sort = this.getAttr('sort') || 'recent';
|
||||
|
||||
this.addClass('page', 'home-page');
|
||||
|
||||
this.setHtml(`
|
||||
<rant-feed sort="${sort}" feed-type="rants"></rant-feed>
|
||||
`);
|
||||
}
|
||||
|
||||
onAttributeChanged(name, oldValue, newValue) {
|
||||
if (name === 'sort') {
|
||||
const feed = this.$('rant-feed');
|
||||
if (feed) {
|
||||
feed.setSort(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresh() {
|
||||
const feed = this.$('rant-feed');
|
||||
if (feed) {
|
||||
feed.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('home-page', HomePage);
|
||||
|
||||
export { HomePage };
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @fileoverview Login Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description User authentication page
|
||||
* @keywords login, page, auth, signin, authentication
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class LoginPage extends BaseComponent {
|
||||
init() {
|
||||
this.render();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
render() {
|
||||
const isLoggedIn = this.isLoggedIn();
|
||||
const user = this.getCurrentUser();
|
||||
|
||||
this.addClass('page', 'login-page');
|
||||
|
||||
if (isLoggedIn) {
|
||||
this.setHtml(`
|
||||
<div class="login-container">
|
||||
<header class="login-header">
|
||||
<button class="back-btn" aria-label="Go back">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<h1 class="login-logo">Rantii</h1>
|
||||
</header>
|
||||
<div class="logged-in-info">
|
||||
<user-avatar size="large" username="${user?.username || ''}"></user-avatar>
|
||||
<p class="logged-in-text">Logged in as <strong>${user?.username || ''}</strong></p>
|
||||
<div class="logged-in-actions">
|
||||
<button class="btn btn-primary home-btn">Go Home</button>
|
||||
<button class="btn btn-secondary logout-btn">Logout</button>
|
||||
</div>
|
||||
<p class="switch-text">Want to use a different account?</p>
|
||||
<button class="btn btn-link switch-btn">Switch Account</button>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
} else {
|
||||
this.setHtml(`
|
||||
<div class="login-container">
|
||||
<header class="login-header">
|
||||
<button class="back-btn" aria-label="Go back">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<h1 class="login-logo">Rantii</h1>
|
||||
</header>
|
||||
<login-form></login-form>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.on(this, 'click', this.handleClick);
|
||||
this.on(this, 'login-success', this.handleLoginSuccess);
|
||||
}
|
||||
|
||||
handleClick(e) {
|
||||
const backBtn = e.target.closest('.back-btn');
|
||||
const logoutBtn = e.target.closest('.logout-btn');
|
||||
const switchBtn = e.target.closest('.switch-btn');
|
||||
const homeBtn = e.target.closest('.home-btn');
|
||||
|
||||
if (backBtn) {
|
||||
window.history.back();
|
||||
} else if (logoutBtn) {
|
||||
this.getAuth()?.logout();
|
||||
this.render();
|
||||
} else if (switchBtn) {
|
||||
this.getAuth()?.logout();
|
||||
this.render();
|
||||
} else if (homeBtn) {
|
||||
this.getRouter()?.goHome();
|
||||
}
|
||||
}
|
||||
|
||||
handleLoginSuccess() {
|
||||
this.getRouter()?.goHome();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('login-page', LoginPage);
|
||||
|
||||
export { LoginPage };
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @fileoverview Notifications Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description User notifications view page
|
||||
* @keywords notifications, page, alerts, mentions, updates
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class NotificationsPage extends BaseComponent {
|
||||
init() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.addClass('page', 'notifications-page');
|
||||
|
||||
this.setHtml(`
|
||||
<notification-list></notification-list>
|
||||
`);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
const list = this.$('notification-list');
|
||||
if (list) {
|
||||
list.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('notifications-page', NotificationsPage);
|
||||
|
||||
export { NotificationsPage };
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @fileoverview Profile Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description User profile view page
|
||||
* @keywords profile, page, user, view, account
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class ProfilePage extends BaseComponent {
|
||||
static get observedAttributes() {
|
||||
return ['username'];
|
||||
}
|
||||
|
||||
init() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
const username = this.getAttr('username');
|
||||
|
||||
this.addClass('page', 'profile-page');
|
||||
|
||||
this.setHtml(`
|
||||
<user-profile username="${username || ''}"></user-profile>
|
||||
`);
|
||||
|
||||
if (username) {
|
||||
this.load(username);
|
||||
}
|
||||
}
|
||||
|
||||
async load(username) {
|
||||
const profile = this.$('user-profile');
|
||||
if (profile) {
|
||||
await profile.load(username);
|
||||
}
|
||||
}
|
||||
|
||||
onAttributeChanged(name, oldValue, newValue) {
|
||||
if (name === 'username' && newValue) {
|
||||
this.load(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('profile-page', ProfilePage);
|
||||
|
||||
export { ProfilePage };
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @fileoverview Rant Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description Single rant view page with comments
|
||||
* @keywords rant, page, detail, view, single
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class RantPage extends BaseComponent {
|
||||
static get observedAttributes() {
|
||||
return ['rant-id', 'comment-id'];
|
||||
}
|
||||
|
||||
init() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
const rantId = this.getAttr('rant-id');
|
||||
|
||||
this.addClass('page', 'rant-page');
|
||||
|
||||
this.setHtml(`
|
||||
<rant-detail rant-id="${rantId || ''}"></rant-detail>
|
||||
`);
|
||||
|
||||
if (rantId) {
|
||||
this.load(rantId);
|
||||
}
|
||||
}
|
||||
|
||||
async load(rantId) {
|
||||
const detail = this.$('rant-detail');
|
||||
if (detail) {
|
||||
await detail.load(rantId);
|
||||
|
||||
const commentId = this.getAttr('comment-id');
|
||||
if (commentId) {
|
||||
requestAnimationFrame(() => {
|
||||
detail.scrollToComment(commentId);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAttributeChanged(name, oldValue, newValue) {
|
||||
if (name === 'rant-id' && newValue) {
|
||||
this.load(newValue);
|
||||
}
|
||||
if (name === 'comment-id' && newValue) {
|
||||
const detail = this.$('rant-detail');
|
||||
if (detail) {
|
||||
detail.scrollToComment(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('rant-page', RantPage);
|
||||
|
||||
export { RantPage };
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @fileoverview Search Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description Search results and search interface page
|
||||
* @keywords search, page, results, find, query
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class SearchPage extends BaseComponent {
|
||||
static get observedAttributes() {
|
||||
return ['query'];
|
||||
}
|
||||
|
||||
init() {
|
||||
this.render();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
render() {
|
||||
const query = this.getAttr('query') || '';
|
||||
|
||||
this.addClass('page', 'search-page');
|
||||
|
||||
this.setHtml(`
|
||||
<header class="search-header">
|
||||
<form class="search-form">
|
||||
<input type="search"
|
||||
class="search-input"
|
||||
placeholder="Search rants..."
|
||||
value="${query}"
|
||||
autofocus>
|
||||
<button type="submit" class="search-btn">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 0 0 1.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 0 0-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 0 0 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
</header>
|
||||
<section class="search-results">
|
||||
${query ? `<rant-feed feed-type="search"></rant-feed>` : `
|
||||
<div class="search-empty">
|
||||
<svg viewBox="0 0 24 24" width="64" height="64">
|
||||
<path fill="currentColor" d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 0 0 1.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 0 0-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 0 0 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
|
||||
</svg>
|
||||
<p>Enter a search term</p>
|
||||
</div>
|
||||
`}
|
||||
</section>
|
||||
`);
|
||||
|
||||
if (query) {
|
||||
this.search(query);
|
||||
}
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.on(this, 'submit', this.handleSubmit);
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
const input = this.$('.search-input');
|
||||
if (input && input.value.trim()) {
|
||||
this.getRouter()?.goToSearch(input.value.trim());
|
||||
}
|
||||
}
|
||||
|
||||
async search(query) {
|
||||
const feed = this.$('rant-feed');
|
||||
if (feed) {
|
||||
await feed.search(query);
|
||||
}
|
||||
}
|
||||
|
||||
onAttributeChanged(name, oldValue, newValue) {
|
||||
if (name === 'query') {
|
||||
this.render();
|
||||
if (newValue) {
|
||||
this.search(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('search-page', SearchPage);
|
||||
|
||||
export { SearchPage };
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @fileoverview Settings Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description Application settings and preferences page
|
||||
* @keywords settings, page, preferences, options, config
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class SettingsPage extends BaseComponent {
|
||||
init() {
|
||||
this.render();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
render() {
|
||||
const isLoggedIn = this.isLoggedIn();
|
||||
const user = this.getCurrentUser();
|
||||
|
||||
this.addClass('page', 'settings-page');
|
||||
|
||||
this.setHtml(`
|
||||
<header class="settings-header">
|
||||
<button class="back-btn" aria-label="Go back">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<h1>Settings</h1>
|
||||
</header>
|
||||
<div class="settings-content">
|
||||
${isLoggedIn ? `
|
||||
<section class="settings-section">
|
||||
<h2>Account</h2>
|
||||
<div class="setting-item">
|
||||
<span class="setting-label">Logged in as</span>
|
||||
<span class="setting-value">${user?.username || ''}</span>
|
||||
</div>
|
||||
<button class="btn btn-danger logout-btn">Sign Out</button>
|
||||
</section>
|
||||
` : `
|
||||
<section class="settings-section">
|
||||
<h2>Account</h2>
|
||||
<p>Sign in to access all features</p>
|
||||
<button class="btn btn-primary login-btn">Sign In</button>
|
||||
</section>
|
||||
`}
|
||||
<section class="settings-section">
|
||||
<h2>Appearance</h2>
|
||||
<theme-selector></theme-selector>
|
||||
</section>
|
||||
<section class="settings-section">
|
||||
<h2>About</h2>
|
||||
<div class="about-info">
|
||||
<p><strong>Rantii</strong> - A DevRant Client</p>
|
||||
<p>Version 1.0.0</p>
|
||||
<p>Made with care by retoor</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="settings-section">
|
||||
<h2>Data</h2>
|
||||
<button class="btn btn-secondary clear-cache-btn">Clear Cache</button>
|
||||
</section>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.on(this, 'click', this.handleClick);
|
||||
}
|
||||
|
||||
handleClick(e) {
|
||||
const backBtn = e.target.closest('.back-btn');
|
||||
const loginBtn = e.target.closest('.login-btn');
|
||||
const logoutBtn = e.target.closest('.logout-btn');
|
||||
const clearCacheBtn = e.target.closest('.clear-cache-btn');
|
||||
|
||||
if (backBtn) {
|
||||
window.history.back();
|
||||
return;
|
||||
}
|
||||
|
||||
if (loginBtn) {
|
||||
this.getRouter()?.goToLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
if (logoutBtn) {
|
||||
this.logout();
|
||||
return;
|
||||
}
|
||||
|
||||
if (clearCacheBtn) {
|
||||
this.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.getAuth()?.logout();
|
||||
this.render();
|
||||
this.getApp()?.toast?.success('Signed out successfully');
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
const storage = this.getStorage();
|
||||
if (storage) {
|
||||
storage.clear();
|
||||
storage.setTheme(this.getTheme()?.getTheme() || 'dark');
|
||||
}
|
||||
this.getApp()?.toast?.success('Cache cleared');
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('settings-page', SettingsPage);
|
||||
|
||||
export { SettingsPage };
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @fileoverview Stories Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description Developer stories page
|
||||
* @keywords stories, page, devstories, feed, articles
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class StoriesPage extends BaseComponent {
|
||||
init() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.addClass('page', 'stories-page');
|
||||
|
||||
this.setHtml(`
|
||||
<header class="page-header">
|
||||
<h1>Stories</h1>
|
||||
</header>
|
||||
<rant-feed sort="recent" feed-type="stories"></rant-feed>
|
||||
`);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
const feed = this.$('rant-feed');
|
||||
if (feed) {
|
||||
feed.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('stories-page', StoriesPage);
|
||||
|
||||
export { StoriesPage };
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @fileoverview Weekly Page Component for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description Weekly rant challenge page
|
||||
* @keywords weekly, page, challenge, rants, feed
|
||||
*/
|
||||
|
||||
import { BaseComponent } from '../components/base-component.js';
|
||||
|
||||
class WeeklyPage extends BaseComponent {
|
||||
init() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.addClass('page', 'weekly-page');
|
||||
|
||||
this.setHtml(`
|
||||
<header class="page-header">
|
||||
<h1>Weekly Rant</h1>
|
||||
</header>
|
||||
<rant-feed sort="recent" feed-type="weekly"></rant-feed>
|
||||
`);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
const feed = this.$('rant-feed');
|
||||
if (feed) {
|
||||
feed.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('weekly-page', WeeklyPage);
|
||||
|
||||
export { WeeklyPage };
|
||||
Reference in New Issue
Block a user