Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use vue instead of plain template to render suscribe div of issue #28914

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions routers/web/repo/issue_watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func IssueWatch(ctx *context.Context) {
return
}

watch, err := strconv.ParseBool(ctx.Req.PostForm.Get("watch"))
watch, err := strconv.ParseBool(ctx.FormString("watch"))
if err != nil {
ctx.ServerError("watch is not bool", err)
return
Expand All @@ -52,5 +52,5 @@ func IssueWatch(ctx *context.Context) {
return
}

ctx.Redirect(issue.Link())
ctx.JSONOK()
}
24 changes: 7 additions & 17 deletions templates/repo/issue/view_content/sidebar.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -267,23 +267,13 @@
{{if and $.IssueWatch (not .Repository.IsArchived)}}
<div class="divider"></div>

<div class="ui watching">
<span class="text"><strong>{{ctx.Locale.Tr "notification.notifications"}}</strong></span>
<div class="gt-mt-3">
<form method="post" action="{{.Issue.Link}}/watch">
<input type="hidden" name="watch" value="{{if $.IssueWatch.IsWatching}}0{{else}}1{{end}}">
{{$.CsrfTokenHtml}}
<button class="fluid ui button">
{{if $.IssueWatch.IsWatching}}
{{svg "octicon-mute" 16 "gt-mr-3"}}
{{ctx.Locale.Tr "repo.issues.unsubscribe"}}
{{else}}
{{svg "octicon-unmute" 16 "gt-mr-3"}}
{{ctx.Locale.Tr "repo.issues.subscribe"}}
{{end}}
</button>
</form>
</div>
<div id="issue-subscribe"
data-locale-notifications="{{ctx.Locale.Tr "notification.notifications"}}"
data-locale-unsubscribe="{{ctx.Locale.Tr "repo.issues.unsubscribe"}}"
data-locale-subscribe="{{ctx.Locale.Tr "repo.issues.subscribe"}}"
data-is-watching="{{$.IssueWatch.IsWatching}}"
data-watch-link="{{.Issue.Link}}/watch"
>
</div>
{{end}}
{{if .Repository.IsTimetrackerEnabled $.Context}}
Expand Down
70 changes: 70 additions & 0 deletions web_src/js/components/issue/Subscribe.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<script>
import {initComponent} from '../../init.js';
import {SvgIcon} from '../../svg.js';
import {POST} from '../../modules/fetch.js';
import {showErrorToast} from '../../modules/toast.js';
import {ref} from 'vue';

const sfc = {
name: 'IssueSubscribe',
components: {SvgIcon},
props: {
isWatchingReadOnly: {
type: Boolean,
default: false,
},
watchLink: {
type: String,
required: true,
}
},
data() {
return {
isLoading: false,
};
},
setup(props) {
const isWatching = ref(props.isWatchingReadOnly);
return {
isWatching,
};
},
methods: {
async toggleSubscribe() {
if (this.isLoading) return;

this.isLoading = true;
try {
const resp = await POST(`${this.watchLink}?watch=${!this.isWatching}`);
if (resp.status !== 200) {
showErrorToast(`Update watching status return: ${resp.status}`);
return;
}

this.isWatching = !this.isWatching;
} catch (e) {
showErrorToast(`Network error when fetching ${this.mode}, error: ${e}`);
} finally {
this.isLoading = false;
}
},
}
};

export default sfc;

export function initIssueSubsribe() {
initComponent('issue-subscribe', sfc);
}
</script>
<template>
<div class="ui watching">
<span class="text"><strong>{{ locale.notifications }}</strong></span>
<div class="gt-mt-3" :class="isLoading?'is-loading':''">
<button class="fluid ui button" @click="toggleSubscribe">
<SvgIcon :name="isWatching?'octicon-mute':'octicon-unmute'" class="text white" :size="16" class-name="gt-mr-3"/>
{{ isWatching?locale.unsubscribe:locale.subscribe }}
</button>
</div>
</div>
</template>
3 changes: 3 additions & 0 deletions web_src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import {initRepoIssueList} from './features/repo-issue-list.js';
import {initCommonIssueListQuickGoto} from './features/common-issue-list.js';
import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js';
import {initDirAuto} from './modules/dirauto.js';
import {initIssueSubsribe} from './components/issue/Subscribe.vue';

// Init Gitea's Fomantic settings
initGiteaFomantic();
Expand Down Expand Up @@ -184,4 +185,6 @@ onDomReady(() => {
initRepoDiffView();
initPdfViewer();
initScopedAccessTokenCategories();

initIssueSubsribe();
});
37 changes: 37 additions & 0 deletions web_src/js/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {createApp} from 'vue';

// convertName convert the html tag a-b to aB
export function convertName(o) {
return o.replace(/-(\w)/g, (_, c) => {
return c ? c.toUpperCase() : '';
});
}

// initComponent will mount the component with tag id named id and vue sfc
// it will also assign all attributes of the tag with the prefix data-locale- and data-
// to the component as props
export function initComponent(id, sfc) {
const el = document.getElementById(id);
if (!el) return;

const data = {};

for (const attr of el.getAttributeNames()) {
if (attr.startsWith('data-locale-')) {
data.locale = data.locale || {};
data.locale[convertName(attr.slice(12))] = el.getAttribute(attr);
} else if (attr.startsWith('data-')) {
data[convertName(attr.slice(5))] = el.getAttribute(attr);
}
}

if (!sfc.props.locale) {
sfc.props.locale = {
type: Object,
default: () => {},
};
}

const view = createApp(sfc, data);
view.mount(el);
}
7 changes: 7 additions & 0 deletions web_src/js/init.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {convertName} from './init.js';

test('init', () => {
expect(convertName('abc')).toEqual('abc');
expect(convertName('abc-repo')).toEqual('abcRepo');
expect(convertName('abc-repo-issue')).toEqual('abcRepoIssue');
});
4 changes: 4 additions & 0 deletions web_src/js/svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ import octiconTag from '../../public/assets/img/svg/octicon-tag.svg';
import octiconTriangleDown from '../../public/assets/img/svg/octicon-triangle-down.svg';
import octiconX from '../../public/assets/img/svg/octicon-x.svg';
import octiconXCircleFill from '../../public/assets/img/svg/octicon-x-circle-fill.svg';
import octiconMute from '../../public/assets/img/svg/octicon-mute.svg';
import octiconUnmute from '../../public/assets/img/svg/octicon-unmute.svg';

const svgs = {
'gitea-double-chevron-left': giteaDoubleChevronLeft,
Expand Down Expand Up @@ -140,6 +142,8 @@ const svgs = {
'octicon-triangle-down': octiconTriangleDown,
'octicon-x': octiconX,
'octicon-x-circle-fill': octiconXCircleFill,
'octicon-mute': octiconMute,
'octicon-unmute': octiconUnmute,
};

// TODO: use a more general approach to access SVG icons.
Expand Down
Loading