kopia lustrzana https://github.com/nextcloud/social
commit
17b1db4eb4
appinfo
lib/Controller
|
@ -0,0 +1,14 @@
|
|||
module.exports = {
|
||||
plugins: ['@babel/plugin-syntax-dynamic-import'],
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
browsers: ['last 2 versions', 'ie >= 11']
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
68
.eslintrc.js
68
.eslintrc.js
|
@ -1,3 +1,67 @@
|
|||
module.exports = {
|
||||
"extends": "standard"
|
||||
};
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
node: true,
|
||||
jest: true
|
||||
},
|
||||
globals: {
|
||||
t: true,
|
||||
n: true,
|
||||
OC: true,
|
||||
OCA: true,
|
||||
Vue: true,
|
||||
VueRouter: true
|
||||
},
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
ecmaVersion: 6
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:node/recommended',
|
||||
'plugin:vue/essential',
|
||||
'plugin:vue/recommended',
|
||||
'standard'
|
||||
],
|
||||
plugins: ['vue', 'node'],
|
||||
rules: {
|
||||
// space before function ()
|
||||
'space-before-function-paren': ['error', 'never'],
|
||||
// curly braces always space
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
// stay consistent with array brackets
|
||||
'array-bracket-newline': ['error', 'consistent'],
|
||||
// 1tbs brace style
|
||||
'brace-style': 'error',
|
||||
// tabs only
|
||||
indent: ['error', 'tab'],
|
||||
'no-tabs': 0,
|
||||
'vue/html-indent': ['error', 'tab'],
|
||||
// only debug console
|
||||
'no-console': ['error', { allow: ['error', 'warn', 'debug'] }],
|
||||
// classes blocks
|
||||
'padded-blocks': ['error', { classes: 'always' }],
|
||||
// always have the operator in front
|
||||
'operator-linebreak': ['error', 'before'],
|
||||
// ternary on multiline
|
||||
'multiline-ternary': ['error', 'always-multiline'],
|
||||
// es6 import/export and require
|
||||
'node/no-unpublished-require': ['off'],
|
||||
'node/no-unsupported-features/es-syntax': ['off'],
|
||||
// space before self-closing elements
|
||||
'vue/html-closing-bracket-spacing': 'error',
|
||||
// code spacing with attributes
|
||||
'vue/max-attributes-per-line': [
|
||||
'error',
|
||||
{
|
||||
singleline: 3,
|
||||
multiline: {
|
||||
max: 3,
|
||||
allowFirstLine: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
js/
|
||||
\.idea/
|
||||
|
||||
node_modules/
|
||||
vendor/
|
||||
|
|
|
@ -10,8 +10,14 @@
|
|||
return [
|
||||
'routes' => [
|
||||
['name' => 'Navigation#navigate', 'url' => '/', 'verb' => 'GET'],
|
||||
['name' => 'Navigation#timeline', 'url' => '/timeline/{path}', 'verb' => 'GET', 'requirements' => ['path' => '.+'], 'defaults' => ['path' => '']],
|
||||
['name' => 'Navigation#account', 'url' => '/account/{path}', 'verb' => 'GET', 'requirements' => ['path' => '.+'], 'defaults' => ['path' => '']],
|
||||
['name' => 'Navigation#public', 'url' => '/{username}', 'verb' => 'GET'],
|
||||
|
||||
|
||||
// ['name' => 'Account#create', 'url' => '/local/account/{username}', 'verb' => 'POST'],
|
||||
['name' => 'Account#info', 'url' => '/local/account/{username}', 'verb' => 'GET'],
|
||||
|
||||
|
||||
['name' => 'ActivityPub#sharedInbox', 'url' => '/inbox', 'verb' => 'POST'],
|
||||
['name' => 'ActivityPub#actor', 'url' => '/users/{username}', 'verb' => 'GET'],
|
||||
|
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 17 KiB |
528
js/social.js
528
js/social.js
File diff suppressed because one or more lines are too long
|
@ -35,9 +35,14 @@ use OCA\Social\AppInfo\Application;
|
|||
use OCA\Social\Service\ActorService;
|
||||
use OCA\Social\Service\ConfigService;
|
||||
use OCA\Social\Service\MiscService;
|
||||
use OCP\Accounts\IAccountManager;
|
||||
use OCP\Accounts\IAccountProperty;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\Http\NotFoundResponse;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserManager;
|
||||
|
||||
|
||||
class AccountController extends Controller {
|
||||
|
@ -56,6 +61,9 @@ class AccountController extends Controller {
|
|||
/** @var MiscService */
|
||||
private $miscService;
|
||||
|
||||
/** @var IAccountManager */
|
||||
private $accountManager;
|
||||
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
|
@ -67,8 +75,9 @@ class AccountController extends Controller {
|
|||
* @param MiscService $miscService
|
||||
*/
|
||||
public function __construct(
|
||||
IRequest $request, string $userId, ConfigService $configService,
|
||||
ActorService $actorService, MiscService $miscService
|
||||
IRequest $request, ConfigService $configService,
|
||||
ActorService $actorService, MiscService $miscService,
|
||||
IAccountManager $accountManager, IUserManager $userManager, string $userId = null
|
||||
) {
|
||||
parent::__construct(Application::APP_NAME, $request);
|
||||
|
||||
|
@ -76,6 +85,8 @@ class AccountController extends Controller {
|
|||
$this->configService = $configService;
|
||||
$this->actorService = $actorService;
|
||||
$this->miscService = $miscService;
|
||||
$this->accountManager = $accountManager;
|
||||
$this->userManager = $userManager;
|
||||
}
|
||||
|
||||
|
||||
|
@ -83,6 +94,9 @@ class AccountController extends Controller {
|
|||
* Called by the frontend to create a new Social account
|
||||
*
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
* @NoAdminRequired
|
||||
* @NoSubAdminRequired
|
||||
*
|
||||
* @param string $username
|
||||
*
|
||||
|
@ -98,6 +112,45 @@ class AccountController extends Controller {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
* @NoAdminRequired
|
||||
* @NoSubAdminRequired
|
||||
* @param string $username
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function info(string $username): Response {
|
||||
$user = $this->userManager->get($username);
|
||||
if ($user === null) {
|
||||
// TODO: Proper handling of external accounts
|
||||
$props = [];
|
||||
$props['cloudId'] = $username;
|
||||
$props['displayname'] = ['value' => 'External account'];
|
||||
$props['posts'] = 1;
|
||||
$props['following'] = 2;
|
||||
$props['followers'] = 3;
|
||||
return new DataResponse($props);
|
||||
}
|
||||
$account = $this->accountManager->getAccount($user);
|
||||
/** @var IAccountProperty[] $props */
|
||||
$props = $account->getFilteredProperties(IAccountManager::VISIBILITY_PUBLIC, null);
|
||||
if ($this->userId !== null) {
|
||||
$props = array_merge($props, $account->getFilteredProperties(IAccountManager::VISIBILITY_CONTACTS_ONLY, null));
|
||||
}
|
||||
if (\array_key_exists('avatar', $props)) {
|
||||
$props['avatar']->setValue(\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $username, 'size' => 128]));
|
||||
}
|
||||
|
||||
// Add counters
|
||||
$props['cloudId'] = $user->getCloudId();
|
||||
$props['posts'] = 1;
|
||||
$props['following'] = 2;
|
||||
$props['followers'] = 3;
|
||||
return new DataResponse($props);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -30,14 +30,18 @@ declare(strict_types=1);
|
|||
namespace OCA\Social\Controller;
|
||||
|
||||
|
||||
use OC\Accounts\AccountManager;
|
||||
use OC\User\NoUserException;
|
||||
use OCA\Social\AppInfo\Application;
|
||||
use OCA\Social\Exceptions\AccountAlreadyExistsException;
|
||||
use OCA\Social\Service\ActorService;
|
||||
use OCA\Social\Service\MiscService;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
|
@ -58,6 +62,8 @@ class NavigationController extends Controller {
|
|||
/** @var MiscService */
|
||||
private $miscService;
|
||||
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
|
||||
/**
|
||||
* NavigationController constructor.
|
||||
|
@ -70,8 +76,8 @@ class NavigationController extends Controller {
|
|||
* @param MiscService $miscService
|
||||
*/
|
||||
public function __construct(
|
||||
IRequest $request, string $userId, IConfig $config, IURLGenerator $urlGenerator,
|
||||
ActorService $actorService, MiscService $miscService
|
||||
IRequest $request, $userId, IConfig $config, IURLGenerator $urlGenerator,
|
||||
ActorService $actorService, MiscService $miscService, IL10N $l10n
|
||||
) {
|
||||
parent::__construct(Application::APP_NAME, $request);
|
||||
|
||||
|
@ -81,6 +87,7 @@ class NavigationController extends Controller {
|
|||
|
||||
$this->actorService = $actorService;
|
||||
$this->miscService = $miscService;
|
||||
$this->l10n = $l10n;
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,8 +101,12 @@ class NavigationController extends Controller {
|
|||
* @return TemplateResponse
|
||||
* @throws NoUserException
|
||||
*/
|
||||
public function navigate(): TemplateResponse {
|
||||
$data = [];
|
||||
public function navigate($path = ''): TemplateResponse {
|
||||
$data = [
|
||||
'serverData' => [
|
||||
'public' => false,
|
||||
]
|
||||
];
|
||||
|
||||
try {
|
||||
$this->actorService->createActor($this->userId, $this->userId);
|
||||
|
@ -103,8 +114,59 @@ class NavigationController extends Controller {
|
|||
// we do nothing
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new TemplateResponse(Application::APP_NAME, 'main', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the navigation page of the Social app.
|
||||
*
|
||||
* @NoCSRFRequired
|
||||
* @NoAdminRequired
|
||||
* @NoSubAdminRequired
|
||||
*
|
||||
* @return TemplateResponse
|
||||
* @throws NoUserException
|
||||
*/
|
||||
public function timeline($path = ''): TemplateResponse {
|
||||
return $this->navigate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the navigation page of the Social app.
|
||||
*
|
||||
* @NoCSRFRequired
|
||||
* @NoAdminRequired
|
||||
* @NoSubAdminRequired
|
||||
*
|
||||
* @return TemplateResponse
|
||||
* @throws NoUserException
|
||||
*/
|
||||
public function account($path = ''): TemplateResponse {
|
||||
return $this->navigate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoCSRFRequired
|
||||
* @PublicPage
|
||||
*
|
||||
* @param $username
|
||||
* @return RedirectResponse|PublicTemplateResponse
|
||||
*/
|
||||
public function public($username) {
|
||||
if (\OC::$server->getUserSession()->isLoggedIn()) {
|
||||
return $this->navigate();
|
||||
}
|
||||
|
||||
$data = [
|
||||
'serverData' => [
|
||||
'public' => true,
|
||||
]
|
||||
];
|
||||
$page = new PublicTemplateResponse(Application::APP_NAME, 'main', $data);
|
||||
$page->setHeaderTitle($this->l10n->t('Social') . ' ' . $username);
|
||||
return $page;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Plik diff jest za duży
Load Diff
33
package.json
33
package.json
|
@ -27,7 +27,9 @@
|
|||
"test:coverage": "jest --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue-components": "github:nextcloud/vue-components#init",
|
||||
"@babel/polyfill": "^7.0.0",
|
||||
"nextcloud-axios": "^0.1.2",
|
||||
"nextcloud-vue": "file:/home/jus/repos/nextcloud/vue-components",
|
||||
"uuid": "^3.3.2",
|
||||
"vue": "^2.5.16",
|
||||
"vue-click-outside": "^1.0.7",
|
||||
|
@ -43,17 +45,18 @@
|
|||
"node": ">=10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/test-utils": "^1.0.0-beta.20",
|
||||
"babel-core": "^6.26.3",
|
||||
"@babel/core": "^7.1.2",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/preset-env": "^7.1.0",
|
||||
"@vue/test-utils": "^1.0.0-beta.25",
|
||||
"babel-eslint": "^8.2.5",
|
||||
"babel-jest": "^23.4.0",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-jest": "^23.6.0",
|
||||
"babel-loader": "^8.0.4",
|
||||
"css-loader": "^0.28.11",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-standard": "^11.0.0",
|
||||
"eslint-friendly-formatter": "^4.0.1",
|
||||
"eslint-loader": "^2.0.0",
|
||||
"eslint-loader": "^2.1.1",
|
||||
"eslint-plugin-import": "^2.13.0",
|
||||
"eslint-plugin-node": "^7.0.1",
|
||||
"eslint-plugin-promise": "^3.8.0",
|
||||
|
@ -61,24 +64,24 @@
|
|||
"eslint-plugin-vue": "^4.5.0",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^1.1.11",
|
||||
"jest": "^23.4.0",
|
||||
"jest": "^23.6.0",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"mini-css-extract-plugin": "^0.4.1",
|
||||
"node-sass": "^4.9.2",
|
||||
"mini-css-extract-plugin": "^0.4.4",
|
||||
"node-sass": "^4.9.4",
|
||||
"prettier-eslint": "^8.8.2",
|
||||
"raw-loader": "^0.5.1",
|
||||
"sass-loader": "^7.0.3",
|
||||
"sass-resources-loader": "^1.3.3",
|
||||
"sass-resources-loader": "^1.3.4",
|
||||
"stylelint": "^8.4.0",
|
||||
"stylelint-config-recommended-scss": "^3.2.0",
|
||||
"stylelint-scss": "^3.1.3",
|
||||
"stylelint-scss": "^3.3.2",
|
||||
"stylelint-webpack-plugin": "^0.10.5",
|
||||
"vue-jest": "^2.6.0",
|
||||
"vue-loader": "^15.2.4",
|
||||
"vue-loader": "^15.4.2",
|
||||
"vue-style-loader": "^4.1.1",
|
||||
"vue-template-compiler": "^2.5.16",
|
||||
"webpack": "^4.16.0",
|
||||
"webpack-cli": "^3.0.4",
|
||||
"webpack": "^4.23.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-merge": "^4.1.2"
|
||||
},
|
||||
"jest": {
|
||||
|
|
281
src/App.vue
281
src/App.vue
|
@ -1,163 +1,150 @@
|
|||
<template>
|
||||
<div id="content" class="app-social">
|
||||
<div id="app-navigation">
|
||||
<app-navigation :menu="menu">
|
||||
<!--<template slot="settings-content">Settings</template>-->
|
||||
</app-navigation>
|
||||
<div class="app-social">
|
||||
<div v-if="!serverData.public" id="app-navigation">
|
||||
<app-navigation :menu="menu" />
|
||||
</div>
|
||||
<div id="app-content">
|
||||
<div class="social__container">
|
||||
<h2>🎉 Nextcloud becomes part of the federated social networks!</h2>
|
||||
</div>
|
||||
<router-view :key="$route.fullPath" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
PopoverMenu,
|
||||
AppNavigation
|
||||
} from 'vue-components';
|
||||
<style scoped>
|
||||
.app-social {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
PopoverMenu, AppNavigation
|
||||
<script>
|
||||
import {
|
||||
PopoverMenu,
|
||||
AppNavigation,
|
||||
Multiselect,
|
||||
Avatar
|
||||
} from 'nextcloud-vue'
|
||||
import TimelineEntry from './components/TimelineEntry'
|
||||
import ProfileInfo from './components/ProfileInfo'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
PopoverMenu,
|
||||
AppNavigation,
|
||||
TimelineEntry,
|
||||
Multiselect,
|
||||
Avatar,
|
||||
ProfileInfo
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
infoHidden: false,
|
||||
state: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
url: function() {
|
||||
return OC.linkTo('social', 'img/nextcloud.png')
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
isOpen: false,
|
||||
// example popover in the content
|
||||
menuPopover: [
|
||||
{
|
||||
icon: 'icon-delete',
|
||||
text: 'Delete item',
|
||||
action: () => {
|
||||
alert('Deleted!');
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: 'icon-user',
|
||||
text: 'Nextcloud website',
|
||||
action: () => {},
|
||||
href: 'https://nextcloud.com'
|
||||
},
|
||||
{
|
||||
icon: 'icon-details',
|
||||
longtext: 'Add item',
|
||||
action: () => {
|
||||
alert('details');
|
||||
}
|
||||
currentUser: function() {
|
||||
return OC.getCurrentUser()
|
||||
},
|
||||
socialId: function() {
|
||||
return '@' + OC.getCurrentUser().uid + '@' + OC.getHost()
|
||||
},
|
||||
timeline: function() {
|
||||
return this.$store.getters.getTimeline
|
||||
},
|
||||
serverData: function() {
|
||||
return this.$store.getters.getServerData
|
||||
},
|
||||
menu: function() {
|
||||
let defaultCategories = [
|
||||
{
|
||||
id: 'social-timeline',
|
||||
classes: [],
|
||||
icon: 'icon-category-monitoring',
|
||||
text: t('social', 'Timeline'),
|
||||
router: {
|
||||
name: 'timeline'
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// App navigation
|
||||
menu: function () {
|
||||
let defaultCategories = [
|
||||
{
|
||||
id: 'social-timeline',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-category-monitoring',
|
||||
text: t('social', 'Timeline'),
|
||||
},
|
||||
{
|
||||
id: 'social-account',
|
||||
classes: [],
|
||||
icon: 'icon-user',
|
||||
text: t('social', 'Your account'),
|
||||
router: {
|
||||
name: 'profile',
|
||||
params: { account: this.currentUser.uid }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'social-friends',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-category-social',
|
||||
text: t('social', 'Friends')
|
||||
},
|
||||
{
|
||||
id: 'social-favorites',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-favorite',
|
||||
text: t('social', 'Favorites')
|
||||
},
|
||||
{
|
||||
id: 'social-direct-messages',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-comment',
|
||||
utils: {
|
||||
counter: 3
|
||||
},
|
||||
{
|
||||
id: 'social-your-posts',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-user',
|
||||
text: t('social', 'Your posts'),
|
||||
},
|
||||
{
|
||||
id: 'social-friends',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-category-social',
|
||||
text: t('social', 'Friends'),
|
||||
},
|
||||
{
|
||||
id: 'social-favorites',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-favorite',
|
||||
text: t('social', 'Favorites'),
|
||||
},
|
||||
{
|
||||
id: 'social-direct-messages',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-comment',
|
||||
utils: {
|
||||
counter: 3,
|
||||
},
|
||||
text: t('social', 'Direct messages'),
|
||||
},
|
||||
/*{
|
||||
caption: true,
|
||||
text: t('social', 'Popular topics'),
|
||||
},
|
||||
{
|
||||
id: 'social-topic-nextcloud',
|
||||
classes: [],
|
||||
icon: 'icon-tag',
|
||||
href: '#',
|
||||
utils: {
|
||||
actions: [
|
||||
{
|
||||
icon: 'icon-delete',
|
||||
text: t('settings', 'Remove topic'),
|
||||
action: function () {
|
||||
console.log('remove')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
text: t('settings', '#nextcloud'),
|
||||
},
|
||||
{
|
||||
id: 'social-topic-mastodon',
|
||||
classes: [],
|
||||
icon: 'icon-tag',
|
||||
href: '#',
|
||||
utils: {
|
||||
actions: [
|
||||
{
|
||||
icon: 'icon-delete',
|
||||
text: t('settings', 'Remove topic'),
|
||||
action: function () {
|
||||
console.log('remove')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
text: t('social', '#mastodon'),
|
||||
},
|
||||
{
|
||||
id: 'social-topic-privacy',
|
||||
classes: [],
|
||||
icon: 'icon-tag',
|
||||
href: '#',
|
||||
utils: {
|
||||
actions: [
|
||||
{
|
||||
icon: 'icon-delete',
|
||||
text: t('settings', 'Remove topic'),
|
||||
action: function () {
|
||||
console.log('remove')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
text: t('social', '#privacy'),
|
||||
},*/
|
||||
];
|
||||
return {
|
||||
items: defaultCategories,
|
||||
loading: false
|
||||
text: t('social', 'Direct messages')
|
||||
}
|
||||
]
|
||||
return {
|
||||
items: defaultCategories,
|
||||
loading: false
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeMount: function() {
|
||||
// importing server data into the store
|
||||
const serverDataElmt = document.getElementById('serverData')
|
||||
if (serverDataElmt !== null) {
|
||||
this.$store.commit('setServerData', JSON.parse(document.getElementById('serverData').dataset.server))
|
||||
}
|
||||
|
||||
let example = {
|
||||
message: 'Want to #DropDropbox? #DeleteGoogle? #decentralize? We got you covered, easy as a piece of 🥞\n'
|
||||
+ '\n'
|
||||
+ 'Get started right now: https://nextcloud.com/signup',
|
||||
author: 'Nextcloud 📱☁️💻',
|
||||
authorId: '@nextcloud@mastodon.xyz',
|
||||
authorAvatar: OC.linkTo('social', 'img/nextcloud.png'),
|
||||
timestamp: '1 day ago'
|
||||
}
|
||||
let data = []
|
||||
for (let i = 0; i < 3; i++) {
|
||||
example.id = Math.floor((Math.random() * 100))
|
||||
data.push(example)
|
||||
}
|
||||
data.push({
|
||||
message: 'Want to #DropDropbox? #DeleteGoogle? #decentralize? We got you covered, easy as a piece of 🥞\n'
|
||||
+ '\n'
|
||||
+ 'Get started right now: https://nextcloud.com/signup',
|
||||
author: 'Admin☁️💻',
|
||||
authorId: 'admin',
|
||||
authorAvatar: OC.linkTo('social', 'img/nextcloud.png'),
|
||||
timestamp: '1 day ago'
|
||||
})
|
||||
this.$store.commit('addToTimeline', data)
|
||||
},
|
||||
methods: {
|
||||
hideInfo() {
|
||||
this.infoHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
- @author Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program 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 Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div v-if="uid && accountInfo" class="user-profile">
|
||||
<div class="user-profile--info">
|
||||
<avatar :user="uid" :display-name="displayName" :size="128" />
|
||||
<h2>{{ displayName }}</h2>
|
||||
<p>{{ accountInfo.cloudId }}</p>
|
||||
<p v-if="accountInfo.website">Website: <a :href="accountInfo.website.value">{{ accountInfo.website.value }}</a></p>
|
||||
|
||||
<button v-if="!serverData.public" class="primary" @click="follow">Follow this user</button>
|
||||
</div>
|
||||
|
||||
<ul class="user-profile--sections">
|
||||
<li>
|
||||
<router-link to="./" class="icon-category-monitoring">{{ accountInfo.posts }} posts</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="./following" class="icon-category-social">{{ accountInfo.following }} following</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="./followers" class="icon-category-social">{{ accountInfo.followers }} followers</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.user-profile {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
h2 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.user-profile--info {
|
||||
width: 40%;
|
||||
}
|
||||
.user-profile--sections {
|
||||
width: 60%;
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.user-profile--sections li {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.user-profile--sections li a {
|
||||
padding-left: 24px;
|
||||
background-position: 0 center;
|
||||
height: 40px;
|
||||
opacity: .6;
|
||||
}
|
||||
.user-profile--sections li a.active {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
||||
import { Avatar } from 'nextcloud-vue'
|
||||
|
||||
export default {
|
||||
name: 'ProfileInfo',
|
||||
components: {
|
||||
Avatar
|
||||
},
|
||||
props: {
|
||||
uid: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
displayName() {
|
||||
if (typeof this.accountInfo.displayname !== 'undefined') { return this.accountInfo.displayname.value || '' }
|
||||
return this.uid
|
||||
},
|
||||
serverData: function() {
|
||||
return this.$store.getters.getServerData
|
||||
},
|
||||
accountInfo: function() {
|
||||
return this.$store.getters.getAccount(this.uid)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
follow() {
|
||||
// TODO: implement following users
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<div class="timeline-entry">
|
||||
<div class="entry-content">
|
||||
<div class="post-avatar">
|
||||
<avatar :size="32" :url="item.authorAvatar" />
|
||||
</div>
|
||||
<div class="post-content">
|
||||
<div class="post-author-wrapper">
|
||||
<router-link :to="{ name: 'profile', params: { account: item.authorId }}">
|
||||
<span class="post-author">{{ item.author }}</span>
|
||||
<span class="post-author-id">{{ item.authorId }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="post-message" v-html="formatedMessage" />
|
||||
</div>
|
||||
<div class="post-timestamp">{{ item.timestamp }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Avatar } from 'nextcloud-vue'
|
||||
|
||||
export default {
|
||||
name: 'TimelineEntry',
|
||||
components: {
|
||||
Avatar
|
||||
},
|
||||
props: {
|
||||
item: { type: Object, default: () => {} }
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formatedMessage: function() {
|
||||
let message = this.item.message
|
||||
message = message.replace(/(?:\r\n|\r|\n)/g, '<br />')
|
||||
return message
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.timeline-entry {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.social__welcome h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.post-author-id {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.post-avatar {
|
||||
margin: 5px;
|
||||
margin-right: 10px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
min-width: 32px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
float: right;
|
||||
}
|
||||
|
||||
span {
|
||||
/* opacity: 0.5; */
|
||||
}
|
||||
.entry-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.post-timestamp {
|
||||
opacity: .7;
|
||||
}
|
||||
</style>
|
22
src/main.js
22
src/main.js
|
@ -19,9 +19,25 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
import '@babel/polyfill'
|
||||
|
||||
import Vue from 'vue'
|
||||
import { sync } from 'vuex-router-sync'
|
||||
|
||||
import App from './App'
|
||||
import store from './store'
|
||||
import router from './router'
|
||||
|
||||
sync(store, router)
|
||||
|
||||
// CSP config for webpack dynamic chunk loading
|
||||
// eslint-disable-next-line
|
||||
__webpack_nonce__ = btoa(OC.requestToken)
|
||||
|
||||
// Correct the root of the app for chunk loading
|
||||
// OC.linkTo matches the apps folders
|
||||
// eslint-disable-next-line
|
||||
__webpack_public_path__ = OC.linkTo('social', 'js/')
|
||||
|
||||
Vue.prototype.t = t
|
||||
Vue.prototype.n = n
|
||||
|
@ -30,5 +46,7 @@ Vue.prototype.OCA = OCA
|
|||
|
||||
/* eslint-disable-next-line no-new */
|
||||
new Vue({
|
||||
render: h => h(App)
|
||||
}).$mount('#content')
|
||||
router: router,
|
||||
render: h => h(App),
|
||||
store: store
|
||||
}).$mount('#vue-content')
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
|
||||
// Dynamic loading
|
||||
const Timeline = () => import('./views/Timeline')
|
||||
const Profile = () => import('./views/Profile')
|
||||
const ProfileTimeline = () => import('./views/ProfileTimeline')
|
||||
const ProfileFollowers = () => import('./views/ProfileFollowers')
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
mode: 'history',
|
||||
// if index.php is in the url AND we got this far, then it's working:
|
||||
// let's keep using index.php in the url
|
||||
base: OC.generateUrl(''),
|
||||
linkActiveClass: 'active',
|
||||
routes: [
|
||||
{
|
||||
path: '/:index(index.php/)?apps/social/',
|
||||
components: {
|
||||
default: Timeline
|
||||
},
|
||||
props: true,
|
||||
name: 'timeline'
|
||||
},
|
||||
{
|
||||
path: '/:index(index.php/)?apps/social/account/:account',
|
||||
components: {
|
||||
default: Profile
|
||||
},
|
||||
props: true,
|
||||
name: 'profile',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
components: {
|
||||
details: ProfileTimeline
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'followers',
|
||||
components: {
|
||||
default: Profile,
|
||||
details: ProfileFollowers
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'following',
|
||||
components: {
|
||||
default: Profile,
|
||||
details: ProfileFollowers
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/:index(index.php/)?apps/social/:account',
|
||||
component: Profile,
|
||||
props: true,
|
||||
name: 'public'
|
||||
}
|
||||
]
|
||||
})
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import axios from 'nextcloud-axios'
|
||||
import Vue from 'vue'
|
||||
|
||||
const state = {
|
||||
accounts: {}
|
||||
}
|
||||
const mutations = {
|
||||
addAccount(state, { uid, data }) {
|
||||
Vue.set(state.accounts, uid, data)
|
||||
}
|
||||
}
|
||||
const getters = {
|
||||
getAccount(state) {
|
||||
return (uid) => state.accounts[uid]
|
||||
}
|
||||
}
|
||||
const actions = {
|
||||
fetchAccountInfo(context, uid) {
|
||||
axios.get(OC.generateUrl('apps/social/local/account/' + uid)).then((response) => {
|
||||
context.commit('addAccount', { uid: uid, data: response.data })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default { state, mutations, getters, actions }
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import timeline from './timeline'
|
||||
import account from './account'
|
||||
import settings from './settings'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production'
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
timeline,
|
||||
account,
|
||||
settings
|
||||
},
|
||||
strict: debug
|
||||
})
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
const state = {
|
||||
serverData: {}
|
||||
}
|
||||
const mutations = {
|
||||
setServerData(state, data) {
|
||||
state.serverData = data
|
||||
}
|
||||
}
|
||||
const getters = {
|
||||
getServerData(state) {
|
||||
return state.serverData
|
||||
}
|
||||
}
|
||||
const actions = {}
|
||||
|
||||
export default { state, mutations, getters, actions }
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
const state = {
|
||||
timeline: []
|
||||
}
|
||||
const mutations = {
|
||||
addToTimeline(state, data) {
|
||||
for (let item in data) {
|
||||
state.timeline.push(data[item])
|
||||
}
|
||||
}
|
||||
}
|
||||
const getters = {
|
||||
getTimeline(state) {
|
||||
return state.timeline
|
||||
}
|
||||
}
|
||||
const actions = {}
|
||||
|
||||
export default { state, mutations, getters, actions }
|
|
@ -0,0 +1,156 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
- @author Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program 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 Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="social__wrapper">
|
||||
<profile-info :uid="uid" />
|
||||
<div class="social__container">
|
||||
<router-view name="details" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.social__wrapper {
|
||||
max-width: 700px;
|
||||
margin: 15px auto;
|
||||
}
|
||||
|
||||
.social__welcome {
|
||||
max-width: 700px;
|
||||
margin: 15px auto;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
background-color: var(--color-background-dark);
|
||||
}
|
||||
|
||||
.social__welcome h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.social__welcome .icon-close {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.social__welcome .social-id {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.social__timeline {
|
||||
max-width: 700px;
|
||||
margin: 15px auto;
|
||||
}
|
||||
|
||||
.new-post {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
background-color: var(--color-main-background);
|
||||
position: sticky;
|
||||
top: 47px;
|
||||
z-index: 100;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.new-post-author {
|
||||
padding: 5px;
|
||||
}
|
||||
.author .social-id {
|
||||
opacity: .5;
|
||||
}
|
||||
.new-post-form {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
}
|
||||
.message {
|
||||
width: 100%;
|
||||
}
|
||||
[contenteditable=true]:empty:before{
|
||||
content: attr(placeholder);
|
||||
display: block; /* For Firefox */
|
||||
opacity: .5;
|
||||
}
|
||||
input[type=submit] {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin: 0;
|
||||
padding: 13px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
opacity: 0.3;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#app-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import {
|
||||
PopoverMenu,
|
||||
AppNavigation,
|
||||
Multiselect,
|
||||
Avatar
|
||||
} from 'nextcloud-vue'
|
||||
import TimelineEntry from './../components/TimelineEntry'
|
||||
import ProfileInfo from './../components/ProfileInfo'
|
||||
|
||||
export default {
|
||||
name: 'Profile',
|
||||
components: {
|
||||
PopoverMenu,
|
||||
AppNavigation,
|
||||
TimelineEntry,
|
||||
Multiselect,
|
||||
Avatar,
|
||||
ProfileInfo
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
state: [],
|
||||
uid: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
serverData: function() {
|
||||
return this.$store.getters.getServerData
|
||||
},
|
||||
currentUser: function() {
|
||||
return OC.getCurrentUser()
|
||||
},
|
||||
socialId: function() {
|
||||
return '@' + OC.getCurrentUser().uid + '@' + OC.getHost()
|
||||
},
|
||||
timeline: function() {
|
||||
return this.$store.getters.getTimeline
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.uid = this.$route.params.account
|
||||
this.$store.dispatch('fetchAccountInfo', this.uid)
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,52 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
- @author Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program 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 Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="social__timeline">
|
||||
<ul>
|
||||
<li>User List</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.social__timeline {
|
||||
max-width: 700px;
|
||||
margin: 15px auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import TimelineEntry from './../components/TimelineEntry'
|
||||
|
||||
export default {
|
||||
name: 'ProfileFollowers',
|
||||
components: {
|
||||
TimelineEntry
|
||||
},
|
||||
computed: {
|
||||
timeline: function() {
|
||||
return this.$store.getters.getTimeline
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,50 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
- @author Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program 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 Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="social__timeline">
|
||||
<timeline-entry v-for="entry in timeline" :item="entry" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.social__timeline {
|
||||
max-width: 700px;
|
||||
margin: 15px auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import TimelineEntry from './../components/TimelineEntry'
|
||||
|
||||
export default {
|
||||
name: 'ProfileTimeline',
|
||||
components: {
|
||||
TimelineEntry
|
||||
},
|
||||
computed: {
|
||||
timeline: function() {
|
||||
return this.$store.getters.getTimeline
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,224 @@
|
|||
<template>
|
||||
<div class="social__wrapper">
|
||||
<div class="social__container">
|
||||
<div v-if="!infoHidden" class="social__welcome">
|
||||
<a class="close icon-close" href="#" @click="hideInfo()"><span class="hidden-visually">Close</span></a>
|
||||
<h3>🎉{{ t('social', 'Nextcloud becomes part of the federated social networks!') }}</h3>
|
||||
<p>
|
||||
{{ t('social', 'We have automatically created a social account for you. Your social id is the same as the federated cloud id:') }}
|
||||
<span class="social-id">{{ socialId }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="social__timeline">
|
||||
<div class="new-post" data-id="">
|
||||
<div class="new-post-author">
|
||||
<avatar :user="currentUser.uid" :display-name="currentUser.displayName" :size="32" />
|
||||
</div>
|
||||
<form class="new-post-form">
|
||||
<div class="author currentUser">
|
||||
{{ currentUser.displayName }}
|
||||
<span class="social-id">{{ socialId }}</span>
|
||||
</div>
|
||||
<div contenteditable="true" class="message" placeholder="Share a thought…" />
|
||||
<input class="submit icon-confirm has-tooltip" type="submit" value=""
|
||||
title="" data-original-title="Post">
|
||||
<div class="submitLoading icon-loading-small hidden" />
|
||||
</form>
|
||||
</div>
|
||||
<timeline-entry v-for="entry in timeline" :item="entry" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.social__wrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.social__container {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.social__profile {
|
||||
max-width: 500px;
|
||||
flex-grow: 1;
|
||||
border-right: 1px solid var(--color-background-dark);
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
.social__welcome {
|
||||
max-width: 700px;
|
||||
margin: 15px auto;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
background-color: var(--color-background-dark);
|
||||
}
|
||||
|
||||
.social__welcome h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.social__welcome .icon-close {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.social__welcome .social-id {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.social__timeline {
|
||||
max-width: 700px;
|
||||
margin: 15px auto;
|
||||
}
|
||||
|
||||
.new-post {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
background-color: var(--color-main-background);
|
||||
position: sticky;
|
||||
top: 47px;
|
||||
z-index: 100;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.new-post-author {
|
||||
padding: 5px;
|
||||
}
|
||||
.author .social-id {
|
||||
opacity: .5;
|
||||
}
|
||||
.new-post-form {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
}
|
||||
.message {
|
||||
width: 100%;
|
||||
}
|
||||
[contenteditable=true]:empty:before{
|
||||
content: attr(placeholder);
|
||||
display: block; /* For Firefox */
|
||||
opacity: .5;
|
||||
}
|
||||
input[type=submit] {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin: 0;
|
||||
padding: 13px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
opacity: 0.3;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#app-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import {
|
||||
PopoverMenu,
|
||||
AppNavigation,
|
||||
Multiselect,
|
||||
Avatar
|
||||
} from 'nextcloud-vue'
|
||||
import TimelineEntry from './../components/TimelineEntry'
|
||||
|
||||
export default {
|
||||
name: 'Timeline',
|
||||
components: {
|
||||
PopoverMenu, AppNavigation, TimelineEntry, Multiselect, Avatar
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
infoHidden: false,
|
||||
state: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
url: function() {
|
||||
return OC.linkTo('social', 'img/nextcloud.png')
|
||||
},
|
||||
currentUser: function() {
|
||||
return OC.getCurrentUser()
|
||||
},
|
||||
socialId: function() {
|
||||
return '@' + OC.getCurrentUser().uid + '@' + OC.getHost()
|
||||
},
|
||||
timeline: function() {
|
||||
return this.$store.getters.getTimeline
|
||||
},
|
||||
menu: function() {
|
||||
let defaultCategories = [
|
||||
{
|
||||
id: 'social-timeline',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-category-monitoring',
|
||||
text: t('social', 'Timeline')
|
||||
},
|
||||
{
|
||||
id: 'social-account',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-category-user',
|
||||
text: t('social', 'Your account')
|
||||
},
|
||||
{
|
||||
id: 'social-friends',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-category-social',
|
||||
text: t('social', 'Friends')
|
||||
},
|
||||
{
|
||||
id: 'social-favorites',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-favorite',
|
||||
text: t('social', 'Favorites')
|
||||
},
|
||||
{
|
||||
id: 'social-direct-messages',
|
||||
classes: [],
|
||||
href: '#',
|
||||
icon: 'icon-comment',
|
||||
utils: {
|
||||
counter: 3
|
||||
},
|
||||
text: t('social', 'Direct messages')
|
||||
}
|
||||
]
|
||||
return {
|
||||
items: defaultCategories,
|
||||
loading: false
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeMount: function() {
|
||||
let example = {
|
||||
message: 'Want to #DropDropbox? #DeleteGoogle? #decentralize? We got you covered, easy as a piece of 🥞\n'
|
||||
+ '\n'
|
||||
+ 'Get started right now: https://nextcloud.com/signup',
|
||||
author: 'Nextcloud 📱☁️💻',
|
||||
authorId: '@nextcloud@mastodon.xyz',
|
||||
authorAvatar: OC.linkTo('social', 'img/nextcloud.png'),
|
||||
timestamp: '1 day ago'
|
||||
}
|
||||
let data = []
|
||||
for (let i = 0; i < 20; i++) {
|
||||
let item = Object.assign({}, example)
|
||||
item.id = i
|
||||
data.push(item)
|
||||
}
|
||||
this.$store.commit('addToTimeline', data)
|
||||
},
|
||||
methods: {
|
||||
hideInfo() {
|
||||
this.infoHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1 +0,0 @@
|
|||
ACTOR !
|
|
@ -1 +0,0 @@
|
|||
FOLLOWERS !!!1
|
|
@ -1 +0,0 @@
|
|||
FOLLOWING
|
|
@ -2,4 +2,5 @@
|
|||
script('social', 'social');
|
||||
style('social', 'style');
|
||||
?>
|
||||
<span id="serverData" data-server="<?php p(json_encode($_['serverData']));?>"></span>
|
||||
<div id="vue-content"></div>
|
||||
|
|
|
@ -8,5 +8,5 @@ module.exports = merge(common, {
|
|||
noInfo: true,
|
||||
overlay: true
|
||||
},
|
||||
devtool: '#eval-source-map',
|
||||
devtool: 'source-map',
|
||||
})
|
||||
|
|
Ładowanie…
Reference in New Issue